Monday, 23 June 2014

Stochastic Mortality (Limited)

Introduction

I have now added some stochastic mortality functionality to Mortality Manager. However, I have limited this to just generating projected rates using R and simply adding the ability to use these rates.

This post therefore covers how I created the rates using R (but through F#) and how these rates can be viewed and plotted in Mortality Manager.

Generating the Rates

To generate the rates I used the following:
To create the rates you first need to ensure that the demography package is downloaded to make it available to R. You then need to download the mx and population rates, which are needed as inputs to the Lee-Carter code in the R package.

I chose to use UK rates. These contain rates for the general UK population from 1922 to 2011. I downloaded the two required files - Mx_1x1.txt and Exposures_1x1.txt. Here is a sample from each:

Mx_1x1.txt


United Kingdom, Death rates (period 1x1)     Last modified: 06-May-2013, MPv5 (May07)

   Year      Age       Female       Male         Total
   1922        0     0.070053     0.092574     0.081500
   1922        1     0.024976     0.027255     0.026130
   1922        2     0.012143     0.012979     0.012566
   1922        3     0.006072     0.006324     0.006199
   1922        4     0.004426     0.004305     0.004365
   1922        5     0.003861     0.003781     0.003821
   1922        6     0.002958     0.003157     0.003058

...

Exposures_1x1.txt

United Kingdom, Exposure to risk (period 1x1)     Last modified: 06-May-2013, MPv5 (May07)

   Year      Age       Female          Male         Total
   1922        0       446849.54    461921.95    908771.49
   1922        1       452480.82    463987.76    916468.59
   1922        2       427577.73    439102.38    866680.10
   1922        3       359372.64    367637.22    727009.86
   1922        4       326025.47    331448.37    657473.84
   1922        5       349516.32    355195.04    704711.36
   1922        6       379820.11    385139.04    764959.16
   1922        7       404411.29    407120.72    811532.01
   1922        8       418049.48    419024.29    837073.76
   1922        9       418158.45    420482.13    838640.58 

...

I then created an F# Console Application to generate the projected rates:

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
25: 
26: 
27: 
28: 
29: 
30: 
31: 
open System
open System.IO
open RProvider
open RDotNet
open RProvider.demography
open MortMgr

[<EntryPoint>]
let main argv = 
    let uk = R.read_demogdata("c:/Mx_1x1.txt","c:/Exposures_1x1.txt", "mortality", label="UK")
    let maxage = 110
    let h,nsim,ser,snm = 100,1000,"Male","UK Males"
    let LCm=R.lca(uk,series=box ser,max_age=box maxage,interpolate=box true)
    let fcastLCm=R.forecast_lca(LCm,h=h)
    let yrs = fcastLCm.AsList().["year"].AsInteger()|>Seq.toArray
    let sim=R.simulate_fmforecast(namedParams["object",box fcastLCm;"nsim",box nsim])
    let simv = sim.AsNumeric()|>Seq.toArray
    let qxts =
        let ans = Array.zeroCreate nsim
        for s = 0 to nsim-1 do
            let qxt = Array2D.create 121 h 1.0
            for a = 0 to 120 do
                for t = 0 to h-1 do
                    let mxt = simv.[s*h*(maxage+1)+t*(maxage+1)+(min a maxage)]
                    qxt.[a,t] <- mxt/(1.0+0.5*mxt)
            ans.[s]<-qxt
        ans
    let stbl = {StochName=snm;StartYear=yrs.[0];EndYear=yrs.[yrs.Length-1];Nosims=qxts.Length}
    IndStoch.addtbl stbl
    qxts|>Array.mapi (fun i q -> IndStoch.addsim stbl (i+1) q)|>ignore
    0 // return an integer exit code

Lines 1 to 6 set up the required references. Line 3 and 4 reference the R Provider. Line 5 references the specific R package. Line 6 references Mortality Manager, which is used to store the generated simulations.

Line 10 loads the files using R. Lines 11 and 12 set up some required parameters. In this case, we are going to generate 1,000 simulations for Males, projecting for 100 years.

Line 13 uses R to fit the input data to the Lee-Carter model. Line 14 then generates the forecast function. Line 15 gets the years to be forecast and converts this to an array. Line 16 then generates 1,000 sample forecast simulations. Line 17 then converts these simulations into an array.

Lines 18 to 27 then converts these simulations of mx to a 2D array of qx values.

Lines 28 to 30 store the results. Lines 28 and 29 just stores the general summary of the data as a Record Type. Line 30 separately stores each simulation. These results are stored as Json using Json.NET.

 Viewing the Simulations

I have just added a single Task Pane to allow you to view these simulations.


This provides the ability to view an individual simulation using the View Simulation button. In this case, simulation 1 is displayed. This shows the projected values of qx for each age and for each projected year. Note that the projection starts at 2012, one year after the last year of the data from the Human Mortality Database.

Simulation from a Specific Age/Year

The other facility provided is to view projected rates for a specified age and year.

This allows you to get simulations for a particular age from a particular year using the View Simulations button. This shows how the model might simulate rates of mortality for a particular individual. In this case, we are simulating from year 2020 for a male then aged 30.

You also get a plot of these simulations:


Mortality Manager v004

I have also packaged up Mortality Manager into a new distribution. These files are stored with the source code on bitbucket. The application is available in macro enabled workbooks for 32 bit and 64 bit versions of Excel:
The application is also available as an XLL, again for 32 bit or 64 bit Excel:
If you use these, you may also want to download an example workbook which uses the application:

Saturday, 7 June 2014

More Plotting

Introduction

As I mentioned in my last post, I have decided to extend the code I have for Excel interop through a new project on bitbucket. This is principally to add further charting capabilities and allow it to be used generally, in particular, when using F# Interactive.

I recently came across an interesting book on forecasting called "Forecasting: principles and practice" by Rob J Hyndman and George Athana­sopou­los. This is available in hard copy, but also published online at:
This contains a number of interesting plots using R and is supported by a CRAN package containing code and data for the examples in the book. In this post, I will replicate the plots shown in the chapter "the forecasters toolbox" - 2.1 Graphics.

Setting up the data

I first needed to make the data available to F#. I loaded up RStudio, loaded the fpp package and then tested I could generate the first example chart. I then saved the backing data in CSV format. This is the simple R code:

##do plot
plot(melsyd[,"Economy.Class"], main="Economy class passengers: Melbourne-Sydney",xlab="Year",ylab="Thousands")

##write csv for F#
write.zoo(as.zoo(melsyd),file="c:/vs/vs13/FSharp.XlInt/fpp/ch2/melsyd.txt",sep=",")


This generated a text file with these contents:

"Index","First.Class","Business.Class","Economy.Class"
1987.48076923077,1.912,NA,20.167
1987.5,1.848,NA,20.161
1987.51923076923,1.856,NA,19.993
1987.53846153846,2.142,NA,20.986
1987.55769230769,2.118,NA,20.497
....


The following chart was displayed:

In F# interactive I then used Deedle to load this data for use by F# and then called my library to display the chart in Excel.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
#load @"..\..\packages\Deedle.0.9.12\Deedle.fsx"
#r @"..\..\FSharp.XlIntLib\bin\debug\Office.dll"
#r @"..\..\FSharp.XlIntLib\bin\debug\Microsoft.Office.Interop.Excel.dll"
#r @"..\..\FSharp.XlIntLib\bin\debug\FSharp.XlIntLib.dll"

open System
open Deedle
open FSharp.XlInt

// call unless Excel is open and OK to use open version
Xl.start()

//do melsyd
let melsyd0 = Frame.ReadCsv(__SOURCE_DIRECTORY__ + "/melsyd.txt")
let melsyd1 : Frame<float, string> = melsyd0 |> Frame.indexRows "Index"
let plt1 = 
    Plt.line (melsyd1?``Economy.Class``
              |> Series.observations, Title = "Economy Melbourne-Sydney", XTitle = "Year", YTitle = "Thousands", XSpacing = 48, XNumberFormat = "0")

In lines 1 to 8, I set up the references needed to Deedle, Excel Interop and my library. In line 11, I call a utility function in my library that opens Excel and makes this instance the one to work with. In line 14, I use Deedle to load the CSV file into a dataframe. In line 15, I create an amended version of the dataframe that uses the Index field as the index of the dataframe. Finally, in lines 16 to 18, I call my library to plot the Economy.Class series. This is the result:


Further Plots

Similarly, I loaded other data from R into CSV files and then loaded them for use by F#. This is the remainder of the F# code used to generate further plots:

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
25: 
26: 
27: 
28: 
29: 
30: 
31: 
32: 
33: 
34: 
//do a10
let a10 = Frame.ReadCsv(__SOURCE_DIRECTORY__ + "/a10.txt")
let a101 : Frame<float, string> = a10 |> Frame.indexRows "Index"
let sales = a101?x
            |> Series.observations
let plt2 = Plt.line (sales, Title = "Antidiabetic drug sales", XTitle = "Year", YTitle = "$ million", XSpacing = 60, XNumberFormat = "0")

//do seasonplot
Plt.seasonplot (sales, Title = "Seasonal drug sales", YTitle = "$ million")
//do monthplot
Plt.monthplot (sales, Title = "Seasonal deviation drug sales", YTitle = "$ million")

//do fuel
let fuel = Frame.ReadCsv(__SOURCE_DIRECTORY__ + "/fuel.txt")
let city = fuel?City
           |> Series.values
let carbon = fuel?Carbon
             |> Series.values
let xy = Seq.zip city carbon

//jitter
Plt.scatterplot (Plt.jitter (xy), Title = "Jitter", XTitle = "City mpg", YTitle = "Carbon footprint") |> ignore

//scatterplot matrix
let litres = fuel?Litres
             |> Series.values
let highway = fuel?Highway
              |> Series.values
let data = [| litres; city; highway; carbon |]
let labels = [| "Litres"; "City"; "Highway"; "Carbon" |]

Plt.scatterplotmatrix (data, labels, Title = "ScatterPlot Matrix") |> ignore
//call to tidy up excel
Xl.close()

In lines 1 to 5, the data for sales is loaded. In line 6, a simple line plot of this date is created:

In line 9, the data is loaded into a plot showing the seasonal changes each month for each year:


In line 11, there is a variation on this plot which shows for each month how the sales change over the years:

In lines 14 to 19, some car fuel data is loaded, which is then plotted as a standard scatter plot in line 22. The more interesting element of this plot is that the data is "jittered" before being plotted to help illustrate where certain points have overlapping data items.

In lines 25 to 30, four of the series in the fuel dataframe are set up to be plotted against each other in the commonly used scatterplot matrix. In line 32, this data was plotted:


Finally, in line 34, a utility function was called to close this instance of Excel.


Sunday, 18 May 2014

Charting and Stochastic Mortality (Delayed)

Stochastic Mortality - Challenges

I am planning to add some Stochastic Mortality functionality to Mortality Manager, but this is proving more of a challenge than expected.

The material publicly available seems quite limited. For example, if you go to the Wikipedia page for the Lee Carter model at http://en.wikipedia.org/wiki/Lee_Carter_Model you will find this statement: "There are surprisingly few software packages for forecasting with the Lee-Carter Model.".

I will be continuing to research this, but if anyone has any good references, please let me know.

In the interim, I have decided to add some charting to the application, which is a necessary capability, as and when, I add some stochastic functionality. 

F# Charting Options 

The very useful website F# for Fun and Profit gives a list of options on this page:
This includes the most commonly used option with F# interactive - FSharp.Charting, which I did use in an earlier post.

For Mortality Manager, I need a facility that works with Excel. Two links were provided:
  1. MSDN Excel Charting Tutorial
  2. Blog post on plotting functions in Excel
The former contains extracts from a very good book - Real-World Functional Programming. However, this is quite a specific use rather than a generic facility.

The latter example is more generic and includes some impressive examples, but it is limited to charting functions rather than sets of data.

I therefore decided to create a generic facility, which I could use for mortality manager, but may be more widely useful.

FSharp.XlInt

To progress this, I decided to expand the code that I had to "interop" with Excel. I also wanted to use this more widely. I therefore moved the code into a separate solution called "FSharp.XlInt"and created a new repository on bitbucket at:
 In progressing this development I had some issues with NetOffice (principally when debugging). I therefore moved over to using the standard Microsoft Interop Assemblies.

The code was fairly straightforward to create. For example, here is code to create a separate chart sheet with a simple line graph:
 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
24: 
25: 
26: 
27: 
28: 
29: 
30: 
31: 
32: 
/// create plot
static member create (xy : seq<'a * 'b>, ?main : string, ?xlab : string, ?ylab : string, ?legend : string) = 
    let chart = 
        if main.IsSome then Cht.add (main.Value)
        else Cht.add()
        
    let plt = 
        { xys = [ xy |> Seq.map (fun (a, b) -> (box a, box b)) ]
            main = main
            xlab = xlab
            ylab = ylab
            chart = Some(chart) }
        
    chart.ChartType <- XlChartType.xlLine
    chart.HasLegend <- false
    if main.IsSome then 
        chart.Name <- main.Value
        chart.HasTitle <- true
        chart.ChartTitle.Text <- main.Value
    if xlab.IsSome then 
        let x = chart.Axes(1) :?> Axis
        x.HasTitle <- true
        x.AxisTitle.Text <- xlab.Value
    if ylab.IsSome then 
        let y = chart.Axes(2) :?> Axis
        y.HasTitle <- true
        y.AxisTitle.Text <- ylab.Value
    if legend.IsSome then 
        chart.HasLegend <- true
        Plt.draw (0, plt, legend.Value)
    else Plt.draw (0, plt)
    plt

The code uses a method rather than a function as this allows us to use optional parameters.The code simply creates a Chart object (lines 3 to 6), it stores properties in a Record type (lines 7 to 12), adds properties as specified to the Chart object (lines 14 to 29) and then calls the draw method:

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
/// draw xy on plot
static member draw (i, plt, ?legend) = 
    match plt.chart with
    | None -> ignore()
    | Some(chart) -> 
        let seriesCollection = chart.SeriesCollection() :?> SeriesCollection
        let series = seriesCollection.NewSeries()
        if legend.IsSome then series.Name <- legend.Value
        series.XValues <- plt.xys.[i]
                            |> Seq.map (fun (x, _) -> x)
                            |> Seq.toArray
        series.Values <- plt.xys.[i]
                            |> Seq.map (fun (_, y) -> y)
                            |> Seq.toArray

This method is passed in an integer, which is the index into the "xy" values, the chart as an option type and the legend if required. It then simply tests whether the option type has a value. If it has, then in lines 6 to 8 it gets the charts series collection then adds in a new series and sets the name to the provided legend. In lines 9 to 11, the xys field is used to set the values on the x axis. This files is a sequence of tuples holding the values for x and y. In lines 12 to 14 the values on the y axis are similarly set.

F# Interactive

This library is written so that it can easily be used from F# interactive. Below is the simplest of scripts:
 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
#r @"..\FSharp.XlIntLib\bin\debug\Microsoft.Office.Interop.Excel.dll"
#r @"..\FSharp.XlIntLib\bin\debug\FSharp.XlIntLib.dll"

open FSharp.XlInt

//simple series function
let xy = 
    [| (0.0, 0.0)
       (1.0, 1.0)
       (2.0, 2.0) |]

let plt = Plt.create (xy, main = "lines", xlab = "x values", ylab = "y values", legend = "First")

let xy1 = 
    [| (0.0, 0.0)
       (1.0, 2.0)
       (2.0, 3.0) |]

Plt.addline(xy1,plt,"Second")

We first set up the required references (lines1 to 4). We then define a simple set of xy data and create a chart using this data (lines 7 to 12). We can then add a second set of data to the chart (lines 14 to 19). This is displayed in Excel:

Chart in Mortality Manager

I have used this facility to add a chart as part of the compare facility for mortality tables. I added this code in Cmp.fs:
 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
// do chart
let rtst1 = Mort.getrtst seltbl1
let rtst2 = Mort.getrtst seltbl2
let p = Plt.create (rtst1.[rtst1.Length - 1].[20..85], "Comparison Chart", "Age", "Rate", seltbl1.Name)
Plt.addline (rtst2.[rtst2.Length - 1].[20..85], p, seltbl2.Name)
if rtst1.Length > 1 then 
    for i = 0 to rtst1.Length - 2 do
        Plt.addline (rtst1.[i].[20..85], p, 
                        if i = 0 then seltbl1.Name + "_Sel"
                        else seltbl1.Name + "_Sel+" + i.ToString())
if rtst2.Length > 1 then 
    for i = 0 to rtst2.Length - 2 do
        Plt.addline (rtst2.[i].[20..85], p, 
                        if i = 0 then seltbl2.Name + "_Sel"
                        else seltbl2.Name + "_Sel+" + i.ToString())

The mortality rates are converted to an array of tuple arrays (lines 2 to 3). There are multiple tuple arrays if the rates include select rates. A chart is then created for the ultimate rates for the first set of mortality rates (line 4). The ultimate rates for the second set of mortality rates are then added (line 5). If the first set has select rates then these are also added (lines 6 to 10). Similarly, select rates are added for the second set (lines 11 to 15).

As an example, this is the chart for AMC00 compared to AFC00:



Saturday, 26 April 2014

Mortality Manager v003

Overview

I have now completed the development of support for Mortality Projections. I have decided to produce an updated version which includes this additional functionality in a similar way to that made available in the post Mortality Manager with FCell 2.1.
This post describes what is provided and instructions for use and also covers the changes made to the UDFs.

Workbook Customisation

One option for obtaining Mortality Manager v003 is as a standalone workbook. I have created two versions for 32 bit and 64 bit Excel. These are available from bitbucket at:
 Once you download the appropriate version, you can simply open in Excel:



Simply follow the instructions in the GettingStarted sheet to be able to replicate the examples contained in the workbook.

Excel Customisation

The other option for obtaining Mortality Manager v003 is as an .XLL file. This provides the Ribbon, Custom Task Pane and UDF additions included in Mortality Manager using a single file.

I have created two versions of the XLL for 32 bit and 64 bit Excel. These are also available from bitbucket at:
Once you have downloaded these, they can simply be opened using Excel or to install permanently use File > Options > Add-ins and then use the Go.. button to "Manage the Excel Add-ins" and then Browse to add the downloaded XLL.

(N.B. If you get a message that the XLL is in an invalid format then either you are trying to use the wrong version or your computer is missing the Visual C++ runtime for Visual Studio 2012, which is required by these XLLs. For the latter issue please use the installer available from Microsoft at http://www.microsoft.com/en-us/download/details.aspx?id=30679.)

Once loaded, you should see the Mort Ribbon and additions in the function wizard, e.g. a new category MortMgrUdf.Mort.

To get started, I suggest using the following steps:
  1. On the Mort Ribbon select "Settings". Specify local folders to store local versions of tables and to store Transitory data when importing from the SoA site, e.g. "C:\Mort\Local" and "C:\Mort\SoA". N.B. Create these folders first and then browse to select them using the "..." button.
  2. Restart Excel to get these settings reloaded. Re-open the Settings dialog and confirm that the folders are still correctly set.
  3. Using the SoA toggle button open the SoA Task Pane. Use the "Reload Index from Website" button - this may take some time! Confirm that the file "soaindx.json" has been create in the SoA folder, e.g. in C:\Mort\SoA
  4. Import some sample Select and Ultimate tables. For example, in the SoA Task Pane select a Nation of "United Kingdom". Select the checkboxes for the tables "ELT No. 16 (2000-02) - Female, ANB", "ELT No. 16 (2000-02) - Male, ANB", "AFC00" and "AMC00". Import using the "Import" button. Confirm that new json files appear in the local tables folder, e.g.  in C:\Mort\Local.
  5. In the CMI 2012 Task Pane, select Get Sample Results and then Save Rates to create a default set of Projected Rates.
You should then be able to use the Task Panes included to view the tables, compare tables and calculate various factors. You should also be able to get results from the tables using the UDFs provided.

UDFs with complex table parameters and results

I have converted all the supplied UDFs to use a facility included in FCell 2.1 to use complex parameters and return complex results. The UDFs that return mortality results now all work with the mortality table as one of the parameters. It is assumed that another UDF has been used to pull in the mortality table reference to an Excel cell.

This is best illustrated by an example provided with the customised workbook:



Please note the get UDF references in cell F3, with this formula:
=get("ELT No. 16 (2000-02) – Female, ANB")
This simple gets this mortality table and holds a reference to it in the cell. You can then use this in another UDF, as illustrated by the function wizard for cell F7:


The code for this UDF is simply:

1: 
2: 
3: 
4: 
5: 
6: 
7: 
/// qx for ultimate table
[<XlFunctionHelp("Get q from Ultimate table")>]
let qx tbl x = 
    let rts = tbl.MortRates
    match rts with
    | Ultimate(r) -> r.[x]
    | _ -> failwith "Table is not an Ultimate table"

Note the use of the XlFunctionHelp attribute to add documentation in the function wizard in Excel.

Monday, 21 April 2014

Mortality Projection Functions

Overview

I have now completed the Ribbon and Custom Task Panes relating to Mortality Projections within Mortality Manager. This includes functionality to:
  • Generate Mortality Projection using the CMI 2012 model 
  • Save these Generated projected rates
  • View and Edit Saved Projected Rates (This also allows you to create these by hand or sourced from other data)
  • Compare two sets of Projected Rates
  • Generate Single Life and Joint Life Mortality Functions (qx, dx, lx, ex) from a set of Projected Rates (depending on a specified age and calendar year)
  • Generate Single Life and Joint Life Commutation Functions (Dx, Nx, Sx, Cx, Mx, Rx) from a set of Projected Rates (depending on a specified age and calendar year)
  • Generate Single Life and Joint Life Present Value Functions (ax, Ax, Px) from a set of Projected Rates (depending on a specified age and calendar year)
The updated code is available on bitbucket. Mortality Manager now looks like this:

Persisting using Records and Json

In a very similar way to the Mortality tables, I have used a combination of F# Record Types and Json.NET to support saving these projected rates. The Record used is defined as:
1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
9: 
type Qxt = 
    {   BaseName : string
        BaseRates : float []
        BaseDate : System.DateTime
        ProjName : string
        StartYear : int
        EndYear : int
        QxtName : string
        QxtRates : float [,] }

This includes details of the Base Mortality rates used (their name, the rates  and the assumed date these rates apply), then the name of the Projection used and then details of the projected rates (start and end year, the name used to identify these rates and the rates as a 2D array).

These can be simple saved to Json using this code:
1: 
2: 
3: 
4: 
/// save Qxt to json
let save (ptbl : Qxt) jsonfil = 
    let jsontxt = JsonConvert.SerializeObject(ptbl)
    File.WriteAllText(jsonfil, jsontxt)

The saved sets of rates can be viewed using the View/Create option for Projections in Mortality Manager:



Re-using the Mortality Code 

To generate the functionality to calculate Mortality Functions, Commutation Functions and Present
Value functions, the simplest approach was to re-use the code already written for Mortality Tables. To do this, it was simple to generate an intermediate Mortality Table Record Type and then pass this to the existing functions.

To create this intermediate type, this code was used:

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
// get mort table given age and year and proj table
let getmtbl iage iyr qxts = 
    //get qs from this age and year forwards
    let pj_qxs = pj_qx iage iyr qxts
    //zero fill for prior ages
    let rts = [|0..120|]|> Array.map (fun a -> if a <iage then 0.0 else pj_qxs.[a-iage])
    //return mortality table record
    { Name = (qxts.QxtName + " at age " + iage.ToString() + " and year_" + iyr.ToString())
      MortInfo = dummy_mi
      MortRates = Ultimate(rts) }


Please note the need to supply a specific age and year to allow a single array of rates to be extracted from the Projected Rates. Also, note that the rates have been zero filled for ages below the specified age. The function just returns a dummy Mortality Table Record with a dummy name and info, just holding the projected values as a set of Ultimate rates.  The results using this code therefore apply to someone who is the specified age in the specified year and allows for mortality improvements from that date onwards. 

The next blog will complete the functionality for Mortality Projections by adding related User Defined Functions and the existing UDFs will be revisited allowing for the extra options available under FCell 2.1.