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

In LogTransform, clip after log, not before.#9477

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
tacaswell merged 7 commits intomatplotlib:masterfromanntzer:clip-log-further
Oct 25, 2017
Merged
Show file tree
Hide file tree
Changes fromall 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 deletionsdoc/api/api_changes.rst
View file
Open in desktop
Original file line numberDiff line numberDiff 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 deletionslib/matplotlib/axes/_axes.py
View file
Open in desktop
Original file line numberDiff line numberDiff 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 DownExpand 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 deletionslib/matplotlib/axes/_base.py
View file
Open in desktop
Original file line numberDiff line numberDiff 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 DownExpand 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 deletionslib/matplotlib/scale.py
View file
Open in desktop
Original file line numberDiff line numberDiff 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 DownExpand 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))
Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

just format the entire string? no need to add, also no need for!r asrepr(dict) == format(dict, "")

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

If you try to format the whole string it blows up on a key error with "'basex', 'subsx', nonposx'".

Copy link
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

"{" -> "{{".
If you think it's not an improvement, then at least replace "{!r}.format(...)" by repr(...), no? seems a bit perverse otherwise :)


if nonpos not in ['mask', 'clip']:
raise ValueError("nonposx, nonposy kwarg must be 'mask' or 'clip'")
Expand DownExpand 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
View file
Open in desktop
Loading
Sorry, something went wrong.Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View file
Open in desktop
Loading
Sorry, something went wrong.Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View file
Open in desktop
Loading
Sorry, something went wrong.Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View file
Open in desktop
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 deletionslib/matplotlib/tests/test_path.py
View file
Open in desktop
Original file line numberDiff line numberDiff 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 deletionslib/matplotlib/tests/test_scale.py
View file
Open in desktop
Original file line numberDiff line numberDiff 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 DownExpand 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')

[8]ページ先頭

©2009-2025 Movatter.jp