Skip to content

Commit

Permalink
DOC: Explain parameters linthresh and linscale of symlog scale
Browse files Browse the repository at this point in the history
  • Loading branch information
timhoffm committed Dec 19, 2024
1 parent f8900ea commit 5835396
Show file tree
Hide file tree
Showing 2 changed files with 84 additions and 7 deletions.
89 changes: 82 additions & 7 deletions galleries/examples/scales/symlog_demo.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
"""
===========
Symlog Demo
===========
============
Symlog scale
============
The symmetric logarithmic scale is an extension of the logarithmic scale that
also covers negative values. As with the logarithmic scale, it is particularly
useful for numerical data that spans a broad range of values, especially when there
are significant differences between the magnitudes of the numbers involved.
Example use of symlog (symmetric log) axis scaling.
"""
Expand Down Expand Up @@ -34,11 +39,81 @@
plt.show()

# %%
# It should be noted that the coordinate transform used by ``symlog``
# has a discontinuous gradient at the transition between its linear
# and logarithmic regions. The ``asinh`` axis scale is an alternative
# technique that may avoid visual artifacts caused by these discontinuities.
# Linear threshold
# ----------------
# Since each decade on a logarithmic scale covers the same amount of visual space
# and there are infinitely many decades between a given number and zero, the symlog
# scale must deviate from logarithmic mapping in a small range (-x0, x0), so that
# that range is mapped to a finite visual space.
#
# The symlog scale achieves this by defining a parameter *linthresh* and switching
# to a linear mapping in the region *(-linthresh, linthresh)*.

def format_axes(ax, title=None):
"""A helper function to better visualize properties of the symlog scale."""
ax.xaxis.get_minor_locator().set_params(subs=[2, 3, 4, 5, 6, 7, 8, 9])
ax.grid()
ax.xaxis.grid(which='minor') # minor grid on too
linthresh = ax.xaxis.get_transform().linthresh
linscale = ax.xaxis.get_transform().linscale
ax.axvspan(-linthresh, linthresh, color='0.9')
if title:
ax.set_title(title.format(linthresh=linthresh, linscale=linscale))

x = np.linspace(-60, 60, 201)
y = np.linspace(0, 100.0, 201)

fig, (ax1, ax2) = plt.subplots(nrows=2, layout="constrained")

ax1.plot(x, y)
ax1.set_xscale('symlog', linthresh=1)
format_axes(ax1, title='Linear region: linthresh={linthresh}')

ax2.plot(x, y)
ax2.set_xscale('symlog', linthresh=5)
format_axes(ax2, title='Linear region: linthresh={linthresh}')

# %%
# Generally, *linthresh* should be chosen so that no or only a few
# data points are in the linear region. As a rule of thumb,
# :math:`linthresh \approx min(abs(data))`.
#
#
# Linear scale
# ------------
# Additionally, the *linscale* parameter determines how much visual space should be
# used for the linear range. More precisely, it defines the ratio of visual space
# of the region (0, linthresh) relative to one decade.

fig, (ax1, ax2) = plt.subplots(nrows=2, layout="constrained")

ax1.plot(x, y)
ax1.set_xscale('symlog', linthresh=1)
format_axes(ax1, title='Linear region: linthresh={linthresh}, linscale={linscale}')

ax2.plot(x, y)
ax2.set_xscale('symlog', linthresh=1, linscale=0.1)
format_axes(ax2, title='Linear region: linthresh={linthresh}, linscale={linscale}')

# The suitable value for linscale depends on the dynamic range of data. As most data
# will be outside the linear region, you typically the linear region only to cover
# a small fraction of the visual area.
#
# Limitations and alternatives
# ----------------------------
# The coordinate transform used by ``symlog`` has a discontinuous gradient at the
# transition between its linear and logarithmic regions. Depending on data and
# scaling, this will be more or less obvious in the plot.

fig, ax = plt.subplots()
ax.plot(x, y)
ax.set_xscale('symlog', linscale=0.05)
format_axes(ax, title="Discontinuous gradient at linear/log transition")

# The ``asinh`` axis scale is an alternative transfromation that supports a wide
# dynamic range with a smooth gradient and thus may avoid such visual artifacts.
# See :doc:`/gallery/scales/assinh_demo`.
#
# %%
#
# .. admonition:: References
Expand Down
2 changes: 2 additions & 0 deletions lib/matplotlib/scale.py
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,8 @@ class SymmetricalLogScale(ScaleBase):
*linthresh* allows the user to specify the size of this range
(-*linthresh*, *linthresh*).
See :doc:`/gallery/scales/symlog_demo` for a detailed description.
Parameters
----------
base : float, default: 10
Expand Down

0 comments on commit 5835396

Please sign in to comment.