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

Skip to content

Commit 7d26ea3

Browse files
committed
Fix performance regression when plotting values from Numpy array sub-classes that needed to be internally reshaped to lists of 1D arrays.
1 parent 9f3d9d6 commit 7d26ea3

File tree

2 files changed

+36
-1
lines changed

2 files changed

+36
-1
lines changed

lib/matplotlib/cbook/__init__.py

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

lib/matplotlib/tests/test_cbook.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,8 +492,10 @@ def test_flatiter():
492492

493493

494494
def test_reshape2d():
495+
495496
class dummy():
496497
pass
498+
497499
xnew = cbook._reshape_2D([], 'x')
498500
assert np.shape(xnew) == (1, 0)
499501

@@ -515,6 +517,39 @@ class dummy():
515517
xnew = cbook._reshape_2D(x, 'x')
516518
assert np.shape(xnew) == (5, 3)
517519

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

519554
def test_contiguous_regions():
520555
a, b, c = 3, 4, 5

0 commit comments

Comments
 (0)