Skip to content

Commit 65ca31c

Browse files
authored
Merge pull request #23956 from meeseeksmachine/auto-backport-of-pr-23751-on-v3.6.x
Backport PR #23751 on branch v3.6.x (FIX: show bars when the first location is nan)
2 parents f751574 + 7381e20 commit 65ca31c

File tree

6 files changed

+52
-17
lines changed

6 files changed

+52
-17
lines changed

lib/matplotlib/axes/_axes.py

+7-7
Original file line numberDiff line numberDiff line change
@@ -2179,12 +2179,12 @@ def _convert_dx(dx, x0, xconv, convert):
21792179
# removes the units from unit packages like `pint` that
21802180
# wrap numpy arrays.
21812181
try:
2182-
x0 = cbook._safe_first_non_none(x0)
2182+
x0 = cbook._safe_first_finite(x0)
21832183
except (TypeError, IndexError, KeyError):
21842184
pass
21852185

21862186
try:
2187-
x = cbook._safe_first_non_none(xconv)
2187+
x = cbook._safe_first_finite(xconv)
21882188
except (TypeError, IndexError, KeyError):
21892189
x = xconv
21902190

@@ -2813,11 +2813,11 @@ def broken_barh(self, xranges, yrange, **kwargs):
28132813
"""
28142814
# process the unit information
28152815
if len(xranges):
2816-
xdata = cbook._safe_first_non_none(xranges)
2816+
xdata = cbook._safe_first_finite(xranges)
28172817
else:
28182818
xdata = None
28192819
if len(yrange):
2820-
ydata = cbook._safe_first_non_none(yrange)
2820+
ydata = cbook._safe_first_finite(yrange)
28212821
else:
28222822
ydata = None
28232823
self._process_unit_info(
@@ -3459,10 +3459,10 @@ def _upcast_err(err):
34593459
# safe_first_element because getitem is index-first not
34603460
# location first on pandas objects so err[0] almost always
34613461
# fails.
3462-
isinstance(cbook._safe_first_non_none(err), np.ndarray)
3462+
isinstance(cbook._safe_first_finite(err), np.ndarray)
34633463
):
34643464
# Get the type of the first element
3465-
atype = type(cbook._safe_first_non_none(err))
3465+
atype = type(cbook._safe_first_finite(err))
34663466
# Promote the outer container to match the inner container
34673467
if atype is np.ndarray:
34683468
# Converts using np.asarray, because data cannot
@@ -4325,7 +4325,7 @@ def _parse_scatter_color_args(c, edgecolors, kwargs, xsize,
43254325
c_is_string_or_strings = (
43264326
isinstance(c, str)
43274327
or (np.iterable(c) and len(c) > 0
4328-
and isinstance(cbook._safe_first_non_none(c), str)))
4328+
and isinstance(cbook._safe_first_finite(c), str)))
43294329

43304330
def invalid_shape_exception(csize, xsize):
43314331
return ValueError(

lib/matplotlib/cbook/__init__.py

+16-5
Original file line numberDiff line numberDiff line change
@@ -1703,19 +1703,29 @@ def safe_first_element(obj):
17031703
This is an type-independent way of obtaining the first element,
17041704
supporting both index access and the iterator protocol.
17051705
"""
1706-
return _safe_first_non_none(obj, skip_none=False)
1706+
return _safe_first_finite(obj, skip_nonfinite=False)
17071707

17081708

1709-
def _safe_first_non_none(obj, skip_none=True):
1709+
def _safe_first_finite(obj, *, skip_nonfinite=True):
17101710
"""
1711-
Return the first non-None element in *obj*.
1711+
Return the first non-None (and optionally finite) element in *obj*.
1712+
17121713
This is a method for internal use.
17131714
17141715
This is an type-independent way of obtaining the first non-None element,
17151716
supporting both index access and the iterator protocol.
17161717
The first non-None element will be obtained when skip_none is True.
17171718
"""
1718-
if skip_none is False:
1719+
def safe_isfinite(val):
1720+
if val is None:
1721+
return False
1722+
try:
1723+
return np.isfinite(val) if np.isscalar(val) else True
1724+
except TypeError:
1725+
# This is something that numpy can not make heads or tails
1726+
# of, assume "finite"
1727+
return True
1728+
if skip_nonfinite is False:
17191729
if isinstance(obj, collections.abc.Iterator):
17201730
# needed to accept `array.flat` as input.
17211731
# np.flatiter reports as an instance of collections.Iterator
@@ -1730,12 +1740,13 @@ def _safe_first_non_none(obj, skip_none=True):
17301740
"as input")
17311741
return next(iter(obj))
17321742
elif isinstance(obj, np.flatiter):
1743+
# TODO do the finite filtering on this
17331744
return obj[0]
17341745
elif isinstance(obj, collections.abc.Iterator):
17351746
raise RuntimeError("matplotlib does not "
17361747
"support generators as input")
17371748
else:
1738-
return next(val for val in obj if val is not None)
1749+
return next(val for val in obj if safe_isfinite(val))
17391750

17401751

17411752
def sanitize_sequence(data):

lib/matplotlib/dates.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1907,7 +1907,7 @@ def default_units(x, axis):
19071907
x = x.ravel()
19081908

19091909
try:
1910-
x = cbook._safe_first_non_none(x)
1910+
x = cbook._safe_first_finite(x)
19111911
except (TypeError, StopIteration):
19121912
pass
19131913

lib/matplotlib/tests/test_axes.py

+24
Original file line numberDiff line numberDiff line change
@@ -8109,3 +8109,27 @@ def test_get_xticklabel():
81098109
for ind in range(10):
81108110
assert ax.get_xticklabels()[ind].get_text() == f'{ind}'
81118111
assert ax.get_yticklabels()[ind].get_text() == f'{ind}'
8112+
8113+
8114+
def test_bar_leading_nan():
8115+
8116+
barx = np.arange(3, dtype=float)
8117+
barheights = np.array([0.5, 1.5, 2.0])
8118+
barstarts = np.array([0.77]*3)
8119+
8120+
barx[0] = np.NaN
8121+
8122+
fig, ax = plt.subplots()
8123+
8124+
bars = ax.bar(barx, barheights, bottom=barstarts)
8125+
8126+
hbars = ax.barh(barx, barheights, left=barstarts)
8127+
8128+
for bar_set in (bars, hbars):
8129+
# the first bar should have a nan in the location
8130+
nanful, *rest = bar_set
8131+
assert (~np.isfinite(nanful.xy)).any()
8132+
assert np.isfinite(nanful.get_width())
8133+
for b in rest:
8134+
assert np.isfinite(b.xy).all()
8135+
assert np.isfinite(b.get_width())

lib/matplotlib/tests/test_cbook.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -602,7 +602,7 @@ def test_flatiter():
602602
it = x.flat
603603
assert 0 == next(it)
604604
assert 1 == next(it)
605-
ret = cbook._safe_first_non_none(it)
605+
ret = cbook._safe_first_finite(it)
606606
assert ret == 0
607607

608608
assert 0 == next(it)
@@ -758,7 +758,7 @@ def test_contiguous_regions():
758758
def test_safe_first_element_pandas_series(pd):
759759
# deliberately create a pandas series with index not starting from 0
760760
s = pd.Series(range(5), index=range(10, 15))
761-
actual = cbook._safe_first_non_none(s)
761+
actual = cbook._safe_first_finite(s)
762762
assert actual == 0
763763

764764

@@ -893,5 +893,5 @@ def test_format_approx():
893893
def test_safe_first_element_with_none():
894894
datetime_lst = [date.today() + timedelta(days=i) for i in range(10)]
895895
datetime_lst[0] = None
896-
actual = cbook._safe_first_non_none(datetime_lst)
896+
actual = cbook._safe_first_finite(datetime_lst)
897897
assert actual is not None and actual == datetime_lst[1]

lib/matplotlib/units.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ def get_converter(self, x):
197197
except KeyError:
198198
pass
199199
try: # If cache lookup fails, look up based on first element...
200-
first = cbook._safe_first_non_none(x)
200+
first = cbook._safe_first_finite(x)
201201
except (TypeError, StopIteration):
202202
pass
203203
else:

0 commit comments

Comments
 (0)