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

Skip to content

Commit cfb27b3

Browse files
committed
FIX: show bars when the first location is nan
Due to the way we handle units on the bar width having an invalid value in the first position of the x bar (of y of barh) would effectively poison all of the widths making all of the bars invisible. This also renames the cbook function _safe_first_non_none function -> _safe_first_finite and adjusts the behavior to also drop nans closes #23687
1 parent 01e916d commit cfb27b3

File tree

6 files changed

+51
-16
lines changed

6 files changed

+51
-16
lines changed

lib/matplotlib/axes/_axes.py

Lines changed: 7 additions & 7 deletions
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

@@ -2801,11 +2801,11 @@ def broken_barh(self, xranges, yrange, **kwargs):
28012801
"""
28022802
# process the unit information
28032803
if len(xranges):
2804-
xdata = cbook._safe_first_non_none(xranges)
2804+
xdata = cbook._safe_first_finite(xranges)
28052805
else:
28062806
xdata = None
28072807
if len(yrange):
2808-
ydata = cbook._safe_first_non_none(yrange)
2808+
ydata = cbook._safe_first_finite(yrange)
28092809
else:
28102810
ydata = None
28112811
self._process_unit_info(
@@ -3447,10 +3447,10 @@ def _upcast_err(err):
34473447
# safe_first_element because getitem is index-first not
34483448
# location first on pandas objects so err[0] almost always
34493449
# fails.
3450-
isinstance(cbook._safe_first_non_none(err), np.ndarray)
3450+
isinstance(cbook._safe_first_finite(err), np.ndarray)
34513451
):
34523452
# Get the type of the first element
3453-
atype = type(cbook._safe_first_non_none(err))
3453+
atype = type(cbook._safe_first_finite(err))
34543454
# Promote the outer container to match the inner container
34553455
if atype is np.ndarray:
34563456
# Converts using np.asarray, because data cannot
@@ -4313,7 +4313,7 @@ def _parse_scatter_color_args(c, edgecolors, kwargs, xsize,
43134313
c_is_string_or_strings = (
43144314
isinstance(c, str)
43154315
or (np.iterable(c) and len(c) > 0
4316-
and isinstance(cbook._safe_first_non_none(c), str)))
4316+
and isinstance(cbook._safe_first_finite(c), str)))
43174317

43184318
def invalid_shape_exception(csize, xsize):
43194319
return ValueError(

lib/matplotlib/cbook/__init__.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1703,10 +1703,10 @@ 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
"""
17111711
Return the first non-None element in *obj*.
17121712
This is a method for internal use.
@@ -1715,7 +1715,14 @@ def _safe_first_non_none(obj, skip_none=True):
17151715
supporting both index access and the iterator protocol.
17161716
The first non-None element will be obtained when skip_none is True.
17171717
"""
1718-
if skip_none is False:
1718+
def safe_isfinite(val):
1719+
try:
1720+
return np.isfinite(val) if np.isscalar(val) else True
1721+
except TypeError:
1722+
# This is something that numpy can not make heads or tails
1723+
# of, assume "finite"
1724+
return True
1725+
if skip_nonfinite is False:
17191726
if isinstance(obj, collections.abc.Iterator):
17201727
# needed to accept `array.flat` as input.
17211728
# np.flatiter reports as an instance of collections.Iterator
@@ -1730,12 +1737,16 @@ def _safe_first_non_none(obj, skip_none=True):
17301737
"as input")
17311738
return next(iter(obj))
17321739
elif isinstance(obj, np.flatiter):
1740+
# TODO do the finite filtering on this
17331741
return obj[0]
17341742
elif isinstance(obj, collections.abc.Iterator):
17351743
raise RuntimeError("matplotlib does not "
17361744
"support generators as input")
17371745
else:
1738-
return next(val for val in obj if val is not None)
1746+
return next(
1747+
val for val in obj
1748+
if val is not None and safe_isfinite(val)
1749+
)
17391750

17401751

17411752
def sanitize_sequence(data):

lib/matplotlib/dates.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1908,7 +1908,7 @@ def default_units(x, axis):
19081908
x = x.ravel()
19091909

19101910
try:
1911-
x = cbook._safe_first_non_none(x)
1911+
x = cbook._safe_first_finite(x)
19121912
except (TypeError, StopIteration):
19131913
pass
19141914

lib/matplotlib/tests/test_axes.py

Lines changed: 24 additions & 0 deletions
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

Lines changed: 3 additions & 3 deletions
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

Lines changed: 1 addition & 1 deletion
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)