Skip to content

Conditionals API

when

Start a conditional expression chain.

Excel-style IF() function with method chaining for multiple conditions. Provides intuitive if/elif/else logic for actuarial calculations. Automatically handles both scalar columns and list columns (projections) with proper broadcasting.

Supported in both debug and optimize modes - conditionals with list columns work seamlessly in either execution mode.

When to use

  • Age-Based Pricing: Apply different premium rates, mortality factors, or underwriting classes based on policyholder age brackets.
  • Maturity Events: Identify when policies mature by comparing projection month against policy term, zeroing cash flows after maturity.
  • Premium Holidays: Suspend premium collection for specific months or conditions, such as grace periods or payment holidays.
  • Commission Schedules: Calculate tiered commission rates based on policy value, product type, or sales channel.
  • Benefit Triggers: Activate guaranteed minimum benefits, death benefits, or surrender values when specific conditions are met.
  • Underwriting Rules: Implement automated underwriting decisions based on sum assured, age, and other risk factors.

Parameters:

Name Type Description Default
condition Any

Boolean expression (e.g., af.age > 65)

required

Returns:

Type Description
ConditionalProxy

ConditionalProxy for chaining .then() and .otherwise()

Examples:

Scalar Example: Age-Based Rate Classification

from gaspatchio_core import ActuarialFrame, when

data = {
    "policy_id": ["P001", "P002", "P003", "P004"],
    "age": [35, 55, 68, 72],
}
af = ActuarialFrame(data)

af.rate_class = when(af.age > 65).then("senior").otherwise("standard")

print(af.collect())
shape: (4, 3)
┌───────────┬─────┬────────────┐
│ policy_id ┆ age ┆ rate_class │
│ ---       ┆ --- ┆ ---        │
│ str       ┆ i64 ┆ str        │
╞═══════════╪═════╪════════════╡
│ P001      ┆ 35  ┆ standard   │
│ P002      ┆ 55  ┆ standard   │
│ P003      ┆ 68  ┆ senior     │
│ P004      ┆ 72  ┆ senior     │
└───────────┴─────┴────────────┘

Vector Example: Maturity Detection with List Broadcasting

from gaspatchio_core import ActuarialFrame, when

