Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Backport PR #9477 on branch v2.1.x #9576

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 26, 2017
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
15 changes: 15 additions & 0 deletions doc/api/api_changes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,24 @@ out what caused the breakage and how to fix it by updating your code.
For new features that were added to Matplotlib, please see
:ref:`whats-new`.

API Changes in 2.1.1
====================

Default behavior of log scales reverted to clip <= 0 values
-----------------------------------------------------------

The change it 2.1.0 to mask in logscale by default had more disruptive
changes than anticipated and has been reverted, however the clipping is now
done in a way that fixes the issues that motivated changing the default behavior
to ``'mask'``.

As a side effect of this change, error bars which go negative now work as expected
on log scales.

API Changes in 2.1.0
====================


Default behavior of log scales changed to mask <= 0 values
----------------------------------------------------------

Expand Down
7 changes: 0 additions & 7 deletions lib/matplotlib/axes/_axes.py
Original file line number Diff line number Diff line change
Expand Up @@ -1556,11 +1556,9 @@ def loglog(self, *args, **kwargs):

dx = {'basex': kwargs.pop('basex', 10),
'subsx': kwargs.pop('subsx', None),
'nonposx': kwargs.pop('nonposx', 'mask'),
}
dy = {'basey': kwargs.pop('basey', 10),
'subsy': kwargs.pop('subsy', None),
'nonposy': kwargs.pop('nonposy', 'mask'),
}

