Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit98184f4

Browse files
authored
Merge pull request#21178 from rwpenney/feature/asinh-scale
Add asinh axis scaling (*smooth* symmetric logscale)
2 parentsdaaa231 +e43cbfd commit98184f4

File tree

11 files changed

+684
-20
lines changed

11 files changed

+684
-20
lines changed

‎doc/api/colors_api.rst‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ Classes
2121
:toctree: _as_gen/
2222
:template: autosummary.rst
2323

24+
AsinhNorm
2425
BoundaryNorm
2526
Colormap
2627
CenteredNorm
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
New axis scale ``asinh`` (experimental)
2+
---------------------------------------
3+
4+
The new ``asinh`` axis scale offers an alternative to ``symlog`` that
5+
smoothly transitions between the quasi-linear and asymptotically logarithmic
6+
regions of the scale. This is based on an arcsinh transformation that
7+
allows plotting both positive and negative values that span many orders
8+
of magnitude.
9+
10+
..plot::
11+
12+
import matplotlib.pyplot as plt
13+
import numpy as np
14+
15+
fig, (ax0, ax1) = plt.subplots(1, 2, sharex=True)
16+
x = np.linspace(-3, 6, 100)
17+
18+
ax0.plot(x, x)
19+
ax0.set_yscale('symlog')
20+
ax0.grid()
21+
ax0.set_title('symlog')
22+
23+
ax1.plot(x, x)
24+
ax1.set_yscale('asinh')
25+
ax1.grid()
26+
ax1.set_title(r'$sinh^{-1}$')
27+
28+
for p in (-2, 2):
29+
for ax in (ax0, ax1):
30+
c = plt.Circle((p, p), radius=0.5, fill=False,
31+
color='red', alpha=0.8, lw=3)
32+
ax.add_patch(c)
Lines changed: 60 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,84 @@
11
"""
22
==================================
3-
Colormap NormalizationsSymlognorm
3+
Colormap NormalizationsSymLogNorm
44
==================================
55
66
Demonstration of using norm to map colormaps onto data in non-linear ways.
77
88
.. redirect-from:: /gallery/userdemo/colormap_normalization_symlognorm
99
"""
1010

11+
###############################################################################
12+
# Synthetic dataset consisting of two humps, one negative and one positive,
13+
# the positive with 8-times the amplitude.
14+
# Linearly, the negative hump is almost invisible,
15+
# and it is very difficult to see any detail of its profile.
16+
# With the logarithmic scaling applied to both positive and negative values,
17+
# it is much easier to see the shape of each hump.
18+
#
19+
# See `~.colors.SymLogNorm`.
20+
1121
importnumpyasnp
1222
importmatplotlib.pyplotasplt
1323
importmatplotlib.colorsascolors
1424

15-
"""
16-
SymLogNorm: two humps, one negative and one positive, The positive
17-
with 5-times the amplitude. Linearly, you cannot see detail in the
18-
negative hump. Here we logarithmically scale the positive and
19-
negative data separately.
2025

21-
Note that colorbar labels do not come out looking very good.
22-
"""
26+
defrbf(x,y):
27+
return1.0/ (1+5* ((x**2)+ (y**2)))
2328

24-
N=100
29+
N=200
30+
gain=8
2531
X,Y=np.mgrid[-3:3:complex(0,N),-2:2:complex(0,N)]
26-
Z1=np.exp(-X**2-Y**2)
27-
Z2=np.exp(-(X-1)**2- (Y-1)**2)
28-
Z= (Z1-Z2)*2
32+
Z1=rbf(X+0.5,Y+0.5)
33+
Z2=rbf(X-0.5,Y-0.5)
34+
Z=gain*Z1-Z2
35+
36+
shadeopts= {'cmap':'PRGn','shading':'gouraud'}
37+
colormap='PRGn'
38+
lnrwidth=0.5
2939

30-
fig,ax=plt.subplots(2,1)
40+
fig,ax=plt.subplots(2,1,sharex=True,sharey=True)
3141

3242
pcm=ax[0].pcolormesh(X,Y,Z,
33-
norm=colors.SymLogNorm(linthresh=0.03,linscale=0.03,
34-
vmin=-1.0,vmax=1.0,base=10),
35-
cmap='RdBu_r',shading='nearest')
43+
norm=colors.SymLogNorm(linthresh=lnrwidth,linscale=1,
44+
vmin=-gain,vmax=gain,base=10),
45+
**shadeopts)
3646
fig.colorbar(pcm,ax=ax[0],extend='both')
47+
ax[0].text(-2.5,1.5,'symlog')
3748

