Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ There are quite a lot of deprecations for this release.

.. rubric:: Features

- Add `~proplot.ticker.SigFigFormatter` (:pr:`149`, :commit:`da6105d2`)
and `~proplot.ticker.SciFormatter` (:pr:`175`, :commit:`c43f7f91`)
axis formatters.
- Use `_LonAxis` and `_LatAxis` dummy axes with custom `LongitudeLocator`
and `LatitudeLocator` to control geographic gridlines (:pr:`168`).
- Add ``'dmslat'`` and ``'dmslon'`` as formatters for cartopy projections,
Expand Down Expand Up @@ -165,8 +168,6 @@ There are quite a lot of deprecations for this release.
(:commit:`f801852b`).
- Change default line style for geographic gridlines from ``':'`` to ``'-'``
and match style from primary gridlines (:commit:`f801852b`).
- Support cartopy inline meridian and parallel gridlines and support
changing the gridline padding (:commit:`###`).
- Support `cartopy 0.18 <https://scitools.org.uk/cartopy/docs/latest/whats_new.html>`__
locators, formatters, deprecations, and new labelling features (:pr:`158`).
- Support building a colormap and `DiscreteNorm` inside `~matplotlib.axes.Axes.scatter`,
Expand Down Expand Up @@ -216,7 +217,7 @@ There are quite a lot of deprecations for this release.
- Fix various issues with axis label sharing and axis sharing for
twinned axes and panel axes (:pr:`164`).
- Permit modifying existing cartopy geographic features with successive
calls to `~proplot.axes.GeoAxes.format` (:commit:`###`).
calls to `~proplot.axes.GeoAxes.format` (:pr:`168`).
- Fix issue drawing bar plots with datetime *x* axes (:pr:`156`).
- Fix issue where `~proplot.ticker.AutoFormatter` tools were not locale-aware, i.e. use
comma as decimal point sometimes (:commit:`c7636296`).
Expand Down
59 changes: 39 additions & 20 deletions docs/axis.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,16 +112,17 @@
#
# These keyword arguments can be used to apply built-in matplotlib
# `~matplotlib.ticker.Formatter`\ s by their "registered" names (e.g.
# ``xformatter='log'``), to apply new "preset" axis formatters (e.g.
# ``xformatter='deglat'`` to label ticks as the geographic latitude or
# ``xformatter='pi'`` to label ticks as fractions of :math:`\pi`), to apply a
# ``%``-style format directive with `~matplotlib.ticker.FormatStrFormatter`
# (e.g. ``xformatter='%.0f'``), or to apply custom tick labels with
# `~matplotlib.ticker.FixedFormatter` (just like
# `~matplotlib.axes.Axes.set_xticklabels` and
# `~matplotlib.axes.Axes.set_yticklabels`). See
# `~proplot.axes.CartesianAxes.format` and `~proplot.constructor.Formatter`
# for details.
# ``xformatter='log'``), to apply a ``%``-style format directive with
# `~matplotlib.ticker.FormatStrFormatter` (e.g. ``xformatter='%.0f'``), or
# to apply custom tick labels with `~matplotlib.ticker.FixedFormatter` (just
# like `~matplotlib.axes.Axes.set_xticklabels` and
# `~matplotlib.axes.Axes.set_yticklabels`). They can also be used
# to apply one of ProPlot's new axis formatters -- for example,
# ``xformatter='deglat'`` to label ticks as the geographic latitude,
# ``xformatter='pi'`` to label ticks as fractions of :math:`\pi`,
# or ``xformatter='sci'`` to label ticks with scientific notation.
# See `~proplot.axes.CartesianAxes.format` and
# `~proplot.constructor.Formatter` for details.
#
# ProPlot also changes the default axis formatter to
# `~proplot.ticker.AutoFormatter`. This class trims trailing zeros by
Expand All @@ -137,36 +138,48 @@
suptitlecolor='w', gridcolor='w', color='w',
titleloc='upper center', titlecolor='w', titleborder=False,
)
fig, axs = plot.subplots(nrows=6, axwidth=5, aspect=(8, 1), share=0)
fig, axs = plot.subplots(nrows=8, axwidth=5, aspect=(8, 1), share=0)

# Fraction formatters
# Scientific notation
axs[0].format(
xlim=(0, 1e20),
xformatter='sci', title='SciFormatter'
)

# N significant figures for ticks at specific values
axs[1].format(
xlim=(0, 20), xlocator=(0.0034, 3.233, 9.2, 15.2344, 7.2343, 19.58),
xformatter=('sigfig', 2), title='SigFigFormatter', # 2 significant digits
)

