TableSource#

TableSource loads an .ltk actuarial table file from disk and exposes the raw decrement arrays and metadata for use by LifeTable, DisabilityTable, and ExitTable constructors (each constructor delegates to TableSource internally).

Table files are located in the directory configured by tables_path (default: absolute path to actuarial_tables/ in the working directory at the time of the first Config() call). A disk-read cache keyed on file path and modification time avoids redundant I/O for repeated access to the same table within a session.

Note

TableSource is primarily an internal component. Users normally access table data through LifeTable and its siblings rather than constructing a TableSource directly.

Bundled catalogue tables are shipped in-repo as Python payloads; install them to tables_path as .ltk files with Tables.install() (import Tables from lactuca.tables.data) before first use — see Bundled Actuarial Tables.

See also

Using Actuarial Tables — Loading and inspecting tables.
Bundled Actuarial Tables — Tables bundled with Lactuca.

class lactuca.TableSource(file_name: str | PathLike[str], path: str | PathLike[str] | None = None)#

Bases: object

Loads an actuarial table (.ltk) and exposes its data and metadata as properties.

All metadata fields (except "decrement_scale_factor" and "mi_scale_factor") are exposed as read-only properties. All attributes are set at initialisation time. If any required metadata field is missing, a ValueError is raised.

See also

DecrementTable

Higher-level actuarial table API built on top of TableSource.

Examples

>>> from lactuca import TableSource
>>> ts = TableSource("PER2020_Ind_1o.ltk")
>>> ts.table_name
'PER2020_Ind_1o'
>>> ts.omega
110
head(n: int = 10, include_age: bool = True, show_normalized: bool = False) DataFrame#

Return the first n rows of the table data, optionally excluding normalized padding.

By default, shows first n rows from meaningful data (starting at start_age). Use show_normalized=True to include padding rows from age 0.

Parameters:
  • n (int, default 10) – Number of rows to return from the start.

  • include_age (bool, default True) – If True, include 'age' column. If False, exclude 'age' column from output.

  • show_normalized (bool, default False) – If True, start from age 0 (show padding if present). If False, start from start_age (exclude padding).

Returns:

The first n rows with or without age column.

Return type:

polars.DataFrame

See also

TableSource.tail

View last n rows.

TableSource.view_data

View full table data.

Examples

>>> from lactuca import TableSource
>>> ts = TableSource("GAM71.ltk")
>>> print(ts.head(3))  # Shows ages 5, 6, 7
>>> print(ts.head(3, show_normalized=True))  # Shows ages 0, 1, 2 (padding)
tail(n: int = 10, include_age: bool = True, show_normalized: bool = False) DataFrame#

Return the last n rows of the table data, always ending at omega.

Use the show_normalized parameter to control which range is considered.

Parameters:
  • n (int, default 10) – Number of rows to return from the end.

  • include_age (bool, default True) – If True, include 'age' column. If False, exclude 'age' column from output.

  • show_normalized (bool, default False) – If True, consider full normalized range (0 to omega). If False, consider only meaningful range (start_age to omega).

Returns:

The last n rows with or without age column.

Return type:

polars.DataFrame

See also

TableSource.head

View first n rows.

TableSource.view_data

View full table data.

Examples

>>> from lactuca import TableSource
>>> ts = TableSource("GAM71.ltk")
>>> print(ts.tail(3))  # Shows ages 108, 109, 110
to_dict() dict[str, Any]#

Return a dictionary containing the main attributes and metadata of the table.

Returns:

Dictionary with the following top-level keys:

  • 'file_name' (str): base name of the .ltk file.

  • 'file_path' (str): absolute POSIX path of the .ltk file.

  • 'w' (int): current terminal age (effective \(\omega\) after any modifications).

  • 'columns' (list[str]): DataFrame column names.

  • 'start_duration' (int or None): minimum valid integer duration for select-ultimate tables (0 for CMI/UK convention, 1 for the standard convention); None for non-select tables. Auto-detected from the minimum _s{k} column suffix at load time — not stored in the .ltk file.

  • 'metadata' (dict): raw file metadata with parsed Python-typed values: 'valid_sexes' as list, 'sex_independent' / 'generational' as bool, scale factors as int or float.

  • 'data' (polars.DataFrame): full table data.

Return type:

dict

Examples

