Formatting Utilities¶
Functions for formatting axis tick labels, rotating labels, and displaying numeric values in common formats.
Overview¶
The formatting module covers the four large-number formats that
matplotlib ships without a built-in answer — millions, billions,
currency, and SI prefixes — plus a label-rotation helper. For
plain percentages and thousands separators, lean on matplotlib’s
ticker.PercentFormatter and ticker.StrMethodFormatter directly.
API Reference¶
Numeric Formatting¶
Note
format_axis_percent and format_axis_thousands were removed in
the 0.4 release. Use matplotlib’s ticker.PercentFormatter and
ticker.StrMethodFormatter directly — see the migration table in
Migration Guide for the per-call replacement. The two examples
below show the canonical patterns.
- dartwork_mpl.format_axis_millions(ax: Axes, axis: Literal['x', 'y', 'both'] = 'y', suffix: str = 'M', decimals: int = 1) None[source]¶
Format axis tick labels in millions.
- Parameters:
ax (Axes) – Matplotlib axes
axis (Literal["x", "y", "both"]) – Which axis to format
suffix (str) – Suffix to add (default: “M”)
decimals (int) – Number of decimal places
Examples
>>> format_axis_millions(ax) # Show as 1.5M instead of 1500000
- dartwork_mpl.format_axis_billions(ax: Axes, axis: Literal['x', 'y', 'both'] = 'y', suffix: str = 'B', decimals: int = 1) None[source]¶
Format axis tick labels in billions.
- Parameters:
ax (Axes) – Matplotlib axes
axis (Literal["x", "y", "both"]) – Which axis to format
suffix (str) – Suffix to add (default: “B”)
decimals (int) – Number of decimal places
Examples
>>> format_axis_billions(ax) # Show as 1.5B instead of 1500000000
- dartwork_mpl.format_axis_currency(ax: Axes, axis: Literal['x', 'y', 'both'] = 'y', symbol: str = '$', position: Literal['prefix', 'suffix'] = 'prefix', decimals: int = 0) None[source]¶
Format axis tick labels as currency.
- Parameters:
ax (Axes) – Matplotlib axes
axis (Literal["x", "y", "both"]) – Which axis to format
symbol (str) – Currency symbol
position (Literal["prefix", "suffix"], optional) – Position of currency symbol
decimals (int) – Number of decimal places
Examples
>>> format_axis_currency(ax) # Format as $1,000 >>> format_axis_currency(ax, symbol="€", position="suffix") # Format as 1,000€
- dartwork_mpl.format_axis_si(ax: Axes, axis: Literal['x', 'y', 'both'] = 'y', decimals: int = 1) None[source]¶
Format axis tick labels with SI prefixes (k, M, G, etc.).
- Parameters:
ax (Axes) – Matplotlib axes
axis (Literal["x", "y", "both"]) – Which axis to format
decimals (int) – Number of decimal places
Examples
>>> format_axis_si(ax) # Show as 1.5k, 2.3M, etc.
Label Management¶
- dartwork_mpl.rotate_tick_labels(ax: Axes, axis: Literal['x', 'y', 'both'] = 'x', rotation: float = 45, ha: Literal['left', 'center', 'right'] | None = None) None[source]¶
Rotate tick labels for better readability.
- Parameters:
ax (Axes) – Matplotlib axes
axis (Literal["x", "y", "both"]) – Which axis to rotate
rotation (float) – Rotation angle in degrees
ha (Literal["left", "center", "right"] | None) – Horizontal alignment. If None, automatically set based on rotation
Examples
>>> rotate_tick_labels(ax) # Rotate x-axis labels 45 degrees >>> rotate_tick_labels(ax, rotation=90, axis="both")
Examples¶
Percentages (matplotlib direct)¶
import dartwork_mpl as dm
import matplotlib.pyplot as plt
from matplotlib.ticker import PercentFormatter
import numpy as np
dm.style.use("scientific")
fig, ax = plt.subplots(figsize=dm.figsize("9cm", "standard"))
# Data as fractions (0 to 1)
x = np.arange(5)
y = np.array([0.15, 0.32, 0.45, 0.28, 0.52])
ax.bar(x, y, color="dc.ocean2")
# Display as integer percentages — multiply 0–1 input by 100
ax.yaxis.set_major_formatter(PercentFormatter(xmax=1.0, decimals=0))
# Or one decimal place
# ax.yaxis.set_major_formatter(PercentFormatter(xmax=1.0, decimals=1))
dm.simple_layout(fig)
Thousands separator (matplotlib direct)¶
from matplotlib.ticker import StrMethodFormatter
sample_counts = np.array([125000, 248000, 392000, 516000, 687000])
fig, ax = plt.subplots(figsize=dm.figsize("9cm", "standard"))
ax.plot(sample_counts, color="dc.forest2")
# 125,000 / 248,000 / …
ax.yaxis.set_major_formatter(StrMethodFormatter("{x:,.0f}"))
dm.simple_layout(fig)
Millions and billions¶
fig, (ax1, ax2) = plt.subplots(
1, 2, figsize=dm.figsize("17cm", "cinema")
)
# Dataset size in millions of rows
dataset_rows = np.array([1.2e6, 2.5e6, 4.8e6, 8.3e6])
ax1.bar(range(len(dataset_rows)), dataset_rows, color="dc.ocean2")
dm.format_axis_millions(ax1, axis="y") # 1.2M, 2.5M, …
ax1.set_title("Dataset rows")
# World population in billions
population = np.array([1.8e9, 2.1e9, 2.4e9, 2.7e9])
ax2.plot(population, "o-", color="dc.cyber2", lw=dm.lw(0))
dm.format_axis_billions(ax2, axis="y", suffix="B") # 1.8B, 2.1B, …
ax2.set_title("Population")
dm.simple_layout(fig)
Currency¶
fig, (ax1, ax2) = plt.subplots(
1, 2, figsize=dm.figsize("17cm", "cinema")
)
prices = np.array([19.99, 34.50, 78.25, 124.00, 259.99])
# US dollars — prefix
ax1.bar(range(len(prices)), prices, color="dc.forest2")
dm.format_axis_currency(ax1, axis="y", symbol="$", position="prefix")
ax1.set_title("Product prices (USD)")
# Euros — suffix
ax2.bar(range(len(prices)), prices * 0.85, color="dc.ocean2")
dm.format_axis_currency(ax2, axis="y", symbol="€", position="suffix")
ax2.set_title("Product prices (EUR)")
dm.simple_layout(fig)
SI prefixes¶
fig, (ax1, ax2) = plt.subplots(
1, 2, figsize=dm.figsize("17cm", "cinema")
)
# Frequency response — 1 kHz to 1 GHz
freq = np.logspace(3, 9, 50)
response = -20 * np.log10(freq / 1e6)
ax1.semilogx(freq, response, color="dc.vivid2")
dm.format_axis_si(ax1, axis="x") # 1k, 10k, 100k, 1M, …
ax1.set_xlabel("Frequency (Hz)")
ax1.set_ylabel("Response (dB)")
# Power measurements — nW to kW
power = np.array([1e-9, 1e-6, 1e-3, 1, 1e3])
ax2.bar(range(len(power)), power, color="dc.sunset2")
ax2.set_yscale("log")
dm.format_axis_si(ax2, axis="y") # 1n, 1μ, 1m, 1, 1k
ax2.set_ylabel("Power (W)")
dm.simple_layout(fig)
Rotating labels¶
fig, (ax1, ax2, ax3) = plt.subplots(
1, 3, figsize=dm.figsize("17cm", "cinema")
)
categories = [
"Category A with Long Name",
"Category B with Even Longer Name",
"Category C Short",
"Category D Medium Length",
"Category E Final",
]
values = np.random.randn(len(categories))
for ax, deg, ha in [(ax1, 45, "right"), (ax2, 90, "right"), (ax3, 30, "right")]:
ax.bar(range(len(categories)), values, color="dc.nordic2")
ax.set_xticks(range(len(categories)))
ax.set_xticklabels(categories)
dm.rotate_tick_labels(ax, axis="x", rotation=deg, ha=ha)
ax.set_title(f"{deg}° rotation")
dm.simple_layout(fig)
Complete example: multi-format dashboard¶
A 2×2 dashboard that exercises all four numeric formatters side-by-side (thousands via matplotlib, currency, millions, billions). The metrics are a neutral mix — sample count, unit price, dataset rows, cumulative energy — so the page reads as a formatter showcase rather than a finance template.
import dartwork_mpl as dm
import matplotlib.pyplot as plt
from matplotlib.ticker import StrMethodFormatter
import numpy as np
dm.style.use("report")
periods = ["T1", "T2", "T3", "T4", "T5"]
samples = np.array([125_000, 248_000, 392_000, 516_000, 687_000])
unit_price = np.array([45.20, 52.30, 61.50, 58.40, 67.80])
dataset_rows = np.array([1.2e6, 3.4e6, 5.1e6, 7.8e6, 9.6e6])
cumulative_energy = np.array([1.8e9, 3.9e9, 6.3e9, 9.0e9, 11.8e9])
fig = plt.figure(figsize=dm.figsize("17cm", "wide"))
gs = fig.add_gridspec(2, 2, hspace=0.45, wspace=0.30)
ax1 = fig.add_subplot(gs[0, 0])
ax1.bar(periods, samples, color="dc.forest2")
ax1.yaxis.set_major_formatter(StrMethodFormatter("{x:,.0f}"))
ax1.set_title("Sample count")
ax1.set_ylabel("Samples")
ax2 = fig.add_subplot(gs[0, 1])
ax2.plot(periods, unit_price, "o-", color="dc.ocean2", lw=dm.lw(0))
dm.format_axis_currency(ax2, axis="y", symbol="$")
ax2.set_title("Unit price")
ax3 = fig.add_subplot(gs[1, 0])
ax3.bar(periods, dataset_rows, color="dc.ocean2")
dm.format_axis_millions(ax3, axis="y")
ax3.set_title("Dataset rows")
ax4 = fig.add_subplot(gs[1, 1])
ax4.bar(periods, cumulative_energy, color="dc.sunset2")
dm.format_axis_billions(ax4, axis="y", suffix="B")
ax4.set_title("Cumulative energy")
ax4.set_ylabel("Joules")
for ax in (ax1, ax2, ax3, ax4):
ax.spines["top"].set_visible(False)
ax.spines["right"].set_visible(False)
ax.grid(True, axis="y", alpha=0.2, color="dc.nordic1", linewidth=dm.lw(0))
ax.set_axisbelow(True)
dm.rotate_tick_labels(ax, axis="x", rotation=45)
dm.simple_layout(fig, gs=gs)
Custom formats via matplotlib¶
For formats not covered by the helpers, lean on matplotlib’s
FuncFormatter directly:
from matplotlib.ticker import FuncFormatter
# Fractions
def _format_fraction(x, pos):
from fractions import Fraction
return str(Fraction(x).limit_denominator(10))
ax.yaxis.set_major_formatter(FuncFormatter(_format_fraction))
# Log-scale exponents
def _format_log(x, pos):
exponent = int(np.log10(x))
return f"$10^{{{exponent}}}$"
ax.xaxis.set_major_formatter(FuncFormatter(_format_log))
# Indian numbering — lakhs and crores
def _format_indian(x, pos):
if x >= 1e7:
return f"{x / 1e7:.1f}Cr"
if x >= 1e5:
return f"{x / 1e5:.1f}L"
return f"{x:.0f}"
Best practices¶
Pick the smallest unit that reads cleanly.
format_axis_millionsbeats raw1,200,000on a tight axis.Decimal places. 0–1 for general audiences, 2–3 for technical or currency values.
Currency placement follows local convention — prefix for USD, suffix for EUR.
SI prefixes belong in scientific / engineering contexts; mixing them with comma-grouped thousands looks inconsistent.
See also¶
Helper Utilities (dm.helpers) — AI helper utilities
Quick Start — getting-started guide