# Fraction formatters
axs[2].format(
xlim=(0, 3 * np.pi), xlocator=plot.arange(0, 4, 0.25) * np.pi,
xformatter='pi', title='FracFormatter',
)
axs[1].format(
axs[3].format(
xlim=(0, 2 * np.e), xlocator=plot.arange(0, 2, 0.5) * np.e,
xticklabels='e', title='FracFormatter',
)

# Geographic formatter
axs[2].format(
axs[4].format(
xlim=(-90, 90), xlocator=plot.arange(-90, 90, 30),
xformatter='deglat', title='Geographic preset'
xformatter='deglat', title='Geographic Formatter'
)

# User input labels
axs[3].format(
axs[5].format(
xlim=(-1.01, 1), xlocator=0.5,
xticklabels=['a', 'b', 'c', 'd', 'e'], title='FixedFormatter',
)

# Custom style labels
axs[4].format(
axs[6].format(
xlim=(0, 0.001), xlocator=0.0001,
xformatter='%.E', title='FormatStrFormatter',
)
axs[5].format(
axs[7].format(
xlim=(0, 100), xtickminor=False, xlocator=20,
xformatter='{x:.1f}', title='StrMethodFormatter',
)
Expand All @@ -178,7 +191,7 @@
plot.rc.linewidth = 2
plot.rc.fontsize = 11
locator = [0, 0.25, 0.5, 0.75, 1]
fig, axs = plot.subplots([[1, 1, 2, 2], [0, 3, 3, 0]], axwidth=1.5, share=0)
fig, axs = plot.subplots(ncols=2, nrows=2, axwidth=1.5, share=0)

# Formatter comparison
axs[0].format(
Expand All @@ -187,11 +200,17 @@
axs[1].format(yticklabelloc='both', title='ProPlot formatter')
axs[:2].format(xlocator=locator, ylocator=locator)

# Limiting the formatter tick range
# Limiting the tick range
axs[2].format(
title='Omitting tick labels', ticklen=5, xlim=(0, 5), ylim=(0, 5),
xtickrange=(0, 2), ytickrange=(0, 2), xlocator=1, ylocator=1
)

# Setting the wrap range
axs[3].format(
title='Wrapping the tick range', ticklen=5, xlim=(0, 7), ylim=(0, 6),
xwraprange=(0, 5), ywraprange=(0, 3), xlocator=1, ylocator=1
)
axs.format(
ytickloc='both', yticklabelloc='both',
titlepad='0.5em', suptitle='Default formatters demo'
Expand Down
32 changes: 24 additions & 8 deletions proplot/axes/cartesian.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
_alt_kwargs = ( # TODO: More systematic approach?
'label', 'locator', 'formatter', 'ticks', 'ticklabels',
'minorlocator', 'minorticks', 'tickminor',
'ticklen', 'tickrange', 'tickdir', 'ticklabeldir', 'tickrotation',
'ticklen', 'tickrange', 'tickdir', 'ticklabeldir', 'tickrotation', 'wraprange',
'bounds', 'margin', 'color', 'linewidth', 'grid', 'gridminor', 'gridcolor',
'locator_kw', 'formatter_kw', 'minorlocator_kw', 'label_kw',
)
Expand Down Expand Up @@ -477,6 +477,7 @@ def format(
xtickminor=None, ytickminor=None,
xticklabeldir=None, yticklabeldir=None,
xtickrange=None, ytickrange=None,
xwraprange=None, ywraprange=None,
xreverse=None, yreverse=None,
xlabel=None, ylabel=None,
xlim=None, ylim=None,
Expand Down Expand Up @@ -595,9 +596,15 @@ def format(
*x* axes.
xtickrange, ytickrange : (float, float), optional
The *x* and *y* axis data ranges within which major tick marks
are labelled. For example, the tick range ``(-1,1)`` with
axis range ``(-5,5)`` and a tick interval of 1 will only
label the ticks marks at -1, 0, and 1.
are labelled. For example, the tick range ``(-1, 1)`` with
axis range ``(-5, 5)`` and a tick interval of 1 will only
label the ticks marks at -1, 0, and 1. See
`~proplot.ticker.AutoFormatter` for details.
xwraprange, ywraprange : (float, float), optional
The *x* and *y* axis data ranges with which major tick mark
values are *wrapped*. For example, the wrap range ``(0, 3)``
causes the values 0 through 9 to be formatted as 0, 1, 2,
0, 1, 2, 0, 1, 2, 0. See `~proplot.ticker.AutoFormatter` for details.
xmargin, ymargin : float, optional
The default margin between plotted content and the *x* and *y* axis
spines. Value is proportional to the width, height of the axes.
Expand Down Expand Up @@ -759,6 +766,7 @@ def format(
tickminor, minorlocator,
lim, reverse, scale,
locator, tickrange,
wraprange,
formatter, tickdir,
ticklabeldir, rotation,
label_kw, scale_kw,
Expand All @@ -776,6 +784,7 @@ def format(
(xtickminor, ytickminor), (xminorlocator, yminorlocator),
(xlim, ylim), (xreverse, yreverse), (xscale, yscale),
(xlocator, ylocator), (xtickrange, ytickrange),
(xwraprange, ywraprange),
(xformatter, yformatter), (xtickdir, ytickdir),
(xticklabeldir, yticklabeldir), (xrotation, yrotation),
(xlabel_kw, ylabel_kw), (xscale_kw, yscale_kw),
Expand Down Expand Up @@ -1073,17 +1082,24 @@ def format(
# NOTE: The only reliable way to disable ticks labels and then
# restore them is by messing with the *formatter*, rather than
# setting labelleft=False, labelright=False, etc.
if formatter is not None or tickrange is not None:
if (
formatter is not None
or tickrange is not None
or wraprange is not None
):
# Tick range
if tickrange is not None:
if tickrange is not None or wraprange is not None:
if formatter not in (None, 'auto'):
warnings._warn_proplot(
'The tickrange feature requires '
'The tickrange and autorange features require '
'proplot.AutoFormatter formatter. Overriding '
'input formatter.'
)
formatter = 'auto'
formatter_kw.setdefault('tickrange', tickrange)
if tickrange is not None:
formatter_kw.setdefault('tickrange', tickrange)
if wraprange is not None:
formatter_kw.setdefault('wraprange', wraprange)

# Set the formatter
# Note some formatters require 'locator' as keyword arg
Expand Down
10 changes: 5 additions & 5 deletions proplot/constructor.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,7 @@
FORMATTERS = { # note default LogFormatter uses ugly e+00 notation
'auto': pticker.AutoFormatter,
'frac': pticker.FracFormatter,
'sci': pticker.SciFormatter,
'sigfig': pticker.SigFigFormatter,
'simple': pticker.SimpleFormatter,
'date': mdates.AutoDateFormatter,
Expand All @@ -124,9 +125,7 @@
'func': mticker.FuncFormatter,
'strmethod': mticker.StrMethodFormatter,
'formatstr': mticker.FormatStrFormatter,
'log': mticker.LogFormatterSciNotation,
'sci': mticker.LogFormatterSciNotation,
'math': mticker.LogFormatterMathtext,
'log': mticker.LogFormatterSciNotation, # NOTE: this is subclass of Mathtext class
'logit': mticker.LogitFormatter,
'eng': mticker.EngFormatter,
'percent': mticker.PercentFormatter,
Expand All @@ -140,6 +139,7 @@
'deglon': partial(pticker.SimpleFormatter, negpos='WE', suffix='\N{DEGREE SIGN}', wraprange=(-180, 180)), # noqa: E501
'dmslon': partial(pticker._LongitudeFormatter, dms=True),
'dmslat': partial(pticker._LatitudeFormatter, dms=True),
'math': mticker.LogFormatterMathtext, # deprecated (use SciNotation subclass)
}
if hasattr(mdates, 'ConciseDateFormatter'):
FORMATTERS['concise'] = mdates.ConciseDateFormatter
Expand Down Expand Up @@ -1007,6 +1007,7 @@ def Formatter(formatter, *args, date=False, index=False, **kwargs):
====================== ============================================== ===============================================================
``'null'``, ``'none'`` `~matplotlib.ticker.NullFormatter` No tick labels
``'auto'`` `~proplot.ticker.AutoFormatter` New default tick labels for axes
``'sci'`` `~proplot.ticker.SciFormatter` Format ticks with scientific notation.
``'simple'`` `~proplot.ticker.SimpleFormatter` New default tick labels for e.g. contour labels
``'sigfig'`` `~proplot.ticker.SigFigFormatter` Format labels using the first ``N`` significant digits
``'frac'`` `~proplot.ticker.FracFormatter` Rational fractions
Expand All @@ -1018,9 +1019,8 @@ def Formatter(formatter, *args, date=False, index=False, **kwargs):
``'formatstr'`` `~matplotlib.ticker.FormatStrFormatter` From C-style ``string % format`` notation
``'func'`` `~matplotlib.ticker.FuncFormatter` Use an arbitrary function
``'index'`` `~matplotlib.ticker.IndexFormatter` List of strings corresponding to non-negative integer positions
``'log'``, ``'sci'`` `~matplotlib.ticker.LogFormatterSciNotation` For log-scale axes with scientific notation
``'log'`` `~matplotlib.ticker.LogFormatterSciNotation` For log-scale axes with scientific notation
``'logit'`` `~matplotlib.ticker.LogitFormatter` For logistic-scale axes
``'math'`` `~matplotlib.ticker.LogFormatterMathtext` For log-scale axes with math text
``'percent'`` `~matplotlib.ticker.PercentFormatter` Trailing percent sign
``'scalar'`` `~matplotlib.ticker.ScalarFormatter` Old default tick labels for axes
``'strmethod'`` `~matplotlib.ticker.StrMethodFormatter` From the ``string.format`` method
Expand Down
65 changes: 62 additions & 3 deletions proplot/ticker.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
'FracFormatter',
'LongitudeLocator',
'LatitudeLocator',
'SciFormatter',
'SigFigFormatter',
'SimpleFormatter',
]
Expand Down Expand Up @@ -250,6 +251,13 @@ def __call__(self, x, pos=None):
string = string + tail # add negative-positive indicator
return string

def get_offset(self):
"""
Get the offset but *always* use math text.
"""
with _set_state(self, _useMathText=True):
return super().get_offset()

@staticmethod
def _add_prefix_suffix(string, prefix=None, suffix=None):
"""
Expand Down Expand Up @@ -384,7 +392,55 @@ def _wrap_tick_range(x, wraprange):
return (x - base) % modulus + base


def SigFigFormatter(sigfig=1, zerotrim=None):
def SciFormatter(precision=None, zerotrim=None):
"""
Return a `~matplotlib.ticker.FuncFormatter` that formats
the number with scientific notation.

Parameters
----------
precision : int, optional
The maximum number of digits after the decimal point. Default is ``6``
when `zerotrim` is ``True`` and ``2`` otherwise.
zerotrim : bool, optional
Whether to trim trailing zeros. Default is
"""
from .config import rc
zerotrim = _not_none(zerotrim, rc['formatter.zerotrim'])
if precision is None:
precision = 6 if zerotrim else 2

def func(x, pos):
# Get string
decimal_point = AutoFormatter._get_default_decimal_point()
string = ('{:.%de}' % precision).format(x)
parts = string.split('e')

# Trim trailing zeros
significand = parts[0].rstrip(decimal_point)
if zerotrim:
significand = AutoFormatter._trim_trailing_zeros(significand, decimal_point)

# Get sign and exponent
sign = parts[1][0].replace('+', '')
exponent = parts[1][1:].lstrip('0')
if exponent:
exponent = f'10^{{{sign}{exponent}}}'
if significand and exponent:
string = rf'{significand}{{\times}}{exponent}'
else:
string = rf'{significand}{exponent}'

# Ensure unicode minus sign
string = AutoFormatter._minus_format(string)

# Return TeX string
return f'${string}$'

return mticker.FuncFormatter(func)


def SigFigFormatter(sigfig=3, zerotrim=None):
"""
Return a `~matplotlib.ticker.FuncFormatter` that rounds numbers
to the specified number of *significant digits*.
Expand Down Expand Up @@ -422,7 +478,7 @@ def func(x, pos):

@docstring.add_snippets
def SimpleFormatter(
precision=6, zerotrim=None, tickrange=None, wraprange=None,
precision=None, zerotrim=None, tickrange=None, wraprange=None,
prefix=None, suffix=None, negpos=None,
):
"""
Expand All @@ -434,11 +490,14 @@ def SimpleFormatter(
Parameters
----------
precision : int, optional
The maximum number of digits after the decimal point. Default is ``6``.
The maximum number of digits after the decimal point. Default is ``6``
when `zerotrim` is ``True`` and ``2`` otherwise.
%(formatter.params)s
"""
from .config import rc
zerotrim = _not_none(zerotrim, rc['formatter.zerotrim'])
if precision is None:
precision = 6 if zerotrim else 2
tickrange = tickrange or (-np.inf, np.inf)
prefix = prefix or ''
suffix = suffix or ''
Expand Down