>>> from lactuca import TableSource
>>> ts = TableSource("PER2020_Ind_1o.ltk")
>>> d = ts.to_dict()
>>> d["file_name"]
'PER2020_Ind_1o.ltk'
>>> list(d.keys())
['file_name', 'file_path', 'w', 'columns', 'start_duration', 'metadata', 'data']

See also

TableSource.to_payload

Minimal serializable payload for table reconstruction.

to_payload() dict[str, object]#

Return a minimal serializable payload sufficient to recreate the table via TableBuilder(payload.pop("data"), **payload).

Data arrays start at start_age (no zero-padding rows).

Scale factors reflect the applied values (e.g. 1000 if data was scaled by 1000 before storage), so that a round-trip TableBuilder receives the correct scale and reconstructs identical data.

Returns:

Serializable dict with keys 'table_name', 'table_type', 'generational', 'omega', 'start_age', 'sex_independent', 'description', 'decrement_scale_factor', and 'data' (a dict of column lists starting at start_age). Optionally includes 'select', 'select_period', 'base_year', 'generational_formula_type', 'grid_years', and 'mi_scale_factor' when applicable.

Return type:

dict

Examples

>>> from lactuca import TableSource
>>> ts = TableSource("PER2020_Ind_1o.ltk")
>>> payload = ts.to_payload()
>>> payload["table_name"]
'PER2020_Ind_1o'
>>> "data" in payload
True

See also

TableSource.to_dict

Full dictionary including the Polars DataFrame.

view_data(include_age: bool = True, show_normalized: bool = False) DataFrame#

Return the full table data, optionally excluding normalized padding rows.

By default, shows only meaningful data (excludes ages below start_age). Use show_normalized=True to see all rows including internal padding.

Parameters:
  • include_age (bool, default True) – If True, include 'age' column (already present in data). If False, exclude 'age' column from output.

  • show_normalized (bool, default False) – If True, show full normalized range (0 to omega). If False, show only meaningful range (start_age to omega).

Returns:

Table data with or without age column.

Return type:

polars.DataFrame

See also

TableSource.head

View first n rows.

TableSource.tail

View last n rows.

TableSource.start_age

Starting age from original table definition.

Examples

View GAM71 data (start_age=5):

>>> from lactuca import TableSource
>>> ts = TableSource("GAM71.ltk")
>>> df = ts.view_data()  # Shows ages 5-110 (106 rows)
>>> df_full = ts.view_data(show_normalized=True)  # Shows ages 0-110 (111 rows)
property base_year: int | None#

Base year for generational improvement calculations.

The base year is the reference period at which the decrement rates stored in the table apply without any improvement adjustment. Cohort calculations project rates forward or backward from this year using the improvement factors in the mi_ columns.

Returns:

Base year as defined in the table metadata, or None for static period tables.

Return type:

int or None

See also

TableSource.generational

Whether the table applies cohort improvement.

TableSource.generational_formula_type

Formula type for improvement calculations.

TableSource.grid_years

Projection grid years (for 'projected_improvement' tables).

property data: DataFrame#

Table data as a Polars DataFrame, covering all ages from 0 to omega.

The 'age' column (integers \(0\) to \(\omega\)) is generated at load time and is always the first column; it is not stored in the .ltk file. Rows below start_age are zero-padded. All remaining columns depend on the table type and structure:

  • Aggregate tables: qx_m, qx_f (life); ix_m, ix_f (disability); ox_m, ox_f (exit).

  • Select-ultimate tables: qx_m_s1, …, qx_m_s{n}, qx_m_ult, and the corresponding _f variants.

  • Generational tables add mi_ improvement columns: mi_m, mi_f (flat structure); mi_m_YYYY, mi_f_YYYY (year-indexed / projected); mi_m_s1, mi_m_ult, … (select-period structure).

Returns:

Full table data including the 'age' column and all decrement/improvement columns.

Return type:

polars.DataFrame

See also

TableSource.metadata

Raw metadata dictionary.

TableSource.view_data

Filtered view excluding zero-padding rows below start_age.

property description: str#

Description of the table from metadata.

Returns:

Human-readable description string, or an empty string if not provided in the .ltk file.

Return type:

str

See also

TableSource.table_name

Name of the table as defined in the file metadata.

TableSource.metadata

Raw metadata dictionary including the description field.

property file_name: str#

Basename of the loaded table file (e.g. 'PER2020.ltk').

Returns:

Base filename including the .ltk extension.

Return type:

str

See also

TableSource.file_path

Full resolved path to the loaded table file.

property file_path: Path#

Full resolved path to the loaded table file.

Returns:

Absolute path to the .ltk file as a pathlib.Path object.

Return type:

pathlib.Path

See also

TableSource.file_name

Basename of the loaded table file.

property generational: bool#

Whether the table applies generational (cohort) improvement factors.

Determined by the generational field in the table metadata.

Returns:

True for generational (cohort) tables; False for static period tables.

Return type:

bool

See also

TableSource.generational_formula_type

Formula type for cohort-improvement calculations.

property generational_formula_type: Literal['exponential_improvement', 'linear_improvement', 'discrete_improvement', 'projected_improvement'] | None#

Formula type used for generational (cohort) improvement calculations.

Returns:

One of 'exponential_improvement', 'linear_improvement', 'discrete_improvement', or 'projected_improvement'; or None for non-generational tables.

Return type:

GenerationalFormulaTypeLiteral or None

See also

TableSource.generational

Whether the table applies generational improvement factors.

property grid_years: list[int] | None#

Sorted list of projection grid years for projected_improvement tables.

These are the years for which year-indexed mi columns (mi_{sex}_YYYY) are stored in the table. For other formula types, returns None.

Returns:

Sorted list of grid years (e.g. [2021, 2022, ..., 2036]), or None if the table is not a period-projection table.

Return type:

list[int] or None

See also

TableSource.generational_formula_type

Formula type for cohort improvement calculations.

TableSource.base_year

Reference year for improvement calculations.

property metadata: dict[str, Any]#

Raw metadata dictionary loaded from the .ltk file.

All values are normalized string metadata as loaded from the table file. For type-safe access to individual fields, prefer the corresponding typed properties (e.g. omega, table_type, generational, valid_sexes).

Returns:

Shallow copy of metadata key-value pairs as loaded from the table file.

Return type:

dict[str, Any]

See also

TableSource.omega

Parsed terminal age (integer).

TableSource.table_type

Parsed table type string.

TableSource.generational

Parsed generational flag (bool).

TableSource.to_dict

Returns parsed metadata together with the table data.

property mi_by_duration: bool#

Whether improvement factors vary by select duration.

True only when mi_structure is 'select_period', i.e. the table stores separate improvement columns per select duration (e.g. mi_m_s1, mi_m_ult).

Returns:

True if per-duration MI columns are present; False otherwise.

Return type:

bool

See also

TableSource.mi_structure

Layout tag for mortality-improvement columns.

property mi_structure: str | None#

Layout tag for mortality-improvement columns.

Identifies the column layout used to store improvement factors:

  • 'flat': a single improvement column per sex (mi_m, mi_f), shared across all ages and durations.

  • 'year_indexed': per-year columns per sex (mi_m_YYYY, mi_f_YYYY), one column per projection year in grid_years.

  • 'select_period': per-duration columns per sex (mi_m_s1, …, mi_m_ult), one column per select duration.

Returns:

One of 'flat', 'year_indexed', or 'select_period' for generational tables; None for non-generational tables.

Return type:

str or None

See also

TableSource.mi_by_duration

Whether improvement factors vary by select duration.

property omega: int#

Terminal age from table metadata (immutable).

\(\omega\) is the terminal age defined in the original table. For a table covering ages \(0\) to \(118\), \(\omega = 118\). All decrement arrays in the original table have length \(\omega + 1\).

This value never changes, even if the table is modified. Use the w property for the current effective terminal age (may change after modifications in DecrementTable).

Returns:

Terminal age \(\omega\) as defined in the table metadata.

Return type:

int

See also

TableSource.w

Current effective terminal age (always equal to omega in TableSource).

Examples

>>> from lactuca import TableSource
>>> ts = TableSource("PER2020_Ind_1o.ltk")
>>> ts.omega  # Terminal age defined in the .ltk metadata
110
>>> ts.omega == ts.w  # Always True in TableSource
True
property select: bool#

Whether this is a select-ultimate table.

Returns:

True if the table contains select-duration columns (e.g. qx_m_s1, qx_m_ult); False for aggregate tables.

Return type:

bool

See also

TableSource.select_period

Number of select-duration years.

property select_improvement_diagonal: str | None#

Calendar-year index used for mortality improvement on select columns.

For generational select-ultimate tables, improvement factors are looked up on a cohort diagonal. The index depends on generational_formula_type:

  • 'projected_improvement''cohort_plus_x_plus_d' (calendar year $t = text{cohort} + x + d$, implemented as effective_cohort = cohort + d).

  • All other generational formulas → 'cohort_plus_x' (calendar year $t = text{cohort} + x$; select duration $d$ affects only the base $q$ column, not the improvement diagonal).

