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

Labels

Type

No type

Fields

Priority

Medium

Scope

Both

Effort

Medium

Projects

Relationships

None yet

Development

No branches or pull requests

Issue actions