38-
pcm=ax[1].pcolormesh(X,Y,Z,cmap='RdBu_r',vmin=-np.max(Z),
39-
shading='nearest')
49+
pcm=ax[1].pcolormesh(X,Y,Z,vmin=-gain,vmax=gain,
50+
**shadeopts)
4051
fig.colorbar(pcm,ax=ax[1],extend='both')
52+
ax[1].text(-2.5,1.5,'linear')
53+
54+
55+
###############################################################################
56+
# In order to find the best visualization for any particular dataset,
57+
# it may be necessary to experiment with multiple different color scales.
58+
# As well as the `~.colors.SymLogNorm` scaling, there is also
59+
# the option of using `~.colors.AsinhNorm` (experimental), which has a smoother
60+
# transition between the linear and logarithmic regions of the transformation
61+
# applied to the data values, "Z".
62+
# In the plots below, it may be possible to see contour-like artifacts
63+
# around each hump despite there being no sharp features
64+
# in the dataset itself. The ``asinh`` scaling shows a smoother shading
65+
# of each hump.
66+
67+
fig,ax=plt.subplots(2,1,sharex=True,sharey=True)
68+
69+
pcm=ax[0].pcolormesh(X,Y,Z,
70+
norm=colors.SymLogNorm(linthresh=lnrwidth,linscale=1,
71+
vmin=-gain,vmax=gain,base=10),
72+
**shadeopts)
73+
fig.colorbar(pcm,ax=ax[0],extend='both')
74+
ax[0].text(-2.5,1.5,'symlog')
75+
76+
pcm=ax[1].pcolormesh(X,Y,Z,
77+
norm=colors.AsinhNorm(linear_width=lnrwidth,
78+
vmin=-gain,vmax=gain),
79+
**shadeopts)
80+
fig.colorbar(pcm,ax=ax[1],extend='both')
81+
ax[1].text(-2.5,1.5,'asinh')
82+
4183

4284
plt.show()

‎examples/scales/asinh_demo.py‎

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
"""
2+
============
3+
Asinh Demo
4+
============
5+
6+
Illustration of the `asinh <.scale.AsinhScale>` axis scaling,
7+
which uses the transformation
8+
9+
.. math::
10+
11+
a\\rightarrow a_0\\sinh^{-1} (a / a_0)
12+
13+
For coordinate values close to zero (i.e. much smaller than
14+
the "linear width" :math:`a_0`), this leaves values essentially unchanged:
15+
16+
.. math::
17+
18+
a\\rightarrow a + {\\cal O}(a^3)
19+
20+
but for larger values (i.e. :math:`|a|\\gg a_0`, this is asymptotically
21+
22+
.. math::
23+
24+
a\\rightarrow a_0\\, {\\rm sgn}(a)\\ln |a| + {\\cal O}(1)
25+
26+
As with the `symlog <.scale.SymmetricalLogScale>` scaling,
27+
this allows one to plot quantities
28+
that cover a very wide dynamic range that includes both positive
29+
and negative values. However, ``symlog`` involves a transformation
30+
that has discontinuities in its gradient because it is built
31+
from *separate* linear and logarithmic transformations.
32+
The ``asinh`` scaling uses a transformation that is smooth
33+
for all (finite) values, which is both mathematically cleaner
34+
and reduces visual artifacts associated with an abrupt
35+
transition between linear and logarithmic regions of the plot.
36+
37+
.. note::
38+
`.scale.AsinhScale` is experimental, and the API may change.
39+
40+
See `~.scale.AsinhScale`, `~.scale.SymmetricalLogScale`.
41+
"""
42+
43+
importnumpyasnp
44+
importmatplotlib.pyplotasplt
45+
46+
# Prepare sample values for variations on y=x graph:
47+
x=np.linspace(-3,6,500)
48+
49+
###############################################################################
50+
# Compare "symlog" and "asinh" behaviour on sample y=x graph,
51+
# where there is a discontinuous gradient in "symlog" near y=2:
52+
fig1=plt.figure()
53+
ax0,ax1=fig1.subplots(1,2,sharex=True)
54+
55+
ax0.plot(x,x)
56+
ax0.set_yscale('symlog')
57+
ax0.grid()
58+
ax0.set_title('symlog')
59+
60+
ax1.plot(x,x)
61+
ax1.set_yscale('asinh')
62+
ax1.grid()
63+
ax1.set_title('asinh')
64+
65+
66+
###############################################################################
67+
# Compare "asinh" graphs with different scale parameter "linear_width":
68+
fig2=plt.figure(constrained_layout=True)
69+
axs=fig2.subplots(1,3,sharex=True)
70+
forax, (a0,base)inzip(axs, ((0.2,2), (1.0,0), (5.0,10))):
71+
ax.set_title('linear_width={:.3g}'.format(a0))
72+
ax.plot(x,x,label='y=x')
73+
ax.plot(x,10*x,label='y=10x')
74+
ax.plot(x,100*x,label='y=100x')
75+
ax.set_yscale('asinh',linear_width=a0,base=base)
76+
ax.grid()
77+
ax.legend(loc='best',fontsize='small')
78+
79+
80+
###############################################################################
81+
# Compare "symlog" and "asinh" scalings
82+
# on 2D Cauchy-distributed random numbers,
83+
# where one may be able to see more subtle artifacts near y=2
84+
# due to the gradient-discontinuity in "symlog":
85+
fig3=plt.figure()
86+
ax=fig3.subplots(1,1)
87+
r=3*np.tan(np.random.uniform(-np.pi/2.02,np.pi/2.02,
88+
size=(5000,)))
89+
th=np.random.uniform(0,2*np.pi,size=r.shape)
90+
91+
ax.scatter(r*np.cos(th),r*np.sin(th),s=4,alpha=0.5)
92+
ax.set_xscale('asinh')
93+
ax.set_yscale('symlog')
94+
ax.set_xlabel('asinh')
95+
ax.set_ylabel('symlog')
96+
ax.set_title('2D Cauchy random deviates')
97+
ax.set_xlim(-50,50)
98+
ax.set_ylim(-50,50)
99+
ax.grid()
100+
101+
plt.show()
102+
103+
###############################################################################
104+
#
105+
# .. admonition:: References
106+
#
107+
# - `matplotlib.scale.AsinhScale`
108+
# - `matplotlib.ticker.AsinhLocator`
109+
# - `matplotlib.scale.SymmetricalLogScale`

