D3 Value Formatters

See the Value Formatters for more information on getting
started with valueFormatter.

Formatting Numbers with d3-format

Dash AG Grid includes the d3-format library, so you have access to all d3-format
functions to use with valueFormatter.

The basic syntax for the d3 function is: d3.format(specifier)(value)

For example:

d3.format(".0%")(0.123);  # rounded percentage, "12%"
d3.format("($.2f")(-3.5); # localized fixed-point currency, "(£3.50)"
d3.format("+20")(42);     # space-filled and signed, "                 +42"
d3.format(".^20")(42);    # dot-filled and centered, ".........42........."
d3.format(".2s")(42e6);   # SI-prefix with two significant digits, "42M"
d3.format("#x")(48879);   # prefixed lowercase hexadecimal, "0xbeef"
d3.format(",.2r")(4223);  # grouped thousands with two significant digits, "4,200"

For more options, see d3-format examples

If this looks familiar to you, it’s because this JavaScript library is based on Python!

So d3.format(".0%")(0.123) is like "{:.0%}".format(0.123) in Python. The specifiers are based on
the Python’s Format Specification.

In dash-ag-grid, use the d3.format function in the column definitions like this:

# example valueFormatter for currency
columnDefs = [
  {
    "valueFormatter": {"function": "d3.format('($,.2f')(params.value)"},
  },
]

Examples: Number formatting

Here is a simple example of formatting numbers with d3.format function:

```python
import dash_ag_grid as dag
from dash import Dash, html

app = Dash()

rowData = [
    dict(account='A', quantity=522.31, balance=522.31, rate=0.139),
    dict(account='B', quantity=1607.9, balance=1607.9, rate=0.104444),
    dict(account='C', quantity=-228.41, balance=-228.41, rate=0.19999),
]

columnDefs = [
    {"field": "account"},
    {
        "field": "quantity",
        "valueFormatter": {"function": "d3.format(',.1f')(params.value)"},
    },
    {
        "field": "balance",
        "valueFormatter": {"function": "d3.format('($,.2f')(params.value)"},
    },
    {
        "field": "rate",
        "valueFormatter": {"function": "d3.format(',.1%')(params.value)"},
    }
]

app.layout = html.Div(
    [
        dag.AgGrid(
            id="d3-value-formatters-number-formatting-grid",
            columnDefs=columnDefs,
            rowData=rowData,
            columnSize="sizeToFit",
            defaultColDef={"type": "rightAligned", "editable": True},
            dashGridOptions={"singleClickEdit": True},
        ),
    ],
)

if __name__ == "__main__":
    app.run(debug=True)
```

Here are examples using the Plolty “Gapminder” dataset with and without number formatting.

```python
import dash_ag_grid as dag
from dash import Dash, html
import plotly.express as px

df = px.data.gapminder()

app = Dash()

columnDefs = [
    {"headerName": "Country", "field": "country"},
    {"headerName": "Continent", "field": "continent"},
    {"headerName": "Year", "field": "year"},
    {
        "headerName": "Life Expectancy",
        "field": "lifeExp",
        "type": "rightAligned",
        "valueFormatter": {"function": "d3.format('.1f')(params.value)"},
    },
    {
        "headerName": "Population",
        "field": "pop",
        "type": "rightAligned",
        "valueFormatter": {"function": "d3.format(',.0f')(params.value)"},
    },
    {
        "headerName": "GDP per Capita",
        "field": "gdpPercap",
        "type": "rightAligned",
        "valueFormatter": {"function": "d3.format('$,.1f')(params.value)"},
    },
]

app.layout = html.Div(
    [
        'With number formatting',
        dag.AgGrid(
            id="d3-value-formatters-number-formatting-gapminder-with-grid",
            columnDefs=columnDefs,
            rowData=df.to_dict("records"),
            columnSize="sizeToFit",
            style={'marginBottom': 20}
        ),
        'Without number formatting',
        dag.AgGrid(
            id="d3-value-formatters-number-formatting-gapminder-without-grid",
            columnDefs=[{"field": i} for i in df.columns if i not in ["iso_alpha", "iso_num"]],
            rowData=df.to_dict("records"),
            columnSize="sizeToFit",
        ),
    ],
)

if __name__ == "__main__":
    app.run(debug=True)
```

Example: d3.format specifiers

Here are examples of formatting numbers with some of the specifiers available in the d3.format function.

