Skip to content

Logit scale nonsingular #14928

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 4 commits into from
Sep 10, 2019
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
39 changes: 39 additions & 0 deletions lib/matplotlib/tests/test_ticker.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import warnings
import re
import itertools

import numpy as np
from numpy.testing import assert_almost_equal, assert_array_equal
Expand Down Expand Up @@ -366,6 +367,44 @@ def test_minor_attr(self):
loc.set_params(minor=False)
assert not loc.minor

acceptable_vmin_vmax = [
*(2.5 ** np.arange(-3, 0)),
*(1 - 2.5 ** np.arange(-3, 0)),
]

@pytest.mark.parametrize(
"lims",
[
(a, b)
for (a, b) in itertools.product(acceptable_vmin_vmax, repeat=2)
if a != b
],
)
def test_nonsingular_ok(self, lims):
"""
Create logit locator, and test the nonsingular method for acceptable
value
"""
loc = mticker.LogitLocator()
lims2 = loc.nonsingular(*lims)
assert sorted(lims) == sorted(lims2)

@pytest.mark.parametrize("okval", acceptable_vmin_vmax)
def test_nonsingular_nok(self, okval):
"""
Create logit locator, and test the nonsingular method for non
acceptable value
"""
loc = mticker.LogitLocator()
vmin, vmax = (-1, okval)
vmin2, vmax2 = loc.nonsingular(vmin, vmax)
assert vmax2 == vmax
assert 0 < vmin2 < vmax2
vmin, vmax = (okval, 2)
vmin2, vmax2 = loc.nonsingular(vmin, vmax)
assert vmin2 == vmin
assert vmin2 < vmax2 < 1


class TestFixedLocator:
def test_set_params(self):
Expand Down
52 changes: 29 additions & 23 deletions lib/matplotlib/ticker.py
Original file line number Diff line number Diff line change
Expand Up @@ -2792,32 +2792,38 @@ def ideal_ticks(x):
return MaxNLocator.tick_values(self, vmin, vmax)

def nonsingular(self, vmin, vmax):
initial_range = (1e-7, 1 - 1e-7)
if not np.isfinite(vmin) or not np.isfinite(vmax):
return initial_range # no data plotted yet

standard_minpos = 1e-7
initial_range = (standard_minpos, 1 - standard_minpos)
if vmin > vmax:
vmin, vmax = vmax, vmin

# what to do if a window beyond ]0, 1[ is chosen
if self.axis is not None:
minpos = self.axis.get_minpos()
if not np.isfinite(minpos):
return initial_range # again, no data plotted
if not np.isfinite(vmin) or not np.isfinite(vmax):
vmin, vmax = initial_range # Initial range, no data plotted yet.
elif vmax <= 0 or vmin >= 1:
# vmax <= 0 occurs when all values are negative
# vmin >= 1 occurs when all values are greater than one
cbook._warn_external(
"Data has no values between 0 and 1, and therefore cannot be "
"logit-scaled."
)
vmin, vmax = initial_range
else:
minpos = 1e-7 # should not occur in normal use

# NOTE: for vmax, we should query a property similar to get_minpos, but
# related to the maximal, less-than-one data point. Unfortunately,
# Bbox._minpos is defined very deep in the BBox and updated with data,
# so for now we use 1 - minpos as a substitute.

if vmin <= 0:
vmin = minpos
if vmax >= 1:
vmax = 1 - minpos
if vmin == vmax:
return 0.1 * vmin, 1 - 0.1 * vmin
minpos = (
self.axis.get_minpos()
if self.axis is not None
else standard_minpos
)
if not np.isfinite(minpos):
minpos = standard_minpos # This should never take effect.
if vmin <= 0:
vmin = minpos
# NOTE: for vmax, we should query a property similar to get_minpos,
# but related to the maximal, less-than-one data point.
# Unfortunately, Bbox._minpos is defined very deep in the BBox and
# updated with data, so for now we use 1 - minpos as a substitute.
if vmax >= 1:
vmax = 1 - minpos
if vmin == vmax:
vmin, vmax = 0.1 * vmin, 1 - 0.1 * vmin

return vmin, vmax

Expand Down