Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork7.9k
Description
Bug summary
Thelinscale
parameter tomatplotlib.scale.SymmetricalLogScale
does not behave as described in itsdocumentation.
Code for reproduction
importmatplotlibasmplimportmatplotlib.pyplotaspltimportnumpyasnpbase=10linthresh=2linscale=5symlog=mpl.scale.SymmetricalLogTransform(base=base,linthresh=linthresh,linscale=linscale,).transform_non_affinedefexpected_transform_a(a):abs_a=np.abs(a)withnp.errstate(divide="ignore",invalid="ignore"):out=np.sign(a)* (linscale+np.log(abs_a/linthresh)/np.log(base))inside=abs_a<=linthreshout[inside]=a[inside]/linthresh*linscalereturnoutdefexpected_transform_b(a):abs_a=np.abs(a)withnp.errstate(divide="ignore",invalid="ignore"):out= (np.sign(a)*linthresh* (linscale+np.log(abs_a/linthresh)/np.log(base)) )inside=abs_a<=linthreshout[inside]=a[inside]*linscalereturnoutxvals=np.linspace(0,30,150)fig, (ax0,ax1,ax2)=plt.subplots(3,1)fig.tight_layout()forax,transformin [ (ax0,symlog), (ax1,expected_transform_a), (ax2,expected_transform_b),]:ax.set_title(transform.__name__)ax.plot(xvals,transform(xvals))checkpoints=np.array([linthresh*base**iforiinrange(2)])checky=transform(checkpoints)ax.scatter(checkpoints,checky)forx,yinzip(checkpoints,checky):ax.annotate(f"({x},{y:.2f})", (x,y),xytext=(10,-10),textcoords="offset points", )plt.savefig("symlog_example.png")plt.show()
Actual outcome
Expected outcome
The value ofsymlog(linthresh)
should be equal tolinscale*(symlog(base*linthresh) - symlog(linthresh))
, as is the case in the lower two subplots.
Additional information
The documentation states
linscale: float, optional
This allows the linear range
(-linthresh, linthresh)
to be stretched relative to the logarithmic range. Its value is the number of decades to use for each half of the linear range. For example, when linscale == 1.0 (the default), the space used for the positive and negative halves of the linear range will be equal to one decade in the logarithmic range.
The behavior is incorrect for any value ofbase
,linthresh
andlinscale
, including the default valuesbase=10, linthresh=2, linscale=1
.
The bug is in the definition of thetransform_non_affine
method ofmatplotlib.scale.SymmetricalLogTransform
:https://github.com/matplotlib/matplotlib/blob/v3.7.2/lib/matplotlib/scale.py#L363
Replacing that definition with eitherexpected_transform_a
orexpected_transform_b
fixes the bug.expected_transform_a
is preferable, as it makes the parameterbase
have the mathematically correct meaning: whenever the argumentx
is multiplied by a factor ofbase
,expected_transform_a(x)
increases by 1.
The variantexpected_transform_b
is closer to the current, in my opinion unintuitive, behavior ofSymmetricalLogTransform
, in thatexpected_transform_b(x)
increases bylinthresh
whenx
is multiplied bybase
.
Operating system
Arch
Matplotlib Version
3.7.2
Matplotlib Backend
TkAgg
Python version
3.11.3
Jupyter version
No response
Installation
pip