Projection
gaspatchio_core.accessors.projection.ProjectionColumnAccessor
¶
Bases: BaseColumnAccessor
Actuarial projection operations for time-series calculations.
This accessor provides methods for transforming rates and probabilities into cumulative values over projection periods. Complex operations like cumulative products use these methods, while simple operations like multiplication should use standard operators.
Design Philosophy
- Complex cumulative operations: Use projection methods
- Simple arithmetic: Use operators (
*,+,-,/) - Terminal/aggregate values: Use Polars (
.list.last())
Accessed via .projection on a column or expression, e.g.,
af["mortality_rate"].projection.cumulative_survival().
Examples:
Cumulative survival from mortality rates:
from gaspatchio_core import ActuarialFrame
data = {"qx": [[0.001, 0.0011, 0.0012], [0.002, 0.0022, 0.0024]]}
af = ActuarialFrame(data)
# Complex cumulative product - use projection method
af.survival_to_t = af.qx.projection.cumulative_survival()
# Simple multiplication - use operators
af.death_benefit = af.face_amount * af.survival_to_t * af.qx
af.premium = af.annual_premium * af.survival_to_t
# Terminal value - use Polars
af.maturity_benefit = af.face_amount.list.last()
Premium holiday modeling:
from gaspatchio_core import ActuarialFrame
data = {"premium": [[1000, 1000, 1000, 1000, 1000]]}
af = ActuarialFrame(data)
# Period override - use projection method
af.premium_with_holiday = af.premium.projection.with_period(3, value=0)
# Result: [1000, 1000, 1000, 0, 1000]
at_period(relative_period, fill_value=0.0)
¶
Get value at relative period offset.
Access values from other time periods using mathematical t notation. Negative values reference prior periods (t-1, t-2), positive values reference future periods (t+1, t+2).
This method provides flexible time-shifting for arbitrary period offsets,
complementing the convenience methods previous_period() (t-1) and
next_period() (t+1).
For list columns, shifts values within each list. For scalar columns,
shifts across rows (use .over() for grouping).
When to use
- Multi-Period Lag Analysis: Access values from multiple periods back (t-2, t-3) for trend analysis and smoothing calculations.
- Reserve Rollforward: Reference reserves from specific prior periods in complex reserve formulas requiring multiple lag periods.
- Experience Studies: Compare values across multiple time periods to analyze experience trends and validate assumptions.
- Flexible Time-Shifting: Use when previous_period() and next_period() don't provide the specific offset needed for your calculation.
Parameters¶
relative_period : int Period offset from current time using mathematical notation: - Negative values: prior periods (e.g., -1 for t-1, -2 for t-2) - Positive values: future periods (e.g., 1 for t+1, 2 for t+2) - Zero: current period (no shift) fill_value : scalar, optional Value to use for missing entries at boundaries. Default is 0.
Returns¶
ExpressionProxy Expression with values from specified relative period
Examples¶
Previous Period: t-1
from gaspatchio_core import ActuarialFrame
data = {"reserve": [[1000, 1100, 1200]]}
af = ActuarialFrame(data)
# at_period(-1) is equivalent to previous_period()
af.reserve_t1 = af.reserve.projection.at_period(-1)
print(af.collect())
shape: (1, 2)
┌──────────────────┬──────────────────┐
│ reserve ┆ reserve_t1 │
│ --- ┆ --- │
│ list[i64] ┆ list[i64] │
╞══════════════════╪══════════════════╡
│ [1000, 1100, ...] ┆ [0, 1000, 1100] │
└──────────────────┴──────────────────┘
Two Periods Back: t-2
from gaspatchio_core import ActuarialFrame
data = {"value": [[100, 110, 120, 130, 140]]}
af = ActuarialFrame(data)
af.value_t2 = af.value.projection.at_period(-2)
print(af.collect())
shape: (1, 2)
┌───────────────────────┬──────────────────────┐
│ value ┆ value_t2 │
│ --- ┆ --- │
│ list[i64] ┆ list[i64] │
╞═══════════════════════╪══════════════════════╡
│ [100, 110, 120, 13... ┆ [0, 0, 100, 110, 120]│
└───────────────────────┴──────────────────────┘
Next Period: t+1
from gaspatchio_core import ActuarialFrame
data = {"cashflow": [[1000, 1100, 1200]]}
af = ActuarialFrame(data)
# at_period(1) is equivalent to next_period()
af.cf_tp1 = af.cashflow.projection.at_period(1)
print(af.collect())
shape: (1, 2)
┌──────────────────┬─────────────────┐
│ cashflow ┆ cf_tp1 │
│ --- ┆ --- │
│ list[i64] ┆ list[i64] │
╞══════════════════╪═════════════════╡
│ [1000, 1100, ...] ┆ [1100, 1200, 0] │
└──────────────────┴─────────────────┘
Reserve Rollforward Formula
from gaspatchio_core import ActuarialFrame
data = {
"reserve": [[0, 950, 1900, 2850]],
"premium": [[1000, 1000, 1000, 1000]],
"interest": [[50, 52, 55, 58]],
"benefit": [[100, 102, 105, 108]],
}
af = ActuarialFrame(data)
# Reserve rollforward formula:
# Reserve(t) = Reserve(t-1) + Premium(t) + Interest(t) - Benefit(t)
af.reserve_t1 = af.reserve.projection.at_period(-1)
af.reserve_calc = af.reserve_t1 + af.premium + af.interest - af.benefit
print(af.collect())
shape: (1, 6)
┌─────────────────┬─────────────┬─────────┬─────────┬──────────┬──────────────┐
│ reserve ┆ premium ┆ intere.. ┆ benefit ┆ reserve..┆ reserve_calc │
│ --- ┆ --- ┆ --- ┆ --- ┆ --- ┆ --- │
│ list[i64] ┆ list[i64] ┆ list.. ┆ list.. ┆ list[i64]┆ list[i64] │
╞═════════════════╪═════════════╪═════════╪═════════╪══════════╪══════════════╡
│ [0, 950, 19...┆ [1000, 10...┆ [50, 52...┆ [100, ...┆ [0, 0, 9...┆ [950, 19...│
└─────────────────┴─────────────┴─────────┴─────────┴──────────┴──────────────┘
See Also¶
previous_period : Convenience method for t-1 next_period : Convenience method for t+1
cumulative_survival(rate_timing=None, start_at=1.0)
¶
Convert mortality rates to cumulative survival probabilities.
Transforms period mortality rates (qx) into cumulative survival probabilities
using the formula tpx[t] = (1-qx[0]) * (1-qx[1]) * ... * (1-qx[t]). Essential
for life insurance projections, reserve calculations, and any actuarial work
requiring survival probabilities from mortality assumptions.
For list columns, applies element-wise cumulative product within each list.
For scalar columns, applies cumulative product across rows (use .over() for
grouping by policy).
When to use
- Life Insurance Projections: Calculate the probability policies remain inforce for death benefit, premium, and cash value projections.
- Reserve Calculations: Compute expected policy counts for reserve valuations and capital requirements.
- Persistency Analysis: Model combined mortality and lapse decrements to project policy persistency over time.
- Pricing Models: Calculate expected present values of benefits and premiums weighted by survival probabilities.
Timing Conventions¶
The rate_timing parameter controls when decrement rates are applied:
-
beginning_of_period (default): Rate at period t is NOT yet applied to P[IF][t]. The survival at t represents the probability of surviving TO the start of period t. Result:
[1.0, tpx[0], tpx[0]*tpx[1], ...] -
end_of_period: Rate at period t HAS been applied to P[IF][t]. The survival at t represents the probability of surviving THROUGH period t. This matches Excel-style timing. Result:
[tpx[0], tpx[0]*tpx[1], ...]
With constant rates, both conventions give identical values. The difference only appears when rates change over time (e.g., at age boundaries).
Parameters¶
rate_timing : {"beginning_of_period", "end_of_period"}, optional When decrement rates are applied. Recommended for most users:
- ``"beginning_of_period"``: Rate at t NOT yet applied (default behavior)
- ``"end_of_period"``: Rate at t HAS been applied (Excel-style)
If not specified, falls back to `start_at` parameter behavior.
start_at : float, optional
Lower-level control over timing. Only use if rate_timing is not set.
Initial survival probability to prepend at t=0:
- 1.0 (default): Beginning-of-period [1.0, tpx[0], tpx[1], ...]
- None: End-of-period [tpx[0], tpx[1], ...]
- Other: Custom initial value (e.g., 0.95 for partial cohort)
Returns¶
ExpressionProxy Cumulative survival probabilities for each period
Raises¶
ValueError
If both rate_timing and a non-default start_at are specified,
or if rate_timing has an invalid value
RuntimeError
If the column is not part of an ActuarialFrame context
Examples¶
Beginning-of-Period Timing (Default)
from gaspatchio_core import ActuarialFrame
data = {
"policy_id": ["P001", "P002"],
"qx": [[0.001, 0.002, 0.003], [0.002, 0.003, 0.004]],
}
af = ActuarialFrame(data)
# Default: rate at t not yet applied
af.pols_if = af.qx.projection.cumulative_survival()
# Or explicitly:
af.pols_if = af.qx.projection.cumulative_survival(
rate_timing="beginning_of_period"
)
print(af.collect())
shape: (2, 3)
┌───────────┬───────────────────────┬────────────────────────┐
│ policy_id ┆ qx ┆ pols_if │
│ --- ┆ --- ┆ --- │
│ str ┆ list[f64] ┆ list[f64] │
╞═══════════╪═══════════════════════╪════════════════════════╡
│ P001 ┆ [0.001, 0.002, 0.003] ┆ [1.0, 0.999, 0.997002] │
│ P002 ┆ [0.002, 0.003, 0.004] ┆ [1.0, 0.998, 0.995006] │
└───────────┴───────────────────────┴────────────────────────┘
End-of-Period Timing (Excel-Style)
from gaspatchio_core import ActuarialFrame
data = {
"qx": [[0.001, 0.002, 0.003]],
}
af = ActuarialFrame(data)
# Excel-style: rate at t has been applied
af.tpx = af.qx.projection.cumulative_survival(rate_timing="end_of_period")
print(af.collect())
shape: (1, 2)
┌───────────────────────┬─────────────────────────────┐
│ qx ┆ tpx │
│ --- ┆ --- │
│ list[f64] ┆ list[f64] │
╞═══════════════════════╪═════════════════════════════╡
│ [0.001, 0.002, 0.003] ┆ [0.999, 0.997002, 0.994011] │
└───────────────────────┴─────────────────────────────┘
Custom Initial Value (Partial Cohort)
from gaspatchio_core import ActuarialFrame
data = {
"qx": [[0.001, 0.002, 0.003]],
}
af = ActuarialFrame(data)
# 95% survived underwriting - use start_at for custom values
af.pols_if = af.qx.projection.cumulative_survival(start_at=0.95)
print(af.collect())
shape: (1, 2)
┌───────────────────────┬─────────────────────────┐
│ qx ┆ pols_if │
│ --- ┆ --- │
│ list[f64] ┆ list[f64] │
╞═══════════════════════╪═════════════════════════╡
│ [0.001, 0.002, 0.003] ┆ [0.95, 0.999, 0.997002] │
└───────────────────────┴─────────────────────────┘
next_period(fill_value=0.0)
¶
Get value from next period (t+1).
Equivalent to shifting forward one period. Less common than
previous_period() but useful for certain actuarial calculations
requiring forward-looking values.
For list columns, shifts values within each list. For scalar columns,
shifts across rows (use .over() for grouping).
When to use
- Forward-Looking Calculations: Access next period values for calculations that require looking ahead in the projection timeline.
- Period-Over-Period Growth: Calculate growth rates or changes by comparing current values to next period values.
- Validation Checks: Verify that projected values follow expected patterns by comparing current and next period results.
- Timing Adjustments: Reference future period values when modeling payment or benefit timing that leads the valuation period.
Parameters¶
fill_value : scalar, optional Value to use for last period where no next value exists. Default is 0.
Returns¶
ExpressionProxy Expression with values shifted from next period
Examples¶
Basic Usage: Next Period Values
from gaspatchio_core import ActuarialFrame
data = {"interest_rate": [[0.05, 0.06, 0.07]]}
af = ActuarialFrame(data)
af.rate_next = af.interest_rate.projection.next_period()
print(af.collect())
shape: (1, 2)
┌────────────────────┬───────────────────┐
│ interest_rate ┆ rate_next │
│ --- ┆ --- │
│ list[f64] ┆ list[f64] │
╞════════════════════╪═══════════════════╡
│ [0.05, 0.06, 0.07] ┆ [0.06, 0.07, 0.0] │
└────────────────────┴───────────────────┘
Forward-Looking Calculation Example
from gaspatchio_core import ActuarialFrame
data = {"cashflow": [[1000, 1100, 1200]]}
af = ActuarialFrame(data)
# Compare current period to next period
af.cf_next = af.cashflow.projection.next_period()
af.cf_growth = af.cf_next - af.cashflow
print(af.collect())
shape: (1, 3)
┌──────────────────┬─────────────────┬──────────────────┐
│ cashflow ┆ cf_next ┆ cf_growth │
│ --- ┆ --- ┆ --- │
│ list[i64] ┆ list[i64] ┆ list[i64] │
╞══════════════════╪═════════════════╪══════════════════╡
│ [1000, 1100, ...] ┆ [1100, 1200, 0] ┆ [100, 100, -...] │
└──────────────────┴─────────────────┴──────────────────┘
See Also¶
previous_period : Get value from previous period (t-1) at_period : Get value at arbitrary period offset
previous_period(fill_value=0.0)
¶
Get value from previous period (t-1).
Equivalent to shifting back one period. Most common case for actuarial projections when referencing prior period values.
For list columns, shifts values within each list. For scalar columns,
shifts across rows (use .over() for grouping).
When to use
- Inforce Rollforward: Calculate beginning-of-period inforce values using ending inforce from the previous period in life insurance models.
- Reserve Calculations: Access prior period reserves for reserve rollforward formulas and cash flow testing.
- Period Comparisons: Compare current period values against previous period for variance analysis and experience studies.
- Dependent Calculations: Reference lagged values in formulas where current period depends on prior period results.
Parameters¶
fill_value : scalar, optional Value to use for first period where no previous value exists. Default is 0.
Returns¶
ExpressionProxy Expression with values shifted from previous period
Examples¶
Basic Usage: Previous Period Values
from gaspatchio_core import ActuarialFrame
data = {"pols_death": [[10, 15, 20]]}
af = ActuarialFrame(data)
af.pols_death_prev = af.pols_death.projection.previous_period()
print(af.collect())
shape: (1, 2)
┌──────────────┬──────────────────┐
│ pols_death ┆ pols_death_prev │
│ --- ┆ --- │
│ list[i64] ┆ list[i64] │
╞══════════════╪══════════════════╡
│ [10, 15, 20] ┆ [0, 10, 15] │
└──────────────┴──────────────────┘
Custom Fill Value: Reserve Calculations
from gaspatchio_core import ActuarialFrame
data = {"reserve": [[1000, 1100, 1200]]}
af = ActuarialFrame(data)
# Use None to get null for missing values
af.reserve_prev = af.reserve.projection.previous_period(fill_value=None)
print(af.collect())
shape: (1, 2)
┌──────────────────┬──────────────────┐
│ reserve ┆ reserve_prev │
│ --- ┆ --- │
│ list[i64] ┆ list[i64] │
╞══════════════════╪══════════════════╡
│ [1000, 1100, ...] ┆ [null, 1000, ...] │
└──────────────────┴──────────────────┘
Actuarial Formula: Inforce Rollforward
from gaspatchio_core import ActuarialFrame
data = {
"pols_if_after_death": [[1000, 990, 975]],
"pols_lapse": [[5, 8, 10]],
}
af = ActuarialFrame(data)
# Calculate beginning-of-period inforce using previous period values
# pols_if_bop(t) = pols_if_after_death(t-1) - pols_lapse(t-1)
af.pols_if_prev = af.pols_if_after_death.projection.previous_period(
fill_value=1000
)
af.pols_lapse_prev = af.pols_lapse.projection.previous_period()
af.pols_if_bop = af.pols_if_prev - af.pols_lapse_prev
print(af.collect())
shape: (1, 4)
┌─────────────────────┬─────────────┬────────────────┬─────────────┐
│ pols_if_after_death ┆ pols_lapse ┆ pols_if_prev ┆ pols_if_bop │
│ --- ┆ --- ┆ --- ┆ --- │
│ list[i64] ┆ list[i64] ┆ list[i64] ┆ list[i64] │
╞═════════════════════╪═════════════╪════════════════╪═════════════╡
│ [1000, 990, 975] ┆ [5, 8, 10] ┆ [1000, 1000... ┆ [1000, 995..│
└─────────────────────┴─────────────┴────────────────┴─────────────┘
See Also¶
next_period : Get value from next period (t+1) at_period : Get value at arbitrary period offset
prospective_value(discount_rate=None, discount_factor=None, *, timing='end_of_period')
¶
Calculate prospective (present) value of future cashflows from each time t.
Computes the present value of all future cashflows from each projection period onwards, using backward recursion: PV(t) = CF(t) + PV(t+1) * v(t).
This is the standard actuarial "prospective policy value" calculation, essential for reserve valuations, embedded value projections, profit testing, and asset adequacy testing. Replaces complex Polars list operations with a clean, actuarial-focused API.
When to use
- Reserve Calculations: Compute present value of future benefits less premiums for statutory and GAAP reserve valuations.
- Embedded Value: Calculate present value of future profits for embedded value and value of in-force business metrics.
- Profit Testing: Project present value of cashflows at each duration for pricing validation and profitability analysis.
- Asset Adequacy: Test sufficiency of assets to cover future liabilities under various interest rate scenarios.
Parameters¶
discount_rate : float or ExpressionProxy or ColumnProxy, optional Per-period discount rate for discounting future cashflows:
- Scalar float: Constant rate for all periods (e.g., 0.05 for 5%)
- List column: Per-period rates that may vary over time
Cannot be specified together with `discount_factor`.
discount_factor : ExpressionProxy or ColumnProxy, optional Pre-computed discount factors (v^t values). Use when you have yield curve or scenario-specific discount factors already calculated.
Cannot be specified together with `discount_rate`.
timing : {"beginning_of_period", "end_of_period"}, default "end_of_period" When cashflows occur within each period:
- ``"end_of_period"``: Cashflow at t paid at end of period (benefits)
- ``"beginning_of_period"``: Cashflow at t paid at start (premiums)
Returns¶
ExpressionProxy Present value of future cashflows at each projection period
Raises¶
ValueError
If both discount_rate and discount_factor are specified,
or if neither is specified
Examples¶
Death Benefit PV with Constant Discount Rate
from gaspatchio_core import ActuarialFrame
data = {
"death_benefit": [[100.0, 100.0, 100.0]],
}
af = ActuarialFrame(data)
# Calculate prospective value at 5% discount rate
af.pv_benefits = af.death_benefit.projection.prospective_value(
discount_rate=0.05
)
print(af.collect())
shape: (1, 2)
┌────────────────────┬─────────────────────────────┐
│ death_benefit ┆ pv_benefits │
│ --- ┆ --- │
│ list[f64] ┆ list[f64] │
╞════════════════════╪═════════════════════════════╡
│ [100.0, 100.0, ... ┆ [285.94, 195.24, 100.0] │
└────────────────────┴─────────────────────────────┘
Premium PV with Time-Varying Rates
from gaspatchio_core import ActuarialFrame
data = {
"premium": [[1000.0, 1000.0, 1000.0]],
"disc_rate": [[0.04, 0.05, 0.06]],
}
af = ActuarialFrame(data)
af.pv_premiums = af.premium.projection.prospective_value(
discount_rate=af.disc_rate,
timing="beginning_of_period"
)
print(af.collect())
With Pre-Computed Discount Factors
from gaspatchio_core import ActuarialFrame
data = {
"benefit": [[100.0, 100.0, 100.0]],
"v_t": [[1.0, 0.952381, 0.907029]], # 5% discount factors
}
af = ActuarialFrame(data)
af.pv = af.benefit.projection.prospective_value(discount_factor=af.v_t)
print(af.collect())
Notes¶
Implementation Details:
The method internally performs:
- Compute discounted cashflows: CF(t) * v(t)
- Fill NaN values with 0 (handles cashflows beyond policy term)
- Apply reverse -> cumsum -> reverse pattern to get "sum from t to end"
- Adjust for timing convention
Replaces Ugly Pattern:
This method replaces verbose Polars list manipulation. The old pattern required 6+ lines of Polars list operations (reverse, cumsum, reverse), while the new API is a single clean method call.
See Also¶
cumulative_survival : Calculate cumulative survival probabilities previous_period : Access prior period values for reserve rollforward
with_period(period, value)
¶
Override value at a specific period (zero-indexed).
Creates a modified version of a list column with a specific element set to a new value. Essential for modeling planned policy changes, premium holidays, benefit adjustments, and other known discontinuities in actuarial projections.
This method only works with list columns. For scalar columns, use conditional
logic with .when() and .then().
When to use
- Premium Holidays: Model scheduled breaks in premium payments, such as waiver of premium periods or contractual payment holidays.
- Benefit Changes: Implement known benefit adjustments at specific durations, like step-up death benefits or maturity bonuses.
- Policy Events: Model surrender charge schedules, conversion options, or guaranteed insurability riders that activate at specific times.
- Assumption Overrides: Apply one-time adjustments to mortality rates, lapse rates, or expenses for specific policy anniversaries.
Parameters¶
period : int Zero-based index to modify. Negative indices supported (-1 = last period). value : float or str Value to set at that period
Returns¶
Modified list with value changed at specified period
Raises¶
RuntimeError: If proxy not associated with an ActuarialFrame
ValueError: If period is out of bounds for the list
Examples¶
Vector Example: Premium Holiday
from gaspatchio_core import ActuarialFrame
data = {"premium": [[1000, 1000, 1000]]}
af = ActuarialFrame(data)
af.premium_adj = af.premium.projection.with_period(1, value=0)
print(af.collect())
shape: (1, 2)
┌────────────────────┬─────────────────┐
│ premium ┆ premium_adj │
│ --- ┆ --- │
│ list[i64] ┆ list[i64] │
╞════════════════════╪═════════════════╡
│ [1000, 1000, 1000] ┆ [1000, 0, 1000] │
└────────────────────┴─────────────────┘
Vector Example: Negative Index (Last Period)
from gaspatchio_core import ActuarialFrame
data = {"benefit": [[1000, 1000, 1000]]}
af = ActuarialFrame(data)
af.benefit_adj = af.benefit.projection.with_period(-1, value=5000)
print(af.collect())
shape: (1, 2)
┌────────────────────┬────────────────────┐
│ benefit ┆ benefit_adj │
│ --- ┆ --- │
│ list[i64] ┆ list[i64] │
╞════════════════════╪════════════════════╡
│ [1000, 1000, 1000] ┆ [1000, 1000, 5000] │
└────────────────────┴────────────────────┘
Vector Example: Benefit Increase
from gaspatchio_core import ActuarialFrame
data = {"face_amount": [[100000, 100000, 100000]]}
af = ActuarialFrame(data)
af.face_adj = af.face_amount.projection.with_period(1, value=150000)
print(af.collect())
shape: (1, 2)
┌──────────────────────────┬──────────────────────────┐
│ face_amount ┆ face_adj │
│ --- ┆ --- │
│ list[i64] ┆ list[i64] │
╞══════════════════════════╪══════════════════════════╡
│ [100000, 100000, 100000] ┆ [100000, 150000, 100000] │
└──────────────────────────┴──────────────────────────┘
with_periods(updates)
¶
Override values at multiple specific periods.
Creates a modified version of a list column with multiple elements changed
at once. More efficient and readable than chaining multiple with_period()
calls. Essential for modeling complex benefit schedules, premium patterns,
and assumption variations across policy durations.
When to use
- Benefit Schedules: Model policies with multiple benefit changes, such as increasing term insurance or scheduled death benefit steps.
- Premium Patterns: Implement complex premium schedules with multiple holidays, increases, or decreases at known policy anniversaries.
- Surrender Charges: Define surrender charge schedules that decrease over time or change at specific durations.
- Assumption Testing: Apply multiple one-time adjustments to test sensitivity to assumption changes at different policy durations.
Parameters¶
updates : dict[int, int | float | str] Dictionary mapping period indices (zero-based) to new values. Negative indices are supported (-1 = last period).
Returns¶
Modified list with values changed at specified periods
Raises¶
RuntimeError: If proxy not associated with an ActuarialFrame
ValueError: If any period is out of bounds for the list
Examples¶
Vector Example: Multiple Premium Holidays
from gaspatchio_core import ActuarialFrame
data = {"premium": [[500, 500, 500]]}
af = ActuarialFrame(data)
af.premium_adj = af.premium.projection.with_periods({0: 0, 2: 0})
print(af.collect())
shape: (1, 2)
┌─────────────────┬─────────────┐
│ premium ┆ premium_adj │
│ --- ┆ --- │
│ list[i64] ┆ list[i64] │
╞═════════════════╪═════════════╡
│ [500, 500, 500] ┆ [0, 500, 0] │
└─────────────────┴─────────────┘
Vector Example: Benefit Schedule
from gaspatchio_core import ActuarialFrame
data = {"benefit": [[1000, 1000, 1000]]}
af = ActuarialFrame(data)
af.benefit_adj = af.benefit.projection.with_periods({0: 1500, -1: 5000})
print(af.collect())
shape: (1, 2)
┌────────────────────┬────────────────────┐
│ benefit ┆ benefit_adj │
│ --- ┆ --- │
│ list[i64] ┆ list[i64] │
╞════════════════════╪════════════════════╡
│ [1000, 1000, 1000] ┆ [1500, 1000, 5000] │
└────────────────────┴────────────────────┘