```python
import dash_ag_grid as dag
from dash import Dash, html

app = Dash()

columnDefs = [
    {"headerName": "Description", "field": "description", "minWidth": 300},
    {
        "headerName": "d3.format",
        "field": "format",
        "minWidth": 300,
        "cellRenderer": "markdown",
    },
    {"headerName": "Specifier", "field": "specifier", "minWidth": 100},
    {"headerName": "Value", "field": "value", "minWidth": 125},
    {
        "headerName": "Formatted Value",
        "field": "formatted",
        "valueFormatter": {"function": "d3.format(params.data.specifier)(params.value)"},
        "minWidth": 150,
    },
]

rowData = [
    {
        "description": "fixed decimal",
        "format": "`d3.format('.1f')(0.1234)`",
        "specifier": ".1f",
        "value": 0.1234,
        "formatted": 0.1234,
        "notes": "",
    },
    {
        "description": "fixed decimal",
        "format": "`d3.format('.2f')(0.1234)`",
        "specifier": ".2f",
        "value": 0.1234,
        "formatted": 0.1234,
        "notes": "",
    },
    {
        "description": "rounded percentage",
        "format": "`d3.format('.0%')(0.1234)`",
        "specifier": ".0%",
        "value": 0.1234,
        "formatted": 0.1234,
        "notes": "",
    },
    {
        "description": "rounded percentage",
        "format": "`d3.format('.1%')(0.1234)`",
        "specifier": ".1%",
        "value": 0.1234,
        "formatted": 0.1234,
        "notes": "",
    },
    {
        "description": "localized fixed-point currency",
        "format": "`d3.format('$,.2f')(1000.1234)`",
        "specifier": "$,.2f",
        "value": 1000.1234,
        "formatted": 1000.1234,
        "notes": "",
    },
    {
        "description": "localized fixed-point currency",
        "format": "`d3.format('$,.2f')(-1000.1234)`",
        "specifier": "$,.2f",
        "value": -1000.1234,
        "formatted": -1000.1234,
        "notes": "",
    },
    {
        "description": "localized fixed-point currency",
        "format": "`d3.format('($,.2f')(-1000.1234)`",
        "specifier": "($,.2f",
        "value": -1000.1234,
        "formatted": -1000.1234,
        "notes": "",
    },
    {
        "description": "signed",
        "format": "`d3.format('+')(12)`",
        "specifier": "+",
        "value": 12,
        "formatted": 12,
        "notes": "",
    },
    {
        "description": "dot filled and centered",
        "format": "`d3.format('.^20')(12)`",
        "specifier": ".^20",
        "value": 12,
        "formatted": 12,
        "notes": "",
    },
    {
        "description": "SI-prefix with two significant digits",
        "format": "`d3.format('.2s')(421e6)`",
        "specifier": ".2s",
        "value": 42e6,
        "formatted": 42e6,
        "notes": "",
    },
    {
        "description": "grouped thousands with two significant digits",
        "format": "`d3.format(',.2r')(4223)`",
        "specifier": ",.2r",
        "value": 4223,
        "formatted": 4223,
        "notes": "",
    },
]

app.layout = html.Div(
    [
        dag.AgGrid(
            id="d3-value-formatters-format-specifiers-grid",
            style={"height": "500px", "width": "100%"},
            columnDefs=columnDefs,
            rowData=rowData,
            columnSize="sizeToFit",
        ),
    ],
)

if __name__ == "__main__":
    app.run(debug=True)
```

Locales

Locales adapt the behavior of d3-format to various languages and places. The definition may include the following
properties:

Note that the thousands property is a misnomer, as the grouping definition allows groups other than thousands.

The default locale for d3.format is U.S. English, which sets:

{
  "decimal": ".",
  "thousands": ",",
  "grouping": [3],
  "currency": ["$", ""]
}

To render currency numbers in French, you could override these default marks by specifying instead a locale such as:

{
  "decimal": ",",
  "thousands": " ",
  "grouping": [3],
  "currency": ["", "€"],
  "nan": "",
}

The decimal separator becomes a comma, thousands are separated by a space, and the default currency mark, the euro
symbol, appears after the number.
This example includes changing the default for not-a-number from NaN to ""

Locales are not loaded by default - you can find them here. Their
definition can be fed to d3.formatLocale:

locale_en_GB = """d3.formatLocale({
  "decimal": ".",
  "thousands": ",",
  "grouping": [3],
  "currency": ["£", ""]
})"""

Then in can be used in the valueFormatter:

columnDefs = [
  {
    "field": "UK",
    "valueFormatter": {"function": f"{locale_en_GB}.format('$,.2f')(params.value)"},
  },
]

Example: Formatting with locale

In this example, each column is formatted with the specifier '$,.2f'. The locale determines how the numbers will be
displayed.
Note that we also set a custom NaN value in the “France” column. Instead of displaying “NaN” it will be blank.

