Skip to content

[ENH] Add Triangle.percentage_of_ultimate() method for loss development pattern visualization #598

@Hendrik240298

Description

@Hendrik240298

Description

chainladder-python currently lacks a visualization method for loss development patterns expressed as percentage of ultimate. This visualization shows cumulative losses divided by ultimate losses across development periods, commonly used in actuarial analysis to examine emergence patterns and compare individual origins against average development. Such visualization helps identify unusual development patterns and supports validation of development assumptions.
Overall, this capability can be valuable across actuarial applications where understanding development velocity is important, including loss reserving analysis, pattern validation, and benchmarking development assumptions across different lines of business or portfolios.

Is your feature request at odds with the scope of the package?

  • Yes, absolutely!
  • No, but it's still worth discussing.
  • N/A (this request is not a codebase enhancement).

Describe the solution you'd like, or your current workaround.

Add a percentage_of_ultimate() method to the Triangle class that creates matplotlib visualizations showing:

  • Individual origin development curves (observed data only)
  • Average development pattern with multiple weighting methods (volume, simple, regression, Zehnwirth & Barnett numeric)
  • Configurable display options for origins, averaging methods, and figure sizing

Method signature:

def percentage_of_ultimate(
    self,
    show_by_origin: bool = True,
    show_average_pattern: bool = True,
    show_origin_years_in_legend: bool = True,
    selected_origins: list = None,
    average: str | float = "volume",
    figsize: tuple = (12, 8)
) -> Figure

Mathematical approach:
The method calculates percentage of ultimate as Cumulative Losses / Ultimate Losses for each development period. Ultimate projections are derived using Development() patterns without tail estimation - the calculation extends observed development through the available CDF factors only.

The method supports volume, simple, regression, and Zehnwirth & Barnett numeric averaging methods (e.g., average=1.5) and works exclusively with raw triangles to provide individual origin analysis.

Key limitations:

  • Currently works exclusively with raw triangles (not fitted Development objects or CDF triangles)
  • Requires matplotlib for visualization
  • Most effective for single triangle analysis

Usage example:

import chainladder as cl

# Load triangle and analyze development patterns
raa = cl.load_sample('raa')

# Basic visualization
fig = raa.percentage_of_ultimate()

# Custom analysis with different averaging
fig = raa.percentage_of_ultimate(
    average="simple",
    selected_origins=[1985, 1987, 1990],
    show_average_pattern=True
)

Plots:

Image
Example of percentage of ultimate for RAA data with volume-weighted average pattern (all origins)
Image
Example of percentage of ultimate for quarterly data set showing selected origins

Do you have any additional supporting notes?

Implementation status: Working implementation available.

Technical details:

  • Proposed location: chainladder/core/display.py (follows existing heatmap() pattern)
  • Dependencies: matplotlib (consistent with current visualization methods)
  • Backend compatibility: Works with numpy, sparse, cupy, and dask backends
  • Breaking changes: None - pure addition to Triangle class

Validation:

  • Tested with RAA sample data and quarterly development triangles
  • Integration confirmed with existing chainladder workflow

Metadata

Metadata

Assignees

No one assigned

    Labels

    Effort > Moderate 🐕Mid-sized tasks estimated to take a few days to a few weeks.Impact > Minor 🔷Small, backward compatible change. Treat like a patch release (e.g., 0.5.8 → 0.5.9).

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions