Skip to content

Create options for triangle and pattern formatting #1154

Description

@genedan

Description

Chainladder is used around the world, so we should have a way to adjust the numeric formatting to accommodate various locales.

Chainladder currently does not have a centralized configuration for Triangle formatting. Printing to console vs. printing to a Jupyter notebook will lead to different levels of precision since the former is handled by Pandas whereas the latter is inferred via this function:

def _get_format_str(data: DataFrame) -> str:
"""
Returns a numerical format string based on the magnitude of the mean absolute value of the values in the
supplied DataFrame.
Returns
-------
str
"""
if np.all(np.isnan(data)):
return ""
elif np.nanmean(abs(data)) < 10:
return "{0:,.4f}"
elif np.nanmean(abs(data)) < 1000:
return "{0:,.2f}"
else:
return "{:,.0f}"

For console and terminal output, there is no chainladder-native way to configure the formatting, you need to go through Pandas. See #875

Is your feature request aligned 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.

  1. Move the auto-formatting based on magnitude to the Options class as the default for all output.
  2. Add options to fine-tune numeric formatting.

Examples:

import pandas as pd
import chainladder as cl

data = pd.DataFrame({
    "origin":     ["2020", "2020", "2020", "2021", "2021", "2022"],
    "valuation":  ["2020-12-31", "2021-12-31", "2022-12-31", "2021-12-31", "2022-12-31", "2022-12-31"],
    "values":     [1234567, 2345678, 2456789, 1345678, 2456789, 1456789],
})

tri = cl.Triangle(data, origin="origin", development="valuation", columns="values", cumulative=True)

print(tri)
             12         24         36
2020  1234567.0  2345678.0  2456789.0
2021  1345678.0  2456789.0        NaN
2022  1456789.0        NaN        NaN

Thousands separator, accepts a format string:

cl.options.set_option("display.value_format", "{:,.0f}")

print(tri)
             12         24         36
2020  1,234,567  2,345,678  2,456,789
2021  1,345,678  2,456,789
2022  1,456,789

Swiss-format, accepts a callable:

def swiss_format(x):
      return f"{x:,.0f}".replace(",", "'")

cl.options.set_option("display.value_format", swiss_format)
print(tri)

               12         24         36
  2020  1'234'567  2'345'678  2'456'789
  2021  1'345'678  2'456'789
  2022  1'456'789

Indian format:

def indian_format(x):
    sign = "-" if x < 0 else ""
    int_part = f"{abs(x):.0f}"
    if len(int_part) <= 3:
        grouped = int_part
    else:
        last3, rest = int_part[-3:], int_part[:-3]
        parts = []
        while len(rest) > 2:
            parts.insert(0, rest[-2:])
            rest = rest[:-2]
        if rest:
            parts.insert(0, rest)
        grouped = ",".join(parts) + "," + last3
    return f"{sign}{grouped}"

cl.options.set_option("display.value_format", indian_format)

print(tri)

              12          24          36
2020  12,34,567   23,45,678   24,56,789
2021  13,45,678   24,56,789
2022  14,56,789

Patterns/Link Ratios:

cl.options.set_option("display.pattern_format", "{:.4f}".format)

print(tri.link_ratio)

        12-24   24-36
2020  1.9000  1.0474
2021  1.8257

Do you have any additional supporting notes?

No response

Would you be willing to contribute this ticket?

  • Yes, absolutely!
  • Yes, but I would like some help.
  • No.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Fields

    Priority

    Medium

    Scope

    Both

    Effort

    Medium

    Projects

    Status
    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions