breaks - Partitioning a scale for readability

All scales have a means by which the values that are mapped onto the scale are interpreted. Numeric digital scales put out numbers for direct interpretation, but most scales cannot do this. What they offer is named markers/ticks that aid in assessing the values e.g. the common odometer will have ticks and values to help gauge the speed of the vehicle.

The named markers are what we call breaks. Properly calculated breaks make interpretation straight forward. These functions provide ways to calculate good(hopefully) breaks.

class mizani.breaks.breaks_log(n: int = 5, base: float = 10)[source]

Integer breaks on log transformed scales

Parameters:
npython:int

Desired number of breaks

basepython:int

Base of logarithm

Examples

>>> x = np.logspace(3, 6)
>>> limits = min(x), max(x)
>>> breaks_log()(limits)
array([     1000,    10000,   100000,  1000000])
>>> breaks_log(2)(limits)
array([  1000, 100000])
>>> breaks_log()([0.1, 1])
array([0.1, 0.3, 1. , 3. ])
__call__(limits: tuple[float, float]) NDArrayFloat[source]

Compute breaks

Parameters:
limitspython:tuple

Minimum and maximum values

Returns:
outnumpy:array_like

Sequence of breaks points

class mizani.breaks.breaks_symlog[source]

Breaks for the Symmetric Logarithm Transform

Examples

>>> limits = (-100, 100)
>>> breaks_symlog()(limits)
array([-100,  -10,    0,   10,  100])
__call__(limits: tuple[float, float]) NDArrayFloat[source]

Call self as a function.

class mizani.breaks.minor_breaks(n: int = 1)[source]

Compute minor breaks

This is the naive method. It does not take into account the transformation.

Parameters:
npython:int

Number of minor breaks between the major breaks.

Examples

>>> major = [1, 2, 3, 4]
>>> limits = [0, 5]
>>> minor_breaks()(major, limits)
array([0.5, 1.5, 2.5, 3.5, 4.5])
>>> minor_breaks()([1, 2], (1, 2))
array([1.5])

More than 1 minor break.

>>> minor_breaks(3)([1, 2], (1, 2))
array([1.25, 1.5 , 1.75])
>>> minor_breaks()([1, 2], (1, 2), 3)
array([1.25, 1.5 , 1.75])
__call__(major: FloatArrayLike, limits: tuple[float, float] | None = None, n: int | None = None) NDArrayFloat[source]

Minor breaks

Parameters:
majornumpy:array_like

Major breaks

limitsnumpy:array_like | python:None

Limits of the scale. If array_like, must be of size 2. If None, then the minimum and maximum of the major breaks are used.

npython:int

Number of minor breaks between the major breaks. If None, then self.n is used.

Returns:
outnumpy:array_like

Minor beraks

class mizani.breaks.minor_breaks_trans(trans: Trans, n: int = 1)[source]

Compute minor breaks for transformed scales

The minor breaks are computed in data space. This together with major breaks computed in transform space reveals the non linearity of of a scale. See the log transforms created with log_trans() like log10_trans.

Parameters:
transtrans or type

Trans object or trans class.

npython:int

Number of minor breaks between the major breaks.

Examples

>>> from mizani.transforms import sqrt_trans
>>> major = [1, 2, 3, 4]
>>> limits = [0, 5]
>>> t1 = sqrt_trans()
>>> t1.minor_breaks(major, limits)
array([1.58113883, 2.54950976, 3.53553391])

# Changing the regular minor_breaks method

>>> t2 = sqrt_trans()
>>> t2.minor_breaks = minor_breaks()
>>> t2.minor_breaks(major, limits)
array([0.5, 1.5, 2.5, 3.5, 4.5])

More than 1 minor break

>>> major = [1, 10]
>>> limits = [1, 10]
>>> t2.minor_breaks(major, limits, 4)
array([2.8, 4.6, 6.4, 8.2])
__call__(major: FloatArrayLike, limits: tuple[float, float] | None = None, n: int | None = None) NDArrayFloat[source]

Minor breaks for transformed scales

Parameters:
majornumpy:array_like

Major breaks

limitsnumpy:array_like | python:None

Limits of the scale. If array_like, must be of size 2. If None, then the minimum and maximum of the major breaks are used.

npython:int

Number of minor breaks between the major breaks. If None, then self.n is used.

Returns:
outnumpy:array_like

Minor breaks

class mizani.breaks.breaks_date(n: int = 5)[source]

Regularly spaced dates

Parameters:
n

Desired number of breaks.

Examples

>>> from datetime import datetime
>>> limits = (datetime(2010, 1, 1), datetime(2026, 1, 1))

Default breaks will be regularly spaced but the spacing is automatically determined

>>> breaks = breaks_date(9)
>>> [d.year for d in breaks(limits)]
[2010, 2012, 2014, 2016, 2018, 2020, 2022, 2024, 2026]
__call__(limits: tuple[datetime, datetime] | tuple[date, date]) Sequence[datetime][source]

Compute breaks

Parameters:
limitspython:tuple

Minimum and maximum datetime.datetime values.