data = {
    "policy_id": ["P001", "P002"],
    "month": [
        [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
        [
            0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
            13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24
        ]
    ],
    "policy_term_years": [1, 2],
    "pols_if": [
        [1000, 998, 996, 994, 992, 990, 988, 986, 984, 982, 980, 978, 976],
        [
            1000, 998, 996, 994, 992, 990, 988, 986, 984, 982, 980, 978,
            976, 974, 972, 970, 968, 966, 964, 962, 960, 958, 956, 954, 952
        ]
    ],
}
af = ActuarialFrame(data)

af.pols_maturity = (
    when(af.month == af.policy_term_years * 12)
    .then(af.pols_if)
    .otherwise(0)
)

print(af.collect())
shape: (2, 5)
┌───────────┬──────────────┬──────────┬───────────────┐
│ policy_id ┆ month        ┆ pols_if  ┆ pols_maturity │
│ ---       ┆ ---          ┆ ---      ┆ ---           │
│ str       ┆ list[i64]    ┆ list[... ┆ list[i64]     │
╞═══════════╪══════════════╪══════════╪═══════════════╡
│ P001      ┆ [0, 1, … 12] ┆ [1000... ┆ [0, 0, … 976] │
│ P002      ┆ [0, 1, … 24] ┆ [1000... ┆ [0, 0, … 952] │
└───────────┴──────────────┴──────────┴───────────────┘

List Broadcasting Behavior

When list columns are involved, the framework automatically broadcasts scalar values across all elements in the list. In the maturity example above:

  • af.month is a list column (projection months 0-12 and 0-24)
  • af.policy_term_years * 12 broadcasts the scalar calculation to each month
  • The condition is evaluated element-wise within each list
  • af.pols_if (then value) and 0 (otherwise value) are applied element-wise
  • Result: maturity value appears only at the matching month, zeros elsewhere

ConditionalProxy

Represents an in-progress conditional expression chain.

This class builds up when/then chains and completes them with otherwise(). It automatically handles list vs scalar broadcasting when needed using the explode/re-aggregate pattern.

get_list_broadcasting_metadata()

Get metadata needed for DataFrame-level list broadcasting.

Returns:

Type Description
dict[str, Any]

Dictionary containing:

dict[str, Any]
  • conditions: List of condition expressions
dict[str, Any]
  • values: List of then-value expressions
dict[str, Any]
  • otherwise_expr: The otherwise value expression (if set)
dict[str, Any]
  • list_columns: Set of detected list column names

needs_list_broadcasting()

Check if this conditional requires list broadcasting.

Returns:

Type Description
bool

True if any columns involved are list columns, False otherwise

otherwise(value)

Complete conditional chain with default value.

Finalizes the conditional expression by providing the value to use when none of the preceding conditions evaluate to true. This method is required - a conditional expression cannot be used without calling .otherwise(). Automatically detects and handles list broadcasting for projection calculations.

When to use

  • Default Rate: Provide standard rate when age doesn't match any premium tiers or risk categories.
  • Zero After Event: Set cash flows to zero for all months after maturity, surrender, or death events occur.
  • Fallback Values: Apply baseline commission rates, default mortality assumptions, or standard policy terms when special conditions aren't met.
  • Maintain Status Quo: Keep existing premium, benefit, or reserve values unchanged when update conditions don't apply.

Parameters:

Name Type Description Default
value Any

Default value when no conditions match. Can be a literal value, column reference, or computed expression. For list columns, this value is broadcast element-wise across all list elements.

required

Returns:

Type Description
ExpressionProxy

ExpressionProxy wrapping the complete conditional expression, ready

ExpressionProxy

for assignment to a column.

Examples:

Scalar Example: Underwriting Classification

from gaspatchio_core import ActuarialFrame, when

data = {
    "policy_id": ["P001", "P002", "P003", "P004", "P005", "P006"],
    "age": [25, 42, 55, 68, 73, 45],
    "sum_assured": [100000, 250000, 500000, 150000, 300000, 600000],
}
af = ActuarialFrame(data)

af.underwriting_class = (
    when(af.sum_assured > 500000)
    .then("refer_underwriting")
    .when(af.age > 65)
    .then("senior_standard")
    .when(af.age < 35)
    .then("young_preferred")
    .otherwise("standard")
)

print(af.collect())
shape: (6, 4)
┌───────────┬─────┬─────────────┬────────────────────┐
│ policy_id ┆ age ┆ sum_assured ┆ underwriting_class │
│ ---       ┆ --- ┆ ---         ┆ ---                │
│ str       ┆ i64 ┆ i64         ┆ str                │
╞═══════════╪═════╪═════════════╪════════════════════╡
│ P001      ┆ 25  ┆ 100000      ┆ young_preferred    │
│ P002      ┆ 42  ┆ 250000      ┆ standard           │
│ P003      ┆ 55  ┆ 500000      ┆ standard           │
│ P004      ┆ 68  ┆ 150000      ┆ senior_standard    │
│ P005      ┆ 73  ┆ 300000      ┆ senior_standard    │
│ P006      ┆ 45  ┆ 600000      ┆ refer_underwriting │
└───────────┴─────┴─────────────┴────────────────────┘

List Broadcasting Behavior

The .otherwise() method automatically detects when list columns are involved and applies the default value element-wise. If the otherwise value is a scalar (like 0 or 100.0), it's broadcast to match the length of each list. If the otherwise value is itself a list column, elements are matched one-to-one.

This enables patterns like: - Zeroing cash flows after maturity: .otherwise(0) - Maintaining baseline premiums: .otherwise(af.base_premium) - Default growth rates: .otherwise(0.03) broadcasts to all months

then(value)

Specify value when condition is true.

Defines the result value for when the preceding condition evaluates to true. Must be followed by either another .when() for chained conditions or .otherwise() to complete the expression. Works with scalar values, column references, or computed expressions.

Parameters:

Name Type Description Default
value Any

Value to return when condition matches. Can be a literal value (number, string, etc.), a column reference (af.column_name), or a computed expression (af.premium * 1.1). For list columns, values are applied element-wise with automatic broadcasting.

required

Returns:

Type Description
ConditionalProxy

Self for chaining more .when() or final .otherwise()

Examples:

Scalar Example: Multi-Tier Premium Rates

from gaspatchio_core import ActuarialFrame, when

data = {
    "policy_id": ["P001", "P002", "P003", "P004", "P005"],
    "age": [25, 42, 55, 68, 73],
}
af = ActuarialFrame(data)

af.premium_rate = (
    when(af.age < 35)
    .then(0.0015)
    .when(af.age < 50)
    .then(0.0025)
    .when(af.age < 65)
    .then(0.0040)
    .otherwise(0.0065)
)

print(af.collect())
shape: (5, 3)
┌───────────┬─────┬──────────────┐
│ policy_id ┆ age ┆ premium_rate │
│ ---       ┆ --- ┆ ---          │
│ str       ┆ i64 ┆ f64          │
╞═══════════╪═════╪══════════════╡
│ P001      ┆ 25  ┆ 0.0015       │
│ P002      ┆ 42  ┆ 0.0025       │
│ P003      ┆ 55  ┆ 0.004        │
│ P004      ┆ 68  ┆ 0.0065       │
│ P005      ┆ 73  ┆ 0.0065       │
└───────────┴─────┴──────────────┘

Vector Example: Premium Holiday

from gaspatchio_core import ActuarialFrame, when

data = {
    "policy_id": ["P001"],
    "month": [[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]],
    "premium_holiday_month": [6],
    "base_premium": [100.0],
}
af = ActuarialFrame(data)

af.premium_due = (
    when(af.month == af.premium_holiday_month)
    .then(0.0)
    .otherwise(af.base_premium)
)

print(af.collect())
shape: (1, 5)
┌───────────┬──────────────┬──────────┬─────────────────────────┐
│ policy_id ┆ month        ┆ base...  ┆ premium_due             │
│ ---       ┆ ---          ┆ ---      ┆ ---                     │
│ str       ┆ list[i64]    ┆ f64      ┆ list[f64]               │
╞═══════════╪══════════════╪══════════╪═════════════════════════╡
│ P001      ┆ [0, 1, … 12] ┆ 100.0    ┆ [100.0, 100.0, … 100.0] │
└───────────┴──────────────┴──────────┴─────────────────────────┘

when(condition)

Add another condition (elif behavior).

Parameters:

Name Type Description Default
condition Any

Additional condition expression

required

Returns:

Type Description
ConditionalProxy

Self for chaining .then()