self.set_xscale('log', **dx)
Expand Down Expand Up @@ -2851,11 +2849,6 @@ def errorbar(self, x, y, yerr=None, xerr=None,
Valid kwargs for the marker properties are

%(Line2D)s

Notes
-----
Error bars with negative values will not be shown when plotted on a
logarithmic axis.
"""
kwargs = cbook.normalize_kwargs(kwargs, _alias_map)
# anything that comes in as 'None', drop so the default thing
Expand Down
10 changes: 0 additions & 10 deletions lib/matplotlib/axes/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -2970,11 +2970,6 @@ def set_xscale(self, value, **kwargs):

matplotlib.scale.LogisticTransform : logit transform
"""
# If the scale is being set to log, mask nonposx to prevent headaches
# around zero
if value.lower() == 'log' and 'nonposx' not in kwargs:
kwargs['nonposx'] = 'mask'

g = self.get_shared_x_axes()
for ax in g.get_siblings(self):
ax.xaxis._set_scale(value, **kwargs)
Expand Down Expand Up @@ -3292,11 +3287,6 @@ def set_yscale(self, value, **kwargs):

matplotlib.scale.LogisticTransform : logit transform
"""
# If the scale is being set to log, mask nonposy to prevent headaches
# around zero
if value.lower() == 'log' and 'nonposy' not in kwargs:
kwargs['nonposy'] = 'mask'

g = self.get_shared_y_axes()
for ax in g.get_siblings(self):
ax.yaxis._set_scale(value, **kwargs)
Expand Down
48 changes: 31 additions & 17 deletions lib/matplotlib/scale.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,15 +92,24 @@ class LogTransformBase(Transform):

def __init__(self, nonpos):
Transform.__init__(self)
if nonpos == 'mask':
self._fill_value = np.nan
else:
self._fill_value = 1e-300
self._clip = {"clip": True, "mask": False}[nonpos]

def transform_non_affine(self, a):
with np.errstate(invalid="ignore"):
a = np.where(a <= 0, self._fill_value, a)
return np.divide(np.log(a, out=a), np.log(self.base), out=a)
with np.errstate(divide="ignore", invalid="ignore"):
out = np.log(a)
out /= np.log(self.base)
if self._clip:
# SVG spec says that conforming viewers must support values up
# to 3.4e38 (C float); however experiments suggest that Inkscape
# (which uses cairo for rendering) runs into cairo's 24-bit limit
# (which is apparently shared by Agg).
# Ghostscript (used for pdf rendering appears to overflow even
# earlier, with the max value around 2 ** 15 for the tests to pass.
# On the other hand, in practice, we want to clip beyond
# np.log10(np.nextafter(0, 1)) ~ -323
# so 1000 seems safe.
out[a <= 0] = -1000
return out


class InvertedLogTransformBase(Transform):
Expand Down Expand Up @@ -220,11 +229,17 @@ def __init__(self, axis, **kwargs):
if axis.axis_name == 'x':
base = kwargs.pop('basex', 10.0)
subs = kwargs.pop('subsx', None)
nonpos = kwargs.pop('nonposx', 'mask')
nonpos = kwargs.pop('nonposx', 'clip')
else:
base = kwargs.pop('basey', 10.0)
subs = kwargs.pop('subsy', None)
nonpos = kwargs.pop('nonposy', 'mask')
nonpos = kwargs.pop('nonposy', 'clip')

if len(kwargs):
raise ValueError(("provided too many kwargs, can only pass "
"{'basex', 'subsx', nonposx'} or "
"{'basey', 'subsy', nonposy'}. You passed ") +
"{!r}".format(kwargs))

if nonpos not in ['mask', 'clip']:
raise ValueError("nonposx, nonposy kwarg must be 'mask' or 'clip'")
Expand Down Expand Up @@ -432,18 +447,17 @@ class LogitTransform(Transform):

def __init__(self, nonpos):
Transform.__init__(self)
if nonpos == 'mask':
self._fill_value = np.nan
else:
self._fill_value = 1e-300
self._nonpos = nonpos
self._clip = {"clip": True, "mask": False}[nonpos]

def transform_non_affine(self, a):
"""logit transform (base 10), masked or clipped"""
with np.errstate(invalid="ignore"):
a = np.select(
[a <= 0, a >= 1], [self._fill_value, 1 - self._fill_value], a)
return np.log10(a / (1 - a))
with np.errstate(divide="ignore", invalid="ignore"):
out = np.log10(a / (1 - a))
if self._clip: # See LogTransform for choice of clip value.
out[a <= 0] = -1000
out[1 <= a] = 1000
return out

def inverted(self):
return LogisticTransform(self._nonpos)
Expand Down
Binary file modified lib/matplotlib/tests/baseline_images/test_axes/log_scales.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 4 additions & 2 deletions lib/matplotlib/tests/test_path.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,15 @@ def test_path_clipping():
xy, facecolor='none', edgecolor='red', closed=True))


@image_comparison(baseline_images=['semi_log_with_zero'], extensions=['png'])
@image_comparison(baseline_images=['semi_log_with_zero'], extensions=['png'],
style='mpl20')
def test_log_transform_with_zero():
x = np.arange(-10, 10)
y = (1.0 - 1.0/(x**2+1))**20

fig, ax = plt.subplots()
ax.semilogy(x, y, "-o", lw=15)
ax.semilogy(x, y, "-o", lw=15, markeredgecolor='k')
ax.set_ylim(1e-7, 1)
ax.grid(True)


Expand Down
34 changes: 34 additions & 0 deletions lib/matplotlib/tests/test_scale.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import matplotlib.pyplot as plt
import numpy as np
import io
import pytest


@image_comparison(baseline_images=['log_scales'], remove_text=True)
Expand Down Expand Up @@ -65,3 +66,36 @@ def test_logscale_mask():
ax.plot(np.exp(-xs**2))
fig.canvas.draw()
ax.set(yscale="log")


def test_extra_kwargs_raise():
fig, ax = plt.subplots()
with pytest.raises(ValueError):
ax.set_yscale('log', nonpos='mask')


@image_comparison(baseline_images=['logscale_nonpos_values'], remove_text=True,
extensions=['png'], style='mpl20')
def test_logscale_nonpos_values():
np.random.seed(19680801)
xs = np.random.normal(size=int(1e3))
fig, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2)
ax1.hist(xs, range=(-5, 5), bins=10)
ax1.set_yscale('log')
ax2.hist(xs, range=(-5, 5), bins=10)
ax2.set_yscale('log', nonposy='mask')

xdata = np.arange(0, 10, 0.01)
ydata = np.exp(-xdata)
edata = 0.2*(10-xdata)*np.cos(5*xdata)*np.exp(-xdata)

ax3.fill_between(xdata, ydata - edata, ydata + edata)
ax3.set_yscale('log')

x = np.logspace(-1, 1)
y = x ** 3
yerr = x**2
ax4.errorbar(x, y, yerr=yerr)

ax4.set_yscale('log')
ax4.set_xscale('log')