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

Skip to content

Commit 095830e

Browse files
authored
Merge pull request #14292 from meeseeksmachine/auto-backport-of-pr-14289-on-v3.1.x
Backport PR #14289 on branch v3.1.x (BUG: Fix performance regression when plotting values from Numpy array sub-classes)
2 parents c80bc34 + c1b5322 commit 095830e

File tree

2 files changed

+38
-1
lines changed

2 files changed

+38
-1
lines changed

lib/matplotlib/cbook/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1420,7 +1420,7 @@ def _reshape_2D(X, name):
14201420
X = np.atleast_1d(X.T if isinstance(X, np.ndarray) else np.asarray(X))
14211421
if len(X) == 0:
14221422
return [[]]
1423-
if X.ndim == 1 and not isinstance(X[0], collections.abc.Iterable):
1423+
elif X.ndim == 1 and np.ndim(X[0]) == 0:
14241424
# 1D array of scalars: directly return it.
14251425
return [X]
14261426
elif X.ndim in [1, 2]:

lib/matplotlib/tests/test_cbook.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,8 +494,10 @@ def test_flatiter():
494494

495495

496496
def test_reshape2d():
497+
497498
class dummy():
498499
pass
500+
499501
xnew = cbook._reshape_2D([], 'x')
500502
assert np.shape(xnew) == (1, 0)
501503

@@ -517,6 +519,41 @@ class dummy():
517519
xnew = cbook._reshape_2D(x, 'x')
518520
assert np.shape(xnew) == (5, 3)
519521

522+
# Now test with a list of lists with different lengths, which means the
523+
# array will internally be converted to a 1D object array of lists
524+
x = [[1, 2, 3], [3, 4], [2]]
525+
xnew = cbook._reshape_2D(x, 'x')
526+
assert isinstance(xnew, list)
527+
assert isinstance(xnew[0], np.ndarray) and xnew[0].shape == (3,)
528+
assert isinstance(xnew[1], np.ndarray) and xnew[1].shape == (2,)
529+
assert isinstance(xnew[2], np.ndarray) and xnew[2].shape == (1,)
530+
531+
# We now need to make sure that this works correctly for Numpy subclasses
532+
# where iterating over items can return subclasses too, which may be
533+
# iterable even if they are scalars. To emulate this, we make a Numpy
534+
# array subclass that returns Numpy 'scalars' when iterating or accessing
535+
# values, and these are technically iterable if checking for example
536+
# isinstance(x, collections.abc.Iterable).
537+
538+
class ArraySubclass(np.ndarray):
539+
540+
def __iter__(self):
541+
for value in super().__iter__():
542+
yield np.array(value)
543+
544+
def __getitem__(self, item):
545+
return np.array(super().__getitem__(item))
546+
547+
v = np.arange(10, dtype=float)
548+
x = ArraySubclass((10,), dtype=float, buffer=v.data)
549+
550+
xnew = cbook._reshape_2D(x, 'x')
551+
552+
# We check here that the array wasn't split up into many individual
553+
# ArraySubclass, which is what used to happen due to a bug in _reshape_2D
554+
assert len(xnew) == 1
555+
assert isinstance(xnew[0], ArraySubclass)
556+
520557

521558
def test_contiguous_regions():
522559
a, b, c = 3, 4, 5

0 commit comments

Comments
 (0)