Returns:

'cohort_plus_x_plus_d', 'cohort_plus_x', or None when the table is not generational or not select-ultimate.

Return type:

str or None

See also

TableSource.generational_formula_type

Dispatches the diagonal convention.

TableSource.select

Whether select-duration columns are present.

property select_period: int | None#

Select period in years (None for non-select tables).

Returns:

Number of select-duration years, or None for non-select tables.

Return type:

int or None

Notes

As defined in the table metadata; represents the number of select-duration years (e.g. s1, s2 for a 2-year select period), excluding the ultimate (ult) column. For a table with duration keys {1, 2, 'ult'}, select_period is 2.

See also

TableSource.select

Whether this is a select-ultimate table.

property sex_independent: bool#

Whether this table has identical rates for both sexes.

When True, the table uses the same mortality/disability/exit rates for males and females (e.g., IASS-90, SS90-TOT, SS90-ABS). At load time, a single-sex column is automatically mirrored to the other sex so that both _m and _f columns are always present.

Returns:

True if rates are sex-independent; False otherwise.

Return type:

bool

See also

TableSource.valid_sexes

Sex codes available in this table.

property start_age: int#

Starting age of the table from original source data.

Returns the age at which the table was originally defined to start. For example, GAM71 starts at age 5, GAM94 starts at age 1.

Although tables are internally normalized to age 0 (with padding for ages below the original start), this property returns the meaningful starting age from the design of the table.

Returns:

Starting age from original table definition (0 if table started at age 0).

Return type:

int

See also

TableSource.omega

Terminal age of the table.

TableSource.view_data

By default excludes padding rows below start_age.

Examples

>>> from lactuca import TableSource
>>> ts = TableSource("GAM71.ltk")
>>> ts.start_age  # Original table starts at age 5
5
>>> ts_gam83 = TableSource("GAM83.ltk")
>>> ts_gam83.start_age  # Original table starts at age 0
0
property start_duration: int | None#

First (minimum) integer duration in the select columns, or None for non-select tables.

Returns:

0 for CMI/UK tables (AM92, AF92 — Duration-0 convention), 1 for most other select tables (Duration-1 convention), or None if this is not a select table.

Return type:

int or None

Notes

Auto-detected at load time by scanning DataFrame columns for the minimum _s{k} suffix index.

See also

TableSource.select

Whether this is a select-ultimate table.

TableSource.select_period

Number of select-duration years.

property table_name: str#

Name of the table as defined in the file metadata.

Returns:

Table name string as stored in the table metadata (e.g. 'PER2020_Ind_1o').

Return type:

str

See also

TableSource.table_type

Type of the table (life, disability, or exit).

property table_type: Literal['life', 'disability', 'exit']#

Type of the table from metadata.

Returns:

One of 'life', 'disability', or 'exit'.

Return type:

TableTypeLiteral

See also

TableSource.table_name

Name of the table.

property valid_sexes: list[str]#

Sex codes for which this table provides decrement rates.

Code 'm' maps to qx_m / ix_m / ox_m columns; code 'f' maps to qx_f / ix_f / ox_f columns. For sex-independent tables both codes are always present, even if the source file contained data for only one sex. The unisex code 'u' is handled by DecrementTable and is not stored here.

Returns:

New list of sex codes (e.g. ['m', 'f']) on every call.

Return type:

list[str]

See also

TableSource.sex_independent

Whether rates are identical for both sexes.

property w: int#

Current effective terminal age (may differ from omega after modifications).

omega is the immutable terminal age read from the .ltk file and never changes. w starts equal to omega but is updated whenever the decrement array is replaced by calling modify_qx, modify_ix, or modify_ox on a DecrementTable instance. After such a call, w equals the last valid index of the new array, which may be larger or smaller than the original omega.

All actuarial outputs — life tables, commutation functions, present values, and annuities — use w as their upper age bound. Always prefer w over omega in internal calculations.

In TableSource itself w always equals omega because no modifications have been applied yet.

Returns:

Current effective terminal age.

Return type:

int

See also

TableSource.omega

Immutable terminal age from table metadata

DecrementTable.modify_qx

Replace the mortality array and update w.

DisabilityTable.modify_ix

Replace the disability array and update w.

ExitTable.modify_ox

Replace the exit array and update w.