```python
import dash_ag_grid as dag
from dash import Dash, html
import pandas as pd
import numpy as np

app = Dash()

df = pd.DataFrame(
    np.random.uniform(-10000, 10000, size=(15, 4)),
    columns=["Default", "France", "Japan", "UK"],
)

# find locals at https://cdn.jsdelivr.net/npm/d3-format@1/locale/

# "nan" is the not-a-number value (defaults `"NaN"`). Here we change it to ""
locale_fr_FR = """d3.formatLocale({
  "decimal": ",",
  "thousands": "\u00a0",
  "grouping": [3],
  "currency": ["", "\u00a0€"],
  "percent": "\u202f%",
  "nan": ""
})"""

locale_ja_JP = """d3.formatLocale({
  "decimal": ".",
  "thousands": ",",
  "grouping": [3],
  "currency": ["", "円"]
})"""

locale_en_GB = """d3.formatLocale({
  "decimal": ".",
  "thousands": ",",
  "grouping": [3],
  "currency": ["£", ""]
})"""

columnDefs = [
    {
        "field": "Default",
        "valueFormatter": {"function": "d3.format('$,.2f')(params.value)"},
    },
    {
        "field": "France",
        "valueFormatter": {"function": f"{locale_fr_FR}.format('$,.2f')(params.value)"},
    },
    {
        "field": "Japan",
        "valueFormatter": {"function": f"{locale_ja_JP}.format('$,.2f')(params.value)"},
    },
    {
        "field": "UK",
        "valueFormatter": {"function": f"{locale_en_GB}.format('$,.2f')(params.value)"},
    },
]

app.layout = html.Div(
    [
        dag.AgGrid(
            id="d3-value-formatters-locale-grid",
            columnDefs=columnDefs,
            rowData=df.to_dict("records"),
            columnSize="sizeToFit",
            defaultColDef={"type": "rightAligned", "editable": True},
        ),
    ],
)

if __name__ == "__main__":
    app.run(debug=True)
```

Editable example

In this table try changing the specifier and the values.

```python
import dash_ag_grid as dag
from dash import Dash, html, dcc

app = Dash()

rowData = [
    {"specifier": "$,.2f", "Default": 1000, "France": 1000, "Japan": 1000, "UK": 1000},
]

# find locals at https://cdn.jsdelivr.net/npm/d3-format@1/locale/
locale_fr_FR = """d3.formatLocale({
  "decimal": ",",
  "thousands": "\u00a0",
  "grouping": [3],
  "currency": ["", "\u00a0€"],
  "percent": "\u202f%"
})"""

locale_ja_JP = """d3.formatLocale({
 "decimal": ".",
  "thousands": ",",
  "grouping": [3],
  "currency": ["", "円"]

})"""

locale_en_GB = """d3.formatLocale({
 "decimal": ".",
  "thousands": ",",
  "grouping": [3],
  "currency": ["£", ""]
})"""

columnDefs = [
    {"field": "specifier", "headerName": "d3 specifier"},
    {
        "field": "Default",
        "valueFormatter": {"function": "d3.format(params.data.specifier)(params.value)"},
    },
    {
        "field": "France",
        "valueFormatter": {"function": f"{locale_fr_FR}.format(params.data.specifier)(params.value)"},
    },
    {
        "field": "Japan",
        "valueFormatter": {"function": f"{locale_ja_JP}.format(params.data.specifier)(params.value)"},
    },
    {
        "field": "UK",
        "valueFormatter": {"function": f"{locale_en_GB}.format(params.data.specifier)(params.value)"},
    },
]

app.layout = html.Div(
    [
        dcc.Markdown(
            "This editable grid demonstrates formatting numbers for different locales using d3-format. Try changing the specifier and/or the values!"
        ),
        dag.AgGrid(
            id="d3-value-formatters-editable-grid",
            columnDefs=columnDefs,
            rowData=rowData,
            columnSize="sizeToFit",
            defaultColDef={"type": "rightAligned", "editable": True},
            dashGridOptions={"singleClickEdit": True},
        ),
    ],
)

if __name__ == "__main__":
    app.run(debug=True)
```

Formatting Dates and Times with d3-time-format

Dash AG Grid includes the d3-time-format library, so you have access to
all d3-time-format functions to use with valueFormatter.

The basic syntax for formatting a date object is:

formatted_date_string = d3.timeFormat(specifier)(date object)

d3.timeFormat() is like strftime() in Python.

Note that even if your datetime data is an object on the server, when it’s sent to the grid in the browser it’s
converted to a string.
See Example: Formatting Dates defined as datetime.

If the datetime data uses the ISO string format 'yyyy-mm-dd' it will be automatically inferred into JavaScript date
object the first time that row data is passed into the grid,
using Cell Data Types.
If the datetime data uses another format like 'dd/mm/yyyy', it is possible to:

d3.timeParse() is like strptime() in Python

When the date is converted to a JavaScript date object, then the AG Grid date filter agDateColumnFilter will work out
of the box, and no additional date filter comparator functions are required.

