DecrementTable#
DecrementTable is the abstract base class for all
single-decrement table types (LifeTable,
DisabilityTable, ExitTable). It provides
the shared logic for table construction, the survival function \(l_x\) with
radix \(l_0 = 1{,}000{,}000\), the governing decrement probability (\(q_x\), \(i_x\), or \(o_x\)
depending on the concrete subclass), and decrement modification
(modify_qx() on life tables;
modify_ix() and modify_ox()
on the other subclasses).
LifeTable extends it further with the full suite of
annuity, insurance, commutation, and life expectancy calculations.
Direct instantiation of DecrementTable is not supported — use one of
the concrete subclasses. Import the base class as
from lactuca.tables import DecrementTable (it is not re-exported from the
top-level lactuca package).
See also
Table Taxonomy — Overview of table types and decrement conventions.
Using Actuarial Tables — Loading, inspecting, and modifying tables.
Modifying Decrements — Scaling, aggravated risk, and table_combination.
LifeTable — LifeTable reference.
DisabilityTable — DisabilityTable reference.
ExitTable — ExitTable reference.
Table registry utilities — TableRegistry and configure_all().
- class lactuca.tables.DecrementTable(table_name: str | list | tuple, sex: Literal['m', 'f', 'u'] | Sequence[Literal['m', 'f', 'u']] | None = None, cohort: int | Sequence | None = None, unisex_blend: object = None, duration: int | str | Sequence | None = None, cartesian: bool = False, return_dict: bool = False, pending: bool = False, **_kw)#
Bases:
ABCAbstract base class for actuarial decrement tables (e.g., life, disability, exit).
This class provides robust, consistent, and memory-efficient logic for loading, validating, and handling actuarial tables. It is designed to be inherited by concrete subclasses such as LifeTable, DisabilityTable, or ExitTable.
Key features:
Strict validation of required columns and metadata.
Efficient memory usage via compact NumPy array storage.
Fast access to configuration and table metadata.
Cohort and sex validation for generational/static tables.
Modification and reset logic for decrement rates.
Abstract contract for
table_typeand shared base implementation ofsummary()that subclasses can extend.
Observable public state is accessible via properties, including
table_name,sex,cohort,omega,w,generational,select,modified,modifications_applied, andunisex_blend.Notes
Actuarial Notation (IAA/IAE Conventions)
Decrement rates follow standard international actuarial notation. The one-year decrement probability at integer age \(x\) is denoted:
\[{}_{1}q_x \quad \text{(life)}, \quad {}_{1}i_x \quad \text{(disability)}, \quad {}_{1}o_x \quad \text{(exit)}\]where:
\(q_x\) (life tables): probability that a life aged \(x\) dies before age \(x+1\)
\(i_x\) (disability tables): probability that a healthy life aged \(x\) becomes disabled before age \(x+1\)
\(o_x\) (exit tables): probability that an active life aged \(x\) exits (e.g., withdrawal, retirement) before age \(x+1\)
Survival Probabilities
The complementary survival probability is denoted \(p_x = 1 - q_x\), representing the probability of surviving (or remaining in the active state) from age \(x\) to \(x+1\).
Survival Function
The survival function \(l_x\) represents the expected number of survivors at exact age \(x\) from an initial cohort (\(l_0 = 1{,}000{,}000\) in Lactuca). For fractional ages, \(l_x\) is computed via interpolation between integer ages.
Interpolation for Fractional Ages
When calculations require fractional ages (e.g., \(x = 50.5\) or monthly payment frequencies), the survival function \(l_x\) is interpolated between integer ages using one of two methods controlled by the
Config.lx_interpolationsetting:'linear'(default): UDD (Uniform Distribution of Deaths) — assumes deaths are uniformly distributed between integer ages. This is arithmetic interpolation of \(l_x\).'exponential': CFM (Constant Force of Mortality) — assumes constant force of mortality between integer ages. This is geometric (log-linear) interpolation of \(l_x\).
Implementation Notes
Subclasses must implement the abstract
table_typeproperty, which determines the decrement column naming convention (\(q_x\) for life, \(i_x\) for disability, \(o_x\) for exit).The
summary()method is implemented in this base class and provides a common metadata/state report; subclasses can append type-specific details.For select-ultimate tables,
durationis required.For generational tables,
cohortis required.For unisex computations without a native unisex column,
unisex_blendis required whensex='u'.
See also
LifeTableConcrete decrement table for mortality rates.
DisabilityTableConcrete decrement table for disability rates.
ExitTableConcrete decrement table for exit rates.
TableSourceTable loader and metadata/data accessor.
Examples
>>> from lactuca import LifeTable >>> lt = LifeTable('PASEM2010', 'm') >>> lt.table_name 'PASEM2010' >>> lt.w 112 >>> lt.qx(50) 0.00249 >>> lt.modified False >>> lt.modify_qx({'decrement_multiplier': 1.1}) >>> lt.modified True
- batch_update() _BatchUpdateContext#
Context manager to batch multiple setter assignments into one rebuild.
Use
with table.batch_update():to suspend rebuilds during a sequence of property assignments. A single decrement rebuild is performed when the context manager exits successfully, regardless of how many setters were called inside the block. If no setter changed any value (all dirty-checks returned early), no rebuild is performed.This provides the same single-rebuild guarantee as
configure()but uses an imperativewithblock instead of a single method call, which may be preferable when assignments are spread across multiple statements or depend on intermediate results.- Returns:
A context manager bound to the table instance.
- Return type:
context manager
- Raises:
ValueError – If the table is still pending after all assignments (missing metadata).
RuntimeError – If
batch_updatecontexts are nested on the same instance.
Notes
If an exception is raised inside the
withblock, all setter changes made up to that point are rolled back and the table is restored to its state at block entry (metadata and decrement arrays).batch_updateis not reentrant: nesting twobatch_updateblocks on the same instance raisesRuntimeErrorimmediately.Prefer
configure()for declarative single-call configuration.batch_updateis suited to imperative workflows where individual assignments depend on intermediate calculations.See also
configure()Declarative atomic configuration with the same single-rebuild guarantee.
metadata_pending()Read-only flag indicating pending state.
Examples
>>> from lactuca import LifeTable >>> lt = LifeTable('PER2020_Ind_1o', pending=True) >>> with lt.batch_update(): ... lt.sex = 'm' ... lt.cohort = 1972 >>> lt.metadata_pending False
- configure(sex: Literal['m', 'f', 'u'] | None = None, cohort: int | None = None, duration: object = None, unisex_blend: object = None, **_extra) DecrementTable#
Configure table metadata atomically with a single rebuild.
Validates all arguments before touching
self; if any validation fails the table state is unchanged (transactional / rollback). Performs exactly one decrement rebuild regardless of how many parameters are supplied. Returnsselfso calls can be chained.Omitted keyword arguments preserve their current value (they are not reset). For example,
lt.configure(cohort=1972)on a table that already hassex='m'keepssex='m'.- Parameters:
sex ({'m', 'f', 'u'} or None, optional) – Sex for calculations.
Nonemeans “keep current value”.cohort (int or None, optional) – Birth cohort year for generational tables.
Nonemeans “keep current value”.duration (int, 'ult', or None, optional) – Select-table duration.
Nonemeans “keep current value”.unisex_blend (float or None, optional) – Male weight for unisex blending in
[0.0, 1.0].Nonemeans “keep current value”.
- Returns:
self, enabling method chaining.- Return type:
- Raises:
ValueError – If called with no arguments (at least one keyword is required). If a supplied value is invalid for the current table configuration.
TypeError – If a supplied value has an incorrect type.
Notes
configure()always performs a single rebuild when all required metadata is complete after applying the supplied arguments. There is no dirty-check: if you want to avoid redundant rebuilds, use the individual property setters (which do dirty-check).The internal order of application is:
sex→cohort→unisex_blend→duration→ single decrement rebuild. This ordering ensures the unisex column (sex='u') is generated aftercohortis set.configure()is also used to reconfigure an already-configured table (e.g., changecohortin a loop without reinstantiating the object). Kwargs not supplied keep their current value; for example,lt.configure(cohort=1972)on a table withsex='m'already set keepssex='m'.If the supplied kwargs leave required metadata still incomplete (e.g.,
configure(cohort=1969)on a table that still lackssex), the table remains in the pending state and no rebuild is performed.On select tables in the pending state,
sexandduration(including'ult') may be supplied in separateconfigure()calls in any order.For
sex='u'on tables without a native unisex column, supplyunisex_blendin the sameconfigure()call (or set it on a prior call); validation mirrors__init__().See also
batch_update()Context manager for imperative setter-based configuration.
metadata_pending()Read-only flag indicating pending state.
configure_allApply
configure(**kwargs)to a collection of tables at once.
Examples
>>> from lactuca import LifeTable >>> lt = LifeTable('PER2020_Ind_1o', pending=True) >>> lt.configure(sex='m', cohort=1969).ax(65, ir=0.03)
Reconfigure in a loop (single instance, minimal allocation):
>>> for c in [1960, 1970, 1980]: ... result = lt.configure(cohort=c).ax(65, ir=0.03)
- copy()#
Return a deep copy of the instance.
- Returns:
Independent copy of the table with all state and caches duplicated.
- Return type:
Notes
Uses
copy.deepcopy()and preserves shared configuration behavior while duplicating table state and computation caches.Examples
>>> from lactuca import LifeTable >>> lt = LifeTable("PASEM2010", "m") >>> lt2 = lt.copy() >>> lt.modify_qx({"decrement_multiplier": 1.1}) >>> lt2.qx(50) # original copy is unmodified 0.00249
- dx(x: object = None) object#
Return \(d_x\) values (number of decrements between ages \(x\) and \(x+1\)).
Computes the expected number of decrements occurring between integer ages x and x+1, using the formula: \(d_x = l_x - l_{x+1}\)
- Parameters:
x (float, int, sequence of float, NDArray[np.float64], or None, optional) – Age(s) to evaluate. Pandas and Polars Series are accepted; converted to float64 internally. If None, returns all dx values from age 0 to omega.
- Returns:
dx value(s), or all dx if x is None. Returns scalar if x is scalar.
- Return type:
Union[float, NDArray[np.float64]]
- Raises:
ValueError – If any
xis negative.TypeError – If
xis not numeric or a sequence of numerics.
Notes
For fractional ages, uses \(l_x\) interpolation as configured.
Results rounded to config.decimals.dx.
For x > \(\omega\), returns 0. At x = \(\omega\), returns \(l_{\omega}\), which equals 0 for standard life tables (where \(q_{\omega-1} = 1\)) but may be non-zero for disability or exit tables.
Examples
>>> from lactuca import LifeTable >>> lt = LifeTable('PASEM2010', 'm') >>> lt.dx(50) 243.78 >>> lt.dx([50, 51, 52]) array([243.78, 247.33, 250.12])
See also
DecrementTable.lxNumber of lives at age x.
DecrementTable.qxDecrement probability at age x.
- head(n: int = 10, include_age: bool = True, show_normalized: bool = False) DataFrame#
Return the first n rows of the computed instance data.
Shows only columns relevant to this instance (sex, duration, modifications). By default starts from start_age (excludes padding rows below the original table start).
- 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 (default), start from start_age (exclude padding).
- Returns:
The first n rows with or without age column.
- Return type:
polars.DataFrame
See also
DecrementTable.tailView last n rows.
DecrementTable.view_dataView full computed data.
Examples
>>> from lactuca import LifeTable >>> lt = LifeTable("GAM71", sex="m") >>> lt.head(3) >>> lt.head(3, show_normalized=True)
- lx(x: object = None) object#
Return lx values (number of lives at age x) for the specified age(s).
Computes the expected number of lives remaining at age(s) x from an initial cohort. Supports integer and fractional ages with configurable interpolation.
- Parameters:
x (float, int, sequence of float, NDArray[np.float64], or None, optional) – Age(s) to evaluate. Pandas and Polars Series are accepted; converted to float64 internally. If None, returns all lx values from age 0 to omega.
- Returns:
lx value(s). Returns scalar if x is scalar, array otherwise.
- Return type:
Union[float, NDArray[np.float64]]
- Raises:
ValueError – If any age is negative.
TypeError – If
xis not numeric or a sequence of numerics.
Notes
If x is None, returns the full \(l_x\) array for ages 0 to \(\omega\).
For fractional ages, uses interpolation method from config.lx_interpolation (‘linear’ or ‘exponential’).
Ages beyond \(\omega\) return 0.0 (actuarial convention).
Results rounded to config.decimals.lx.
Examples
>>> from lactuca import LifeTable >>> lt = LifeTable('PASEM2010', 'm') >>> lt.lx(50) 97856.23 >>> lt.lx([50, 51, 52]) array([97856.23, 97612.45, 97365.12])
See also
DecrementTable.qxDecrement probability at age x.
DecrementTable.pxSurvival probability at age x.
DecrementTable.dxDecrement count between ages x and x+1.
- modify_qx(modifications: ModifyDecrement) None#
Apply modifications to the table’s decrement rates.
Actuarial adjustments to the base rates — age shifting, scaling, aggravated risk factors, or table combinations — are applied to a copy of the base rates and can be reset with
reset_modifications().- Parameters:
modifications (dict) –
Dictionary specifying one or more modification operations. See ModifyDecrement TypedDict for supported keys:
- ’age_shift’int
Shift all ages by N years (drops first N ages).
- ’decrement_multiplier’float or sequence of floats
Multiply all qx values by a constant factor (e.g., 1.1 for +10%). Can be a scalar (applied to all ages) or an array/list of length w+1 (age-specific adjustments).
- ’decrement_geometric_increase’tuple[float, int]
Apply geometric growth to tail: (growth_rate, start_age).
- ’aggravated_risk’float
Apply aggravated-risk transform on survival probabilities: \(p'_x = p_x^{\text{factor}}\), equivalently \(q'_x = 1 - (1 - q_x)^{\text{factor}}\).
- ’table_combination’Union[DecrementTable, list, tuple]
Combine with one or more tables using independent competitive risks (default) or UDD associated singles when
combination_mode='udd'. Pass a single DecrementTable, or a non-empty list/tuple of DecrementTable instances. Allowed combinations (self.table_type → other.table_type):'life'→'exit','disability';'disability'→'exit';'exit'→'exit'. All combined tables must have the same sex asself. Array indexiis integer agex = i; the host table keeps its length and omega; shorter other tables contributeq = 0beyond their last index. Other tables withlen(_decrement) != len(_decrement_base)are rejected. When both tables are generational, cohort years must match. When both tables are select tables with explicit integer durations, durations must match.
- ’combination_mode’
'independent'or'udd' Optional actuarial assumption for
table_combination(default'independent'when omitted).'udd'applies the UDD associated-single formula for two or three causes (host plus one or two others). Requirestable_combinationin the same dict. For two or three causes,'independent'and'udd'yield the same collapsed totalq.
- ’combination_mode’
- Raises:
ValueError – If modification parameters are invalid, including incompatible table types, sex/cohort/duration mismatches, empty
table_combinationsequences, or non-actuarial results. If the table is still in pending state (metadata_pending=True).TypeError – If modification keys or values have incorrect types.
Notes
Modifications are applied in iteration order (dict order in Python 3.7+).
All computations use float64 precision internally.
Final results are rounded to the decimal precision configured for the current table type (qx, ix, or ox decimals from
Config.decimals).Combined tables must have matching sex.
Use reset_modifications() to restore original base rates.
Non-accumulative: each call to
modify_qxreplaces any previous modification entirely — it always starts from the original unmodified base rates. To accumulate multiple operations, pass all keys in a single call as a dict, or callreset_modifications()explicitly between independent experiments.
``table_combination`` actuarial contract (see also Modifying Decrements):
Default
combination_mode='independent': independent competing risks \(q^{\mathrm{comb}}_x = 1 - (1-q^{\mathrm{host}}_x)\prod_j(1-q^{(j)}_x)\).combination_mode='udd': UDD associated singles (two or three causes) collapsed to the same total \(q^{\mathrm{comb}}_x\); per-cause \(q'^{(j)}\) are not returned by the public API.Array index
iis integer agex = iwhen no priorage_shiftin the same dict; afterage_shift=n, indexiis calendar agen + i. Tables withstart_age > 0must have zero padding belowstart_age.Host keeps its array length through combination; shorter other tables imply \(q_{\mathrm{other}} = 0\) beyond their last index.
Reads each
other._decrement(active float64 vector; may reflect priormodify_*on that table). Useother.reset_modifications()or a fresh instance for file/base rates. Not the publicqx()/ix()/ox()accessors (rounded; beyond-\(\omega\) API may differ from alignment).Rejects the host instance in
othersand duplicate table instances inothers.Rejects host padding below
start_age(including after a priorage_shiftin the same dict) and rates outside[0, 1]on the host or any other table before combining.Truncates at the first combined \(q = 1\).
Rejects
len(other._decrement) != len(other._decrement_base)and combined \(q > 1\). The same combined rates feed all fourcalculation_modevalues on downstream calculations. Modes are actuarially coherent (same product/conventions) but not required to return numerically identical results.Modification keys are applied in dict order;
combination_modemay appear before or aftertable_combination, but other keys such asdecrement_multiplierinteract withtable_combinationin insertion order.
Examples
>>> from lactuca import ExitTable, LifeTable >>> lt = LifeTable("PASEM2010", "m") >>> lt.modify_qx({"decrement_multiplier": 1.1}) >>> lt.qx(50) 0.00275
Apply age-specific multipliers:
>>> import numpy as np >>> factors = np.ones(113) >>> factors[50:70] *= 0.95 >>> lt.reset_modifications() >>> lt.modify_qx({"decrement_multiplier": factors})
>>> lt.reset_modifications() >>> lt.modify_qx({"age_shift": 5, "aggravated_risk": 1.5}) >>> lt.w 107
>>> et_combine = ExitTable("DummyEXIT", sex="m") >>> lt.modify_qx({"table_combination": et_combine})
See also
DecrementTable.reset_modificationsRestore original base rates.
- px(x: object = None, m: Literal[1, 2, 3, 4, 6, 12, 14, 24, 26, 52, 365] = 1) float | NDArray[float64]#
Return probability of surviving one period (px) at age x.
Computes the survival probability for one payment period of length 1/m years, starting at age x. Supports fractional payment frequencies and vectorized operations.
- Parameters:
x (float, int, sequence of float, NDArray[np.float64], or None, optional) – Entry age(s) (scalar or array). If None, returns px for all ages in the table.
m (PaymentFrequencyLiteral, optional) – Number of payments per year (1=annual, 12=monthly, etc.). Default is 1.
- Returns:
Survival probability value(s). Returns scalar if x is scalar, array otherwise.
- Return type:
Union[float, NDArray[np.float64]]
Notes
For integer ages and m=1, returns the standard survival probability \(p_x = 1 - q_x\).
For non-integer ages or m>1, uses interpolation: \({}^{1/m}p_x = \frac{l_{x + 1/m}}{l_x}\)
If x is None, returns the full \(p_x\) array and ignores m parameter.
All calculations vectorized using NumPy for maximum speed.
Results rounded to configured decimals (config.decimals.px).
- Raises:
ValueError – If any age in
xis negative. If the table is still in pending state (metadata_pending=True).TypeError – If
xis not numeric, or ifmis not a scalar integer.
Examples
>>> from lactuca import LifeTable >>> lt = LifeTable('PASEM2010', 'm') >>> lt.px(50) 0.99751 >>> lt.px(50, m=12) # Monthly survival probability 0.99979
See also
DecrementTable.qxDecrement probability (complement of px for m=1).
DecrementTable.tpxMulti-period survival probability.
- qx(x: object = None, m: Literal[1, 2, 3, 4, 6, 12, 14, 24, 26, 52, 365] = 1) float | NDArray[float64]#
Return probability of decrement at age x.
Computes the decrement probability (\(q_x\) for life, \(i_x\) for disability, \(o_x\) for exit) for one payment period of length \(1/m\) years, starting at age \(x\). Supports fractional payment frequencies and vectorized operations.
- Parameters:
x (float, int, sequence of float, NDArray[np.float64], or None, optional) – Entry age(s) (scalar or array). If None, returns decrement for all ages in the table.
m (PaymentFrequencyLiteral, optional) – Number of payments per year (1=annual, 12=monthly, etc.). Default is 1.
- Returns:
Decrement probability value(s). Returns scalar if x is scalar, array otherwise.
- Return type:
Union[float, NDArray[np.float64]]
Notes
For integer ages and m=1, returns the precomputed decrement value.
For ages \(x \ge \omega\), returns \(q_x = 1.0\) (no survivors at or beyond the table limit).
For non-integer ages or m>1, uses interpolation: \({}^{1/m}q_x = 1 - \frac{l_{x + 1/m}}{l_x}\)
If x is None, returns the full decrement array and ignores m parameter.
All calculations vectorized using NumPy for maximum speed.
Results rounded to configured decimals based on table_type.
Consistent with international actuarial practice.
- Raises:
ValueError – If any age in
xis negative. If the table is still in pending state (metadata_pending=True).TypeError – If
xis not numeric, or ifmis not a scalar integer.
Examples
>>> from lactuca import LifeTable >>> lt = LifeTable('PASEM2010', 'm') >>> lt.qx(50) 0.00249 >>> lt.qx(50, m=12) # Monthly decrement probability 0.00021
See also
DecrementTable.pxSurvival probability (complement of qx for m=1).
DecrementTable.tqxMulti-period decrement probability.
DecrementTable.modify_qxApply actuarial adjustments to the decrement rates.
- reset_modifications() None#
Restore decrement to the original base values and clear all caches.
Notes
Resets the decrement array to its pre-modification state (equivalent to the table as loaded, before any
modify_qx()call).Sets
modifiedtoFalseand clearsmodifications_applied, sorepr()no longer shows stale entries.Restores
wto its original value, undoing any omega reduction caused by anage_shiftmodification.Invalidates all cached derived values (\(l_x\), \(p_x\), and related caches).
See also
DecrementTable.modify_qxApply modifications to decrement rates.
Examples
>>> from lactuca import LifeTable >>> lt = LifeTable('PASEM2020_Rel_1o', 'm') >>> lt.modify_qx({'decrement_multiplier': 1.1}) >>> lt.modified True >>> lt.reset_modifications() >>> lt.modified False >>> lt.modifications_applied []
- summary() str#
Return a summary of the table metadata and current state.
This base implementation provides common information:
Class name and table name
Sex (and unisex blend weight if
sex='u')Table taxonomy: type, age range, valid sexes, generational/select flags
Cohort year (generational tables only)
Duration column (select tables only)
w (current): effective omega, which may be lower thanomegaafter anage_shiftortable_combinationtruncation at combined \(q = 1\)Modified: True/FalseModifications applied: key=value, ...(only when modified)Note: table_combination truncated omega (...)when combination shortened the host below its pre-combination omega
Subclasses call this method via
super().summary()and append rate-type–specific information (decimals, sample values).- Returns:
Multi-line summary string with table metadata and modification state.
- Return type:
str
Notes
When
metadata_pending=True, this method returns an abbreviated summary that lists only the table name, a"Metadata: pending (needs: ...)"line showing which fields are still missing, the taxonomy block, andw (current). No calculation is attempted on the unconfigured table. Subclasses such asLifeTabledetect the pending state and delegate entirely to this early-return path, so no sample qx/px values are shown.Examples
>>> from lactuca import LifeTable >>> lt = LifeTable('PASEM2010', 'm') >>> print(lt.summary()) LifeTable: PASEM2010 Sex: m Type: life | Ages: 0–112 | Sexes: m, f | Period table w (current): 112 Modified: False
>>> lt_p = LifeTable('PER2020_Ind_1o', pending=True) >>> print(lt_p.summary()) LifeTable: PER2020_Ind_1o Metadata: pending (needs: sex, cohort) Sex: None ...
- tail(n: int = 10, include_age: bool = True, show_normalized: bool = False) DataFrame#
Return the last n rows of the computed instance data, always ending at w.
Shows only columns relevant to this instance (sex, duration, modifications).
- 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 w). If False (default), consider only meaningful range (start_age to w).
- Returns:
The last n rows with or without age column.
- Return type:
polars.DataFrame
See also
DecrementTable.headView first n rows.
DecrementTable.view_dataView full computed data.
Examples
>>> from lactuca import LifeTable >>> lt = LifeTable("GAM71", sex="m") >>> lt.tail(3)
- tpx(x: object, *, t: object = 1) float | NDArray[float64]#
Return interval survival probability tpx for all (x, t) combinations.
Computes the probability that a life aged x survives for t years, using the formula: \({}_{t}p_x = \frac{l_{x+t}}{l_x}\) Supports both scalar and array inputs for ages and durations.
- Parameters:
x (float, int, sequence of float, or NDArray[np.float64]) – Entry age(s) (scalar or array). Pandas and Polars Series are accepted.
t (float, int, sequence of float, or NDArray[np.float64], optional) – Duration(s) in years (scalar or array). Default is 1. Pandas and Polars Series are accepted.
- Returns:
Survival probability(ies). Returns scalar if both x and t are scalar, otherwise array with appropriate broadcasting.
- Return type:
Union[float, NDArray[np.float64]]
Notes
Fast path for t=1 with integer ages uses
px()directly to avoid lx rounding inconsistencies.For non-integer ages or durations, lx interpolation is used as configured.
All calculations vectorized using NumPy for maximum speed.
Results rounded to config.decimals.tpx.
Consistent with international actuarial practice.
- Raises:
ValueError – If any age in x or any duration in t is negative.
TypeError – If x or t is not numeric.
Examples
>>> from lactuca import LifeTable >>> lt = LifeTable('PASEM2010', 'm') >>> lt.tpx(50, t=10) # 10-year survival probability 0.97234 >>> lt.tpx([50, 55], t=[1, 5, 10]) # Grid of probabilities array([[0.99751, 0.98876, 0.97234], [0.99623, 0.98456, 0.96123]])
See also
DecrementTable.tqxMulti-period decrement probability.
DecrementTable.pxOne-period survival probability.
- tqx(x: object, *, t: object = 1) float | NDArray[float64]#
Return interval death probability tqx for all (x, t) combinations.
Computes the probability that a life aged x dies within t years, using the formula: \({}_{t}q_x = \frac{l_x - l_{x+t}}{l_x} = 1 - {}_{t}p_x\) Supports both scalar and array inputs for ages and durations.
- Parameters:
x (float, int, sequence of float, or NDArray[np.float64]) – Entry age(s) (scalar or array). Pandas and Polars Series are accepted.
t (float, int, sequence of float, or NDArray[np.float64], optional) – Duration(s) in years (scalar or array). Default is 1. Pandas and Polars Series are accepted.
- Returns:
Death probability(ies). Returns scalar if both x and t are scalar, otherwise array with appropriate broadcasting.
- Return type:
Union[float, NDArray[np.float64]]
Notes
Computed as complement of interval survival: \({}_{t}q_x = 1 - {}_{t}p_x\).
For non-integer ages or durations, lx interpolation is used as configured.
All calculations vectorized using NumPy for maximum speed.
Results rounded to config.decimals.tqx.
Consistent with international actuarial practice.
- Raises:
ValueError – If any age in x or any duration in t is negative.
TypeError – If x or t is not numeric.
Examples
>>> from lactuca import LifeTable >>> lt = LifeTable('PASEM2010', 'm') >>> lt.tqx(50, t=10) # 10-year death probability 0.02766 >>> lt.tqx([50, 55], t=[1, 5, 10]) # Grid of probabilities array([[0.00249, 0.01124, 0.02766], [0.00377, 0.01544, 0.03877]])
See also
DecrementTable.tpxMulti-period survival probability.
DecrementTable.qxOne-period decrement probability.
- view_data(include_age: bool = True, show_normalized: bool = False) DataFrame#
Return the computed instance data, optionally excluding normalized padding rows.
Shows only columns relevant to this instance (sex, duration, modifications, and MI column when applicable). Does not delegate to the raw
TableSource. For raw multi-column data (all sexes, all columns), uselt.table.view_data().- Parameters:
include_age (bool, default True) – If True, include ‘age’ column. If False, exclude ‘age’ column from output.
show_normalized (bool, default False) – If True, show full normalized range (0 to w). If False (default), show only meaningful range (start_age to w).
- Returns:
Computed instance data with or without age column.
- Return type:
polars.DataFrame
- Raises:
ValueError – If the table is still in pending state (
metadata_pending=True).
See also
DecrementTable.headView first n rows.
DecrementTable.tailView last n rows.
Examples
>>> from lactuca import LifeTable >>> lt = LifeTable("GAM71", sex="m") >>> df = lt.view_data() >>> df_full = lt.view_data(show_normalized=True)
- property base_year: int | None#
Base year for generational improvement.
- Returns:
Calendar year of the base mortality rates (e.g.,
2020).Nonefor period tables without generational improvement.- Return type:
int or None
Notes
Delegates to
TableSourcepropertybase_year.
- property cohort: int | None#
Current cohort (year of birth) for generational tables.
- Returns:
Year of birth for generational tables, None for period tables.
- Return type:
Union[int, None]
Examples
>>> from lactuca import LifeTable >>> lt = LifeTable('PER2020_Ind_1o', 'm', cohort=1970) >>> lt.cohort 1970
- property data: DataFrame#
Raw underlying table data (all columns, all sexes).
- Returns:
Complete table DataFrame with all decrement, improvement, and metadata columns for all sexes.
- Return type:
polars.DataFrame
Notes
Delegates to
TableSourcepropertydata.This returns the full raw DataFrame as stored in the table file. For instance-specific computed data filtered to the active sex and cohort, use
view_data()instead.
- property decimals: _DecimalsConfig#
Access decimal precision settings for all actuarial functions.
This property provides convenient attribute-based access to global decimal precision configuration. All values are proxied dynamically from the
Configsingleton — no state is stored locally in the table instance.- Returns:
Decimal-precision proxy with attributes for actuarial table columns. Common attributes include:
lx: int — Survival function \(\ell_x\) precisiondx: int — Deaths \(d_x\) precisionqx: int — Mortality rate \(q_x\) precisionpx: int — Survival probability \(p_x\) precisiontpx: int — Multi-year survival \({}_t p_x\) precisiontqx: int — Multi-year mortality \({}_t q_x\) precisionix: int — Disability incidence \(i_x\) precisionox: int — Exit rate \(o_x\) precisionDx,Nx,Sx: int — Commutation functions precisionCx,Mx,Rx: int — Insurance commutation functions precision.A_x = M_x/D_xuses end-of-year death placement;Cxappliesmortality_placementoffset (default'mid'). Usemortality_placement='end'for the classical identity.ex: int — Life expectancy \(\mathring{e}_x\) precisionax,Ax: int — Annuities and insurances precision
- Return type:
object
- Raises:
AttributeError – If accessing a property blocked by the specific table subclass. See the subclass
decimalsdocstring for the full list of blocked properties per table type.
Notes
Read-only proxy:
self.decimals.xxx = valueraisesAttributeErrorby design; useConfig().decimals.xxx = valueto change precision globally.Global scope: Changes to
Config.decimalsare immediately visible through all table instances.For permanent changes, assign on the global config proxy, e.g.
Config().decimals.lx = valueorConfig().set('decimals_lx', value).
Examples
>>> from lactuca import LifeTable >>> lt = LifeTable('PASEM2010', 'm') >>> lt.decimals.lx 2 >>> lt.decimals.qx 5 >>> lt.decimals.Dx 2
See also
Config.decimals: Global decimal precision configuration.
- property description: str#
Human-readable table description from metadata.
- Returns:
Descriptive text for the table (e.g.,
'Spanish mortality table 2010').- Return type:
str
Notes
Delegates to
TableSourcepropertydescription.
- property duration: int | str | None#
Return the current policy duration for select or non-select tables.
Returns the active select duration index,
'ult'when the ultimate column is selected, orNonefor non-select tables.The minimum valid integer duration is
start_duration(0for CMI/UK convention,1for most other tables).- Returns:
Active select duration (integer),
'ult'for the ultimate column, orNonefor non-select tables.- Return type:
int or ‘ult’ or None
Examples
>>> from lactuca import LifeTable >>> lt = LifeTable("DummyLIFE_Select", "m", duration=1) >>> lt.duration 1 >>> lt.duration = "ult" >>> lt.duration 'ult'
- property file_name: str#
Basename of the loaded table file.
- Returns:
File name with extension (e.g.,
'PASEM2010.ltk').- Return type:
str
Notes
Delegates to
TableSourcepropertyfile_name.
- property file_path: Path#
Absolute path to the loaded table file.
- Returns:
Full filesystem path to the .ltk file.
- Return type:
pathlib.Path
Notes
Delegates to
TableSourcepropertyfile_path.
- property generational: bool#
Whether the table includes cohort-based improvement factors.
- Returns:
Truefor generational tables with mortality improvement factors;Falsefor period tables.- Return type:
bool
Notes
Delegates to
TableSourcepropertygenerational.Examples
>>> from lactuca import LifeTable >>> LifeTable("PASEM2010", "m").generational False
- property generational_formula_type: str | None#
Generational improvement formula type.
- Returns:
Formula identifier (e.g.,
'exponential_improvement','linear_improvement','discrete_improvement','projected_improvement').Nonefor period tables.- Return type:
str or None
Notes
Delegates to
TableSourcepropertygenerational_formula_type.
- property grid_years: list[int] | None#
Projection calendar years for year-indexed MI tables.
Here, MI means mortality improvement factors (rate-reduction factors) indexed by calendar year, typically with columns such as
mi_m_2020ormi_f_2035.- Returns:
Sorted list of calendar years for which year-indexed MI columns exist, or
Nonefor tables without year-indexed improvement factors.- Return type:
list[int] or None
Notes
Delegates to
TableSourcepropertygrid_years.
- property is_select: bool#
Return True if the underlying table is a select-ultimate table.
- Returns:
Trueif the table has select-period columns;Falsefor period (non-select) tables.- Return type:
bool
Notes
This is an alias for
select.
- property metadata: dict[str, Any]#
Raw metadata dictionary from the table file.
- Returns:
Complete metadata with all table properties (omega, base_year, description, etc.).
- Return type:
dict
Notes
Delegates to
TableSourcepropertymetadata.
- property metadata_pending: bool#
True when required metadata has not yet been supplied via configure() or setters.
A pending table is one created with
pending=Truethat still lacks at least one of:sex,cohort(generational tables), orduration(select tables). For select tables,duration='ult'is stored internally as_duration=None; completion is tracked separately from the numeric duration value. Attempting calculations on a pending table raisesValueError; useconfigure()or the individual property setters to complete configuration.- Returns:
Truewhile the table is not yet fully configured,Falseotherwise.- Return type:
bool
Notes
The flag is cleared automatically once all required metadata is present — either by a successful
configure()call or by the last individual setter that completes the configuration. It is also preserved acrosscopy.deepcopy().summary()andrepr()display the pending state and list the missing metadata fields; all calculation methods raiseValueErroruntil the flag is cleared.See also
configure()Atomically supply all missing metadata in one call.
batch_update()Context manager for imperative setter-based configuration.
Examples
>>> from lactuca import LifeTable >>> lt = LifeTable('PER2020_Ind_1o', pending=True) >>> lt.metadata_pending True >>> lt.configure(sex='m', cohort=1969) >>> lt.metadata_pending False
- property mi_by_duration: bool#
Whether improvement factors are stored by policy duration.
Truemeans the table stores separate mortality-improvement columns by duration (for example,mi_m_s1,mi_m_s2, …,mi_m_ult).Falsemeans a single improvement column per sex is shared across durations (for example,mi_m).- Returns:
Trueif per-duration MI columns are present;Falseif a single shared column is used.- Return type:
bool
Notes
Delegates to
TableSourcepropertymi_by_duration.
- property mi_structure: str | None#
Mortality improvement column layout tag.
- Returns:
Layout identifier (e.g.,
'flat','year_indexed','select_period').Nonefor period tables.- Return type:
str or None
Notes
Delegates to
TableSourcepropertymi_structure.
- property modifications_applied: list#
List of modification descriptors applied in the current call.
Each entry is a string of the form
'key=value', in the order the keys were applied. The list is empty whenmodifiedisFalse.- Returns:
A defensive copy of the internal list, e.g.
['age_shift=2', 'decrement_multiplier=1.05'].- Return type:
list of str
Examples
>>> from lactuca import LifeTable >>> lt = LifeTable("PASEM2020_Rel_1o", "m") >>> lt.modifications_applied [] >>> lt.modify_qx({"age_shift": 2, "decrement_multiplier": 1.05}) >>> lt.modifications_applied ['age_shift=2', 'decrement_multiplier=1.05']
- property modified: bool#
True if a modification is currently active on this table.
- Returns:
Trueafter a successfulmodify_qx()(ormodify_ix/modify_ox) call;Falseafterreset_modifications()or after reassigning any ofsex,cohort,duration, orunisex_blend.- Return type:
bool
Examples
>>> from lactuca import LifeTable >>> lt = LifeTable("PASEM2020_Rel_1o", "m") >>> lt.modified False >>> lt.modify_qx({"decrement_multiplier": 1.05}) >>> lt.modified True >>> lt.reset_modifications() >>> lt.modified False
- property omega: int#
Original terminal age from metadata (immutable).
- Returns:
Terminal age \(\omega\) as recorded in the table file.
- Return type:
int
Notes
Delegates to
TableSourcepropertyomega.This is the original omega defined in the file metadata and never changes. For the current effective omega (which may decrease after age-shift modifications), use the mutable
wproperty instead.Examples
>>> from lactuca import LifeTable >>> lt = LifeTable("PASEM2010", "m") >>> lt.omega 112
- property select: bool#
Whether this is a select-ultimate table.
- Returns:
Trueif the table has select-period columns;Falsefor standard period (non-select) tables.- Return type:
bool
Notes
Delegates to
TableSourcepropertyselect.See also
DecrementTable.is_selectAlias for this property.
Examples
>>> from lactuca import LifeTable >>> LifeTable("PASEM2010", "m").select False
- property select_improvement_diagonal: str | None#
Calendar-year index for generational improvement on select columns.
- Returns:
'cohort_plus_x_plus_d'forprojected_improvementselect tables,'cohort_plus_x'for other generational select tables, orNonefor period or aggregate generational tables.- Return type:
str or None
Notes
Delegates to
TableSourcepropertyselect_improvement_diagonal. See that property for the actuarial definition of each diagonal convention.See also
TableSource.select_improvement_diagonalFull actuarial specification.
- property select_period: int | None#
Select period in years.
- Returns:
Number of years in the select period (e.g.,
5for a 5-year select table).Nonefor non-select tables.- Return type:
int or None
Notes
Delegates to
TableSourcepropertyselect_period.
- property sex: Literal['m', 'f', 'u']#
Current sex used for calculations.
- Returns:
Sex identifier: ‘m’ (male), ‘f’ (female), or ‘u’ (unisex).
- Return type:
SexLiteral
Examples
>>> from lactuca import LifeTable >>> lt = LifeTable('PASEM2010', 'm') >>> lt.sex 'm'
- property sex_independent: bool#
Whether rates are identical for both sexes.
- Returns:
Trueif male and female rates are identical (unisex table);Falseif rates differ by sex.- Return type:
bool
Notes
Delegates to
TableSourcepropertysex_independent.
- property start_age: int#
Minimum defined age in the original table.
- Returns:
First age with data (typically
0, occasionally1or higher).- Return type:
int
Notes
Delegates to
TableSourcepropertystart_age.
- property start_duration: int | None#
Minimum integer duration.
- Returns:
First valid duration index (
0for CMI/UK convention,1for most tables).Nonefor non-select tables.- Return type:
int or None
Notes
Delegates to
TableSourcepropertystart_duration.
- property table: TableSource#
Access the underlying TableSource instance with table data and metadata.
Returns a reference to the internal table source loaded during initialization. This property allows inspection of table metadata and structure.
- Returns:
The underlying table source instance with raw data and metadata.
- Return type:
Notes
For the current effective omega (which may change after modifications), use
winstead oftable.w.Most metadata properties are accessible directly on the
DecrementTableinstance (e.g.table_name,generational,start_age,select, etc.) without going throughtable.xxx.Do not modify the returned object directly. Direct mutations bypass validation and invalidate internal caches, leading to incorrect results.
Examples
>>> from lactuca import LifeTable >>> lt = LifeTable("PASEM2010", "m") >>> lt.table.table_name 'PASEM2010' >>> lt.table.generational False >>> lt.table.w 112 >>> lt.modify_qx({"age_shift": 5}) >>> lt.w # current effective omega after modification 107 >>> lt.table.w # original omega (unchanged) 112
See also
DecrementTable.wCurrent upper age limit (omega), accounting for modifications.
- property table_name: str#
Name of the underlying actuarial table.
- Returns:
Table file name without extension (e.g.,
'PASEM2010').- Return type:
str
Notes
Delegates to
TableSourcepropertytable_name.Examples
>>> from lactuca import LifeTable >>> lt = LifeTable("PASEM2010", "m") >>> lt.table_name 'PASEM2010'
- abstract property table_type: str#
Return the canonical type identifier for this decrement table.
- Returns:
One of
'life','disability', or'exit'.- Return type:
str
Notes
Must be implemented by subclasses. The returned value determines:
Decrement column naming convention (
qx_*for life,ix_*for disability,ox_*for exit).Decimal precision configuration used for decrement outputs (
decimals.qx,decimals.ix,decimals.ox).Valid table combinations accepted by
modify_qx().
- property unisex_blend: float | None#
Male weight used to blend male/female rates into unisex rates.
- Returns:
Male weight in [0.0, 1.0] if a unisex blend has been applied, None otherwise.
- Return type:
Union[float, None]
Examples
>>> from lactuca import LifeTable >>> lt = LifeTable('PER2020_Ind_1o', 'u', cohort=1970, unisex_blend=0.5) >>> lt.unisex_blend 0.5
- property valid_sexes: list[str]#
Valid sexes for this table (e.g.
['m', 'f']).- Returns:
Sex identifiers available in this table, e.g.
['m', 'f']or['m', 'f', 'u']when a native unisex column is present.- Return type:
list of str
Notes
Delegates to
TableSourcepropertyvalid_sexes.
- property w: int#
Current upper age limit (omega), accounting for modifications.
This value may differ from the original table omega after applying modifications like
age_shift. For the original table omega value, useomega.- Returns:
Maximum age in the current (possibly modified) table state.
- Return type:
int
See also
DecrementTable.omegaOriginal terminal age from the table metadata.
Examples
>>> from lactuca import LifeTable >>> lt = LifeTable("PASEM2010", sex="m") >>> lt.w 112 >>> lt.modify_qx({"age_shift": 5}) >>> lt.w # reduced after age_shift 107 >>> lt.omega # original table omega, unchanged 112