‎examples/scales/symlog_demo.py‎

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,17 @@
3333

3434
fig.tight_layout()
3535
plt.show()
36+
37+
###############################################################################
38+
# It should be noted that the coordinate transform used by ``symlog``
39+
# has a discontinuous gradient at the transition between its linear
40+
# and logarithmic regions. The ``asinh`` axis scale is an alternative
41+
# technique that may avoid visual artifacts caused by these disconinuities.
42+
43+
###############################################################################
44+
#
45+
# .. admonition:: References
46+
#
47+
# - `matplotlib.scale.SymmetricalLogScale`
48+
# - `matplotlib.ticker.SymmetricalLogLocator`
49+
# - `matplotlib.scale.AsinhScale`

‎lib/matplotlib/colors.py‎

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1686,6 +1686,38 @@ def linthresh(self, value):
16861686
self._scale.linthresh=value
16871687

16881688

1689+
@make_norm_from_scale(
1690+
scale.AsinhScale,
1691+
init=lambdalinear_width=1,vmin=None,vmax=None,clip=False:None)
1692+
classAsinhNorm(Normalize):
1693+
"""
1694+
The inverse hyperbolic sine scale is approximately linear near
1695+
the origin, but becomes logarithmic for larger positive
1696+
or negative values. Unlike the `SymLogNorm`, the transition between
1697+
these linear and logarithmic regions is smooth, which may reduce
1698+
the risk of visual artifacts.
1699+
1700+
.. note::
1701+
1702+
This API is provisional and may be revised in the future
1703+
based on early user feedback.
1704+
1705+
Parameters
1706+
----------
1707+
linear_width : float, default: 1
1708+
The effective width of the linear region, beyond which
1709+
the transformation becomes asymptotically logarithmic
1710+
"""
1711+
1712+
@property
1713+
deflinear_width(self):
1714+
returnself._scale.linear_width
1715+
1716+
@linear_width.setter
1717+
deflinear_width(self,value):
1718+
self._scale.linear_width=value
1719+
1720+
16891721
classPowerNorm(Normalize):
16901722
"""
16911723
Linearly map a given value to the 0-1 range and then apply

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp