Source code for actuarialpy.contribution

"""Contribution and driver-analysis primitives."""

from __future__ import annotations

import pandas as pd

from actuarialpy.columns import as_list, validate_columns
from actuarialpy.metrics import safe_divide


[docs] def share_of_total(component, total): """Calculate component share of total.""" return safe_divide(component, total)
[docs] def contribution_to_change(component_change, total_change): """Calculate component contribution to a total change.""" return safe_divide(component_change, total_change)
[docs] def top_contributors( df: pd.DataFrame, amount_col: str, *, n: int = 10, ascending: bool = False, by_abs: bool = False, ) -> pd.DataFrame: """Return top contributors by signed or absolute amount.""" validate_columns(df, [amount_col]) result = df.copy() if by_abs: sort_col = "_abs_sort_amount" result[sort_col] = result[amount_col].abs() result = result.sort_values(sort_col, ascending=ascending).drop(columns=sort_col).head(n) else: result = result.sort_values(amount_col, ascending=ascending).head(n) return result.copy()
[docs] def component_contribution( df: pd.DataFrame, *, component_cols, total_col: str | None = None, prefix: str = "share", copy: bool = True, ) -> pd.DataFrame: """Add component share-of-total columns for a set of component columns.""" components = as_list(component_cols) validate_columns(df, components) result = df.copy() if copy else df if total_col is None: total_col = "total_component" result[total_col] = result[components].sum(axis=1) else: validate_columns(result, [total_col]) for col in components: result[f"{col}_{prefix}"] = share_of_total(result[col], result[total_col]) return result