Here are the specifiers:

Example: Formatting Dates defined as strings

```python
from dash import Dash, html
import dash_ag_grid as dag

app = Dash()

rowData = [
    {"date": "2023-01-01"},
    {"date": "2023-02-11"},
    {"date": "2023-06-10"},
    {"date": "2023-11-04"},
    {"date": "2023-12-03"},
]

# function to create a date object from  a date string "YYYY-MM-DD"
date_obj = "d3.timeParse('%Y-%m-%d')(params.data.date)"

columnDefs = [
    {
        "headerName": "Date String",
        "field": "date",
        "filter": False,
    },
    {
        "headerName": "MM/DD/YYYY",
        "valueGetter": {"function": date_obj},
        "valueFormatter": {"function": f"d3.timeFormat('%m/%d/%Y')({date_obj})"},
    },
    {
        "headerName": "Mon DD, YYYY",
        "valueGetter": {"function": date_obj},
        "valueFormatter": {"function": f"d3.timeFormat('%b %d, %Y')({date_obj})"},
    },
    {
        "headerName": "day, Mon DD, YYYY",
        "valueGetter": {"function": date_obj},
        "valueFormatter": {"function": f"d3.timeFormat('%a %b %d, %Y')({date_obj})"},
    },
    {
        "headerName": "Month d, YYYY",
        "valueGetter": {"function": date_obj},
        "valueFormatter": {"function": f"d3.timeFormat('%B %e, %Y')({date_obj})"},
    },
    {
        "headerName": "MM-DD-YY",
        "valueGetter": {"function": date_obj},
        "valueFormatter": {"function": f"d3.timeFormat('%m-%d-%y')({date_obj})"},
    },
]

defaultColDef = {
    "filter": "agDateColumnFilter",
    "filterParams": {"buttons": ["clear", "apply"]},
    "sortable": True,
}

app.layout = html.Div(
    [
        dag.AgGrid(
            id="d3-value-formatters_date-times-grid",
            columnDefs=columnDefs,
            rowData=rowData,
            defaultColDef=defaultColDef
        ),
    ],
)

if __name__ == "__main__":
    app.run(debug=True)
```

Example: Formatting Dates defined as datetime

```python
from dash import Dash, html
import dash_ag_grid as dag
from datetime import datetime

rowData = [
    {"date": datetime(2023, 1, 1, 1, 0, 0)},
    {"date": datetime(2023, 1, 2, 2, 0, 0)},
    {"date": datetime(2023, 1, 3, 3, 0, 0)},
    {"date": datetime(2023, 1, 4, 18, 0, 0)},
    {"date": datetime(2023, 1, 5, 22, 0, 0)},
]

# function to create a date object from  a date string "YYYY-MM-DD"
date_obj = "d3.timeParse('%Y-%m-%dT%H:%M:%S')(params.data.date)"

# if the time is in utc:
# date_obj = "d3.utcParse('%Y-%m-%dT%H:%M:%S')(params.data.date)"


columnDefs = [
    {
        "headerName": "Datetime string",
        "field": "date",
        "filter": False,
    },
    {
        "headerName": "MM/DD/YYYY",
        "valueGetter": {"function": date_obj},
        "valueFormatter": {"function": f"d3.timeFormat('%m/%d/%Y')({date_obj})"},
    },
    {
        "headerName": "Mon DD, YYYY",
        "valueGetter": {"function": date_obj},
        "valueFormatter": {"function": f"d3.timeFormat('%b %d, %Y')({date_obj})"},
    },
    {
        "headerName": "day, Mon DD, YYYY",
        "valueGetter": {"function": date_obj},
        "valueFormatter": {"function": f"d3.timeFormat('%a %b %d, %Y')({date_obj})"},
    },
    {
        "headerName": "yyyy-mm-dd HH:MM:SS tt",
        "valueGetter": {"function": date_obj},
        "valueFormatter": {
            "function": f"d3.timeFormat('%Y-%m-%d %I:%M:%S %p')({date_obj})"
        },
    },
    {
        "headerName": "yyyy-mm-dd hh:mm:ss",
        "valueGetter": {"function": date_obj},
        "valueFormatter": {
            "function": f"d3.timeFormat('%Y-%m-%d %H:%M:%S')({date_obj})"
        },
    },
]

defaultColDef = {
    "filter": "agDateColumnFilter",
    "filterParams": {"buttons": ["clear", "apply"]},
    "sortable": True,
}

app = Dash()

app.layout = html.Div(
    [
        dag.AgGrid(
            id="d3-value-formatters-datetime-grid",
            columnDefs=columnDefs,
            rowData=rowData,
            defaultColDef=defaultColDef
        ),
    ],
)

if __name__ == "__main__":
    app.run(debug=True)
```