Date API¶
Frame-Level Operations¶
gaspatchio_core.accessors.date.DateFrameAccessor
¶
Bases: BaseFrameAccessor
Provides date-related methods applicable to the entire ActuarialFrame.
Accessed via .date on an ActuarialFrame instance,
e.g., af.date.
This accessor allows for complex date manipulations at the frame level, such as generating timelines for projections or adding durations to multiple date columns simultaneously. It integrates with Polars expressions for optimized performance.
add_duration(date_col, duration_str, new_col_name=None)
¶
Adds a duration string (e.g., '1Y', '3M', '-7d') to a date column.
This function leverages Polars' powerful duration arithmetic to efficiently
modify dates within the ActuarialFrame. It can create a new column with
the resulting dates or modify an existing column if new_col_name is not
provided and date_col is a string name.
When to use
- Date Arithmetic: Use this method to shift dates by a fixed duration, such as calculating a policy anniversary, determining a future maturity date, or finding a past event date. It's particularly useful for batch operations on an entire column of dates.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
date_col
|
IntoExprColumn
|
The column containing the dates to add the duration to. |
required |
duration_str
|
str
|
The duration string in Polars format (e.g., "1Y6M", "-3d12h"). |
required |
new_col_name
|
str | None
|
The name for the new column containing the resulting dates. If None, modifies the original column (if it's a string name). |
None
|
Returns:
| Type | Description |
|---|---|
ActuarialFrame
|
A new ActuarialFrame with the added/modified column. |
Raises:
| Type | Description |
|---|---|
ValueError
|
If date_col is not a valid column/expression or if modification is attempted without providing a string name for date_col. |
ComputeError
|
If the duration addition fails (e.g., invalid duration string, incompatible date types). |
Examples:
import datetime
from gaspatchio_core import ActuarialFrame
data = {
"event_date": [datetime.date(2023, 1, 15), datetime.date(2023, 6, 30)],
"term_months": [6, 12]
}
af = ActuarialFrame(data)
af_plus_1y = af.date.add_duration(af.event_date, "1y", new_col_name="event_plus_1y")
print(af_plus_1y.collect())
shape: (2, 3)
┌────────────┬─────────────┬───────────────┐
│ event_date ┆ term_months ┆ event_plus_1y │
│ --- ┆ --- ┆ --- │
│ date ┆ i64 ┆ date │
╞════════════╪═════════════╪═══════════════╡
│ 2023-01-15 ┆ 6 ┆ 2024-01-15 │
│ 2023-06-30 ┆ 12 ┆ 2024-06-30 │
└────────────┴─────────────┴───────────────┘
import datetime
from gaspatchio_core import ActuarialFrame
data = {
"event_date": [datetime.date(2023, 1, 15), datetime.date(2023, 6, 30)],
"term_months": [6, 12]
}
af = ActuarialFrame(data)
af_minus_3m = af.date.add_duration(af.event_date, "-3mo", new_col_name="event_minus_3m")
print(af_minus_3m.collect())
shape: (2, 3)
┌────────────┬─────────────┬────────────────┐
│ event_date ┆ term_months ┆ event_minus_3m │
│ --- ┆ --- ┆ --- │
│ date ┆ i64 ┆ date │
╞════════════╪═════════════╪════════════════╡
│ 2023-01-15 ┆ 6 ┆ 2022-10-15 │
│ 2023-06-30 ┆ 12 ┆ 2023-03-30 │
└────────────┴─────────────┴────────────────┘
create_projection_timeline(valuation_date, projection_end_type='maximum_age', projection_end_value=100, issue_age_column='issue_age', projection_frequency='monthly', projection_start_offset_months=0, store_start_date=True, store_end_date=True, output_column='proj_dates')
¶
Creates a projection timeline for actuarial calculations within the frame.
This powerful method generates a series of projection dates for each row in the ActuarialFrame based on various actuarial projection methodologies. It can handle projections to a maximum age, for a fixed term (in years or months), or until a specific fixed date. The resulting timeline is added as a new list column to the frame, which can then be exploded for detailed cashflow modeling or analysis.
When to use
- Actuarial Projections: This is a cornerstone function for actuarial modeling. Use it to:
- Generate monthly, quarterly, semi-annual, or annual projection dates.
- Model policies projecting to a maximum age (e.g., whole life insurance).
- Model policies with fixed terms (e.g., term life insurance, annuities certain).
- Align projections to specific calendar dates.
- Prepare data for per-period calculations like reserves, premiums, or benefits.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
valuation_date
|
date
|
The valuation date from which to project |
required |
projection_end_type
|
Literal['maximum_age', 'term_years', 'term_months', 'fixed_date']
|
How to determine the end of the projection: - "maximum_age": Project until the policyholder reaches the maximum age - "term_years": Project for a fixed number of years - "term_months": Project for a fixed number of months - "fixed_date": Project until a specific calendar date |
'maximum_age'
|
projection_end_value
|
Union[int, date]
|
The value corresponding to the projection_end_type: - For "maximum_age": The maximum age (e.g., 100) - For "term_years": The number of years to project - For "term_months": The number of months to project - For "fixed_date": A datetime.date object |
100
|
issue_age_column
|
str
|
The column containing the issue age (needed for "maximum_age") |
'issue_age'
|
projection_frequency
|
Literal['monthly', 'quarterly', 'semi-annual', 'annual']
|
The frequency of projection points |
'monthly'
|
projection_start_offset_months
|
int
|
Months to offset the start date from valuation |
0
|
store_start_date
|
bool
|
Whether to store the projection start date |
True
|
store_end_date
|
bool
|
Whether to store the projection end date |
True
|
output_column
|
str
|
The name of the column to store the projection dates |
'proj_dates'
|
Returns:
| Type | Description |
|---|---|
ActuarialFrame
|
The updated ActuarialFrame instance ( |
Examples:
```python no_output_check import datetime from gaspatchio_core import ActuarialFrame data = { "policy_id": ["A1", "B2"], "issue_age": [30, 45], # Needed for maximum_age projection "policy_term_years": [0, 10] # Example, not directly used by max_age } af = ActuarialFrame(data) val_date = datetime.date(2024, 1, 1)
Example 1: Project to maximum age of 65, monthly¶
af_max_age = af.date.create_projection_timeline( valuation_date=val_date, projection_end_type="maximum_age", projection_end_value=32, # Max age of 32 for policy A1 (30+2), 47 for B2 (45+2) issue_age_column="issue_age", projection_frequency="annual", # Simplified for example output_column="projection_dates_max_age" )
Example 2: Project for a fixed term of 2 years, quarterly¶
af_fixed_term = af.date.create_projection_timeline( valuation_date=val_date, projection_end_type="term_years", projection_end_value=2, projection_frequency="quarterly", output_column="projection_dates_fixed_term" )
Example 3: Project to a fixed date, annually¶
fixed_end_date = datetime.date(2025, 12, 31) af_fixed_date = af.date.create_projection_timeline( valuation_date=val_date, projection_end_type="fixed_date", projection_end_value=fixed_end_date, projection_frequency="annual", output_column="projection_dates_fixed_date" ) ```
create_timeline(start_col, end_col, freq='1d', new_col_name='timeline_date', closed='left')
¶
Creates timeline columns based on start and end dates.
Generates a list of dates for each row based on its start and end date, using the specified frequency. The result is exploded to create a longer DataFrame where each original row is repeated for each date in its timeline.
When to use
- Period-to-Event Transformation: This method is useful when you need to transform row-per-period data (where each row has a start and end date) into row-per-event data (where each row represents a specific point in time, like a month-end). For example, to calculate monthly exposures from policy start/end dates.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
start_col
|
IntoExprColumn
|
Column or expression for the start date of the interval. |
required |
end_col
|
IntoExprColumn
|
Column or expression for the end date of the interval. |
required |
freq
|
str
|
The frequency of the timeline (e.g., "1M", "1Y", "1d").
Passed to |
'1d'
|
new_col_name
|
str
|
Name for the new column containing the generated timeline dates. Defaults to "timeline_date". |
'timeline_date'
|
closed
|
str
|
Which side of the interval is closed ("left", "right", "both", "none").
Passed to |
'left'
|
Returns:
| Type | Description |
|---|---|
ActuarialFrame
|
A new ActuarialFrame instance with the original data expanded |
ActuarialFrame
|
by the generated timeline dates. |
Raises:
| Type | Description |
|---|---|
ColumnNotFoundError
|
If start_col or end_col cannot be resolved. |
ComputeError
|
If date range generation fails (e.g., invalid freq, incompatible date types). |
Examples:
import datetime
from gaspatchio_core import ActuarialFrame
data = {
"policy_id": [1, 2],
"start_date": [datetime.date(2023, 1, 1), datetime.date(2023, 2, 15)],
"end_date": [datetime.date(2023, 3, 1), datetime.date(2023, 4, 15)],
}
af = ActuarialFrame(data)
timeline_af = af.date.create_timeline(af.start_date, af.end_date, freq="1mo", new_col_name="month_end")
print(timeline_af.collect())
shape: (4, 4)
┌───────────┬────────────┬────────────┬────────────┐
│ policy_id ┆ start_date ┆ end_date ┆ month_end │
│ --- ┆ --- ┆ --- ┆ --- │
│ i64 ┆ date ┆ date ┆ date │
╞═══════════╪════════════╪════════════╪════════════╡
│ 1 ┆ 2023-01-01 ┆ 2023-03-01 ┆ 2023-01-01 │
│ 1 ┆ 2023-01-01 ┆ 2023-03-01 ┆ 2023-02-01 │
│ 2 ┆ 2023-02-15 ┆ 2023-04-15 ┆ 2023-02-15 │
│ 2 ┆ 2023-02-15 ┆ 2023-04-15 ┆ 2023-03-15 │
└───────────┴────────────┴────────────┴────────────┘
Column-Level Operations¶
gaspatchio_core.accessors.date.DateColumnAccessor
¶
Bases: BaseColumnAccessor
Provides date-related methods for ColumnProxy or ExpressionProxy objects.
Accessed via .date on a column or expression,
e.g., af["my_date_col"].date.
This accessor offers convenient methods to manipulate and extract information from date/datetime columns within Polars expressions.
to_period(freq='M')
¶
Converts a date/datetime column to a period representation (e.g., year-month).
This is useful for grouping or aggregating data by specific time periods like month, quarter, or year. It truncates the date to the beginning of the specified period.
When to use
- Period Aggregation: Use this to aggregate daily or weekly data into monthly, quarterly, or annual summaries.
- Time Series Features: For creating features for time series models based on periods.
- Date Alignment: When you need to align dates to a common period start (e.g., all dates in January
2023 become 2023-01-01 if
freq="M").
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
freq
|
str
|
The frequency string for period conversion (e.g., "M", "Q", "Y").
See Polars documentation for |
'M'
|
Returns:
| Type | Description |
|---|---|
ExpressionProxy
|
An |
ExpressionProxy
|
specified period. |
Examples:
import datetime
import polars as pl
from gaspatchio_core import ActuarialFrame
data = {
"event_timestamp": [
datetime.datetime(2023, 1, 15, 10, 30, 0),
datetime.datetime(2023, 1, 20, 14, 0, 0),
datetime.datetime(2023, 2, 5, 8, 0, 0),
]
}
af = ActuarialFrame(data)
af.month = af.event_timestamp.dt.truncate("1mo").cast(pl.Date)
print(af.collect())
shape: (3, 2)
┌─────────────────────┬────────────┐
│ event_timestamp ┆ month │
│ --- ┆ --- │
│ datetime[μs] ┆ date │
╞═════════════════════╪════════════╡
│ 2023-01-15 10:30:00 ┆ 2023-01-01 │
│ 2023-01-20 14:00:00 ┆ 2023-01-01 │
│ 2023-02-05 08:00:00 ┆ 2023-02-01 │
└─────────────────────┴────────────┘
import datetime
import polars as pl
from gaspatchio_core import ActuarialFrame
data = {
"event_timestamp": [
datetime.datetime(2023, 1, 15, 10, 30, 0),
datetime.datetime(2023, 1, 20, 14, 0, 0),
datetime.datetime(2023, 2, 5, 8, 0, 0),
]
}
af = ActuarialFrame(data)
af.year = af.event_timestamp.dt.truncate("1y").cast(pl.Date)
print(af.collect())
shape: (3, 2)
┌─────────────────────┬────────────┐
│ event_timestamp ┆ year │
│ --- ┆ --- │
│ datetime[μs] ┆ date │
╞═════════════════════╪════════════╡
│ 2023-01-15 10:30:00 ┆ 2023-01-01 │
│ 2023-01-20 14:00:00 ┆ 2023-01-01 │
│ 2023-02-05 08:00:00 ┆ 2023-01-01 │
└─────────────────────┴────────────┘
Datetime Namespace¶
For Polars-native datetime operations (year, month, day extraction), use the .dt namespace directly:
gaspatchio_core.column.namespaces.dt_proxy.DtNamespaceProxy
¶
A proxy for Polars datetime (dt) namespace operations.
Enables type-hinting and IDE intellisense for ActuarialFrame datetime
manipulations.
This proxy intercepts calls to datetime methods, retrieves the underlying
Polars expression from its parent proxy (either a ColumnProxy or
ExpressionProxy), applies the datetime operation, and then wraps the
resulting Polars expression back into an ExpressionProxy.
day()
¶
Extract the day number of the month (1-31) from a date/datetime expression.
This function isolates the day component from a date or datetime, returning it as an integer (e.g., 15 for the 15th of the month). It works for both individual dates and lists of dates.
When to use
Extracting the day of the month can be useful in actuarial contexts for:
- Specific Date Checks: Identifying events occurring on particular days (e.g., end-of-month processing).
- Intra-month Analysis: Analyzing patterns within a month, though less common than month or year analysis.
- Data Validation: Ensuring dates fall within expected day ranges for specific calculations.
Examples¶
Scalar example::
import polars as pl
from gaspatchio_core import ActuarialFrame
af = ActuarialFrame(
{"d": pl.Series(["2023-06-05", "2023-06-15"]).str.to_date()}
)
print(af.select(af.d.dt.day().alias("day")).collect())
shape: (2, 1)
┌─────┐
│ day │
│ --- │
│ i8 │
╞═════╡
│ 5 │
│ 15 │
└─────┘
Vector (list) example - loss-event days::
import datetime
import polars as pl
from gaspatchio_core import ActuarialFrame
data = {
"policy_id": ["E005", "F006"],
"loss_event_dates": [
[datetime.date(2023, 6, 5), datetime.date(2023, 6, 15)],
[datetime.date(2024, 2, 1), datetime.date(2024, 2, 29)],
],
}
af = ActuarialFrame(data).with_columns(
pl.col("loss_event_dates").cast(pl.List(pl.Date))
)
days_expr = af.loss_event_dates.dt.day()
print(af.select("policy_id", days_expr.alias("event_days")).collect())
shape: (2, 2)
┌───────────┬────────────┐
│ literal ┆ event_days │
│ --- ┆ --- │
│ str ┆ list[i8] │
╞═══════════╪════════════╡
│ policy_id ┆ [5, 15] │
│ policy_id ┆ [1, 29] │
└───────────┴────────────┘
month()
¶
Extract the month number (1-12) from a date or datetime expression.
This function allows you to isolate the month component from a series of dates or datetimes. The result is an integer representing the month, where January is 1 and December is 12.
When to use
In actuarial modeling, extracting the month from dates is crucial for various analyses. For instance, you might use this to:
- Analyze seasonality in claims (e.g., identifying if certain types of claims are more frequent in specific months).
- Group policies by their issue month for cohort analysis or to study underwriting patterns.
- Determine premium due dates or benefit payment schedules that occur on a monthly basis.
- Calculate fractional year components for financial calculations.
Examples¶
Scalar example::
import polars as pl
from gaspatchio_core import ActuarialFrame
af = ActuarialFrame(
{
"d": pl.Series(["2022-01-01", "2022-02-01", "2022-03-01"]).str.to_date(
"%Y-%m-%d"
)
}
)
print(af.select(af.d.dt.month().alias("m")).collect())
shape: (3, 1)
┌─────┐
│ m │
│ --- │
│ i8 │
╞═════╡
│ 1 │
│ 2 │
│ 3 │
└─────┘
Vector (list) example - claim-lodgement months::
import datetime
import polars as pl
from gaspatchio_core import ActuarialFrame
data = {
"policy_id": ["C003", "D004"],
"claim_lodgement_dates": [
[datetime.date(2022, 3, 10), datetime.date(2022, 4, 5)],
[datetime.date(2023, 1, 20), datetime.date(2023, 11, 30)],
],
}
af = ActuarialFrame(data).with_columns(
pl.col("claim_lodgement_dates").cast(pl.List(pl.Date))
)
months_expr = af.claim_lodgement_dates.dt.month()
result = af.select(
pl.col("policy_id"), months_expr.alias("lodgement_months")
)
print(result.collect())
shape: (2, 2)
┌───────────┬──────────────────┐
│ policy_id ┆ lodgement_months │
│ --- ┆ --- │
│ str ┆ list[i8] │
╞═══════════╪══════════════════╡
│ C003 ┆ [3, 4] │
│ D004 ┆ [1, 11] │
└───────────┴──────────────────┘
year()
¶
Extract the year from the underlying datetime expression.
This function isolates the year component from a date or datetime,
returning it as an integer (e.g., 2023). It is applicable to both
single date values and lists of dates within your ActuarialFrame.
When to use
Extracting the year is fundamental in actuarial analysis for:
- Valuation and Reporting: Determining the calendar year for financial reporting or regulatory submissions.
- Experience Studies: Grouping data by calendar year of event (e.g., year of claim, year of lapse) to analyze trends.
- Cohort Analysis: Defining cohorts based on the year of policy issue or birth year.
- Projection Models: Calculating durations or projecting cash flows based on calendar years.
Examples¶
Scalar example (single-date column)::
import polars as pl
from gaspatchio_core import ActuarialFrame
data = {
"dates": pl.Series(["2020-01-15", "2021-07-20"]).str.to_date(
format="%Y-%m-%d"
)
}
af = ActuarialFrame(data)
year_expr = af.dates.dt.year()
print(af.select(year_expr.alias("year")).collect())
shape: (2, 1)
┌──────┐
│ year │
│ --- │
│ i32 │
╞══════╡
│ 2020 │
│ 2021 │
└──────┘
Vector example (list-of-dates per policy)::
import datetime
import polars as pl
from gaspatchio_core import ActuarialFrame
data_vec = {
"policy_id": ["A001", "B002"],
"policy_event_dates": [
[datetime.date(2019, 12, 1), datetime.date(2020, 1, 20)],
[
datetime.date(2021, 5, 10),
datetime.date(2021, 8, 15),
datetime.date(2022, 2, 25),
],
],
}
af_vec = ActuarialFrame(data_vec)
af_vec = af_vec.with_columns(
pl.col("policy_event_dates").cast(pl.List(pl.Date))
)
years_expr = af_vec.policy_event_dates.dt.year()
result = af_vec.select(
pl.col("policy_id"), years_expr.alias("event_years")
)
print(result.collect())
shape: (2, 2)
┌───────────┬────────────────────┐
│ policy_id ┆ event_years │
│ --- ┆ --- │
│ str ┆ list[i32] │
╞═══════════╪════════════════════╡
│ A001 ┆ [2019, 2020] │
│ B002 ┆ [2021, 2021, 2022] │
└───────────┴────────────────────┘