Returns:
outnumpy:array_like

Sequence of break points.

class mizani.breaks.breaks_date_width(width: str, offset: int | DatetimeOffset = None)[source]

Regularly spaced dates by width

Parameters:
widthpython:str

The interval between the breaks. A string of the form, "<number> <units>"`. The units are one of:

microseconds milliseconds seconds minutes hours days weeks months years decades centuries

or their singular forms. secs and mins or their singular forms are also recognised as abbreviations for seconds and minutes.

offsetpython:int | timedelta | python:str | Sequence[python:str] | relativedelta | python:None

The breaks are set to start at some "nice" value but apply an offset you can shift them to a value you may prefer..

  • If an int, the units will be the same as the width.

  • If a Sequence, it is of the form ("[+-]<number> <units>", "[+-]<number> <units>", ...) e.g. ("1 year", "2 months", ...).

  • If a str, it is of the form "[+-]<number> <units>" e.g. "2 years".

  • If None, do not shift.

Examples

Breaks at 4 year intervals

>>> limits = [datetime(2010, 1, 1), datetime(2025, 1, 1)]
>>> breaks = breaks_date_width("4 years")
>>> [d.year for d in breaks(limits)]
[2010, 2014, 2018, 2022, 2026]
>>> breaks = breaks_date_width("4 years", offset=1)
>>> [d.year for d in breaks(limits)]
[2011, 2015, 2019, 2023, 2027]
__call__(limits: tuple[datetime, datetime] | tuple[date, date]) Sequence[datetime][source]

Compute breaks

Parameters:
limits

Minimum and maximum datetime.datetime values.

Returns:
out

Sequence of break points.

class mizani.breaks.breaks_width(width: float, offset: float | None = None)[source]

Regularly spaced dates by width

Parameters:
width

The interval between the breaks.

offset

Shift the calculated breaks by this much.

Examples

Breaks at 4 year intervals

>>> limits = [3, 14]
>>> breaks = breaks_width(width=4)
>>> breaks(limits)
array([ 0,  4,  8, 12, 16])
__call__(limits: tuple[float, float]) NDArrayFloat[source]

Call self as a function.

class mizani.breaks.breaks_timedelta(n: int = 5)[source]

Timedelta breaks

Returns:
outpython:callable() f(limits)

A function that takes a sequence of two datetime.timedelta values and returns a sequence of break points.

Examples

>>> from datetime import timedelta
>>> breaks = breaks_timedelta()
>>> limits = (timedelta(days=0), timedelta(days=345))
>>> major = breaks(limits)
>>> [b.days for b in major]
[0, 70, 140, 210, 280, 350]
__call__(limits: tuple[Timedelta, Timedelta]) TimedeltaArrayLike[source]

Compute breaks

Parameters:
limitspython:tuple

Minimum and maximum datetime.timedelta values.

Returns:
outnumpy:array_like

Sequence of break points.

class mizani.breaks.breaks_timedelta_width(width: str, offset: int | TimedeltaOffset = None)[source]

Regularly spaced timedeltas by width

Parameters:
widthpython:str

The interval between the breaks. A string of the form, "<number> <units>"`. The units are one of:

microseconds milliseconds seconds minutes hours days weeks

offset

Use this to shift the calculated breaks so that they start at a value you may prefer.

  • If an int, the units will be the same as the width.

  • If a Sequence, it is of the form ("[+-]<number> <units>", "[+-]<number> <units>", ...) e.g. ("2 days", "12 hours", ...).

  • If a str, it is of the form "[+-]<number> <units>" e.g. "4 hours"

  • If None, do not shift.

__call__(limits: tuple[Timedelta, Timedelta]) TimedeltaArrayLike[source]

Compute breaks

Parameters:
limits

Minimum and maximum datetime.timedelta values.

Returns:
out

Sequence of break points.

class mizani.breaks.breaks_extended(n: int = 5, Q: Sequence[float] = (1, 5, 2, 2.5, 4, 3), only_inside: bool = False, w: Sequence[float] = (0.25, 0.2, 0.5, 0.05))[source]

An extension of Wilkinson's tick position algorithm

Parameters:
npython:int

Desired number of breaks

Qpython:list

List of nice numbers

only_insidebool

If True, then all the breaks will be within the given range.

wpython:list

Weights applied to the four optimization components (simplicity, coverage, density, and legibility). They should add up to 1.

References

  • Talbot, J., Lin, S., Hanrahan, P. (2010) An Extension of Wilkinson's Algorithm for Positioning Tick Labels on Axes, InfoVis 2010.

Additional Credit to Justin Talbot on whose code this implementation is almost entirely based.

Examples

>>> limits = (0, 9)
>>> breaks_extended()(limits)
array([  0. ,   2.5,   5. ,   7.5,  10. ])
>>> breaks_extended(n=6)(limits)
array([  0.,   2.,   4.,   6.,   8.,  10.])
__call__(limits: tuple[float, float]) NDArrayFloat[source]

Calculate the breaks

Parameters:
limitsarray

Minimum and maximum values.

Returns:
outnumpy:array_like

Sequence of break points.