From 1ebf1c3cb5f8db1b3ec464a30eab80f2360a3eb7 Mon Sep 17 00:00:00 2001 From: Antony Lee Date: Thu, 7 Feb 2019 22:36:07 +0100 Subject: [PATCH] Avoid unneeded copies from flatten(). flatten() always returns a new copy, but nearly all places that use flatten() in the codebase don't need to pay that cost; the only place that needed it did an additional copy. Fix them. As a replacement, there's ravel(), which only makes a copy if the input is not C-contiguous to start with, and reshape(..., -1) which never makes a copy (as long as the input is already an ndarray), so prefer the latter. Note that in the changed snippet in mplot3d, the AttributeError would never be raised, as x/y/z are converted to arrays and thus always have a flatten() method. --- lib/matplotlib/transforms.py | 10 +++++----- lib/mpl_toolkits/mplot3d/axes3d.py | 24 +++++++++--------------- 2 files changed, 14 insertions(+), 20 deletions(-) diff --git a/lib/matplotlib/transforms.py b/lib/matplotlib/transforms.py index efe1ad99ccb5..b530e8458f44 100644 --- a/lib/matplotlib/transforms.py +++ b/lib/matplotlib/transforms.py @@ -282,7 +282,7 @@ def __array__(self, *args, **kwargs): def is_unit(self): """Return whether this is the unit box (from (0, 0) to (1, 1)).""" - return list(self.get_points().flatten()) == [0., 0., 1., 1.] + return self.get_points().tolist() == [[0., 0.], [1., 1.]] @property def x0(self): @@ -413,13 +413,13 @@ def size(self): @property def bounds(self): """Return (:attr:`x0`, :attr:`y0`, :attr:`width`, :attr:`height`).""" - x0, y0, x1, y1 = self.get_points().flatten() + (x0, y0), (x1, y1) = self.get_points() return (x0, y0, x1 - x0, y1 - y0) @property def extents(self): """Return (:attr:`x0`, :attr:`y0`, :attr:`x1`, :attr:`y1`).""" - return self.get_points().flatten().copy() + return self.get_points().flatten() # flatten returns a copy. def get_points(self): raise NotImplementedError @@ -1758,10 +1758,10 @@ def is_separable(self): def to_values(self): """ - Return the values of the matrix as a sequence (a,b,c,d,e,f) + Return the values of the matrix as an ``(a, b, c, d, e, f)`` tuple. """ mtx = self.get_matrix() - return tuple(mtx[:2].swapaxes(0, 1).flatten()) + return tuple(mtx[:2].swapaxes(0, 1).flat) @staticmethod def matrix_from_values(a, b, c, d, e, f): diff --git a/lib/mpl_toolkits/mplot3d/axes3d.py b/lib/mpl_toolkits/mplot3d/axes3d.py index 9fe474ce9cd1..922972cd423c 100644 --- a/lib/mpl_toolkits/mplot3d/axes3d.py +++ b/lib/mpl_toolkits/mplot3d/axes3d.py @@ -490,22 +490,16 @@ def autoscale(self, enable=True, axis='both', tight=None): scalez=scalez) def auto_scale_xyz(self, X, Y, Z=None, had_data=None): - x, y, z = map(np.asarray, (X, Y, Z)) - try: - x, y = x.flatten(), y.flatten() - if Z is not None: - z = z.flatten() - except AttributeError: - raise - - # This updates the bounding boxes as to keep a record as - # to what the minimum sized rectangular volume holds the - # data. - self.xy_dataLim.update_from_data_xy(np.array([x, y]).T, not had_data) - if z is not None: + # This updates the bounding boxes as to keep a record as to what the + # minimum sized rectangular volume holds the data. + X = np.reshape(X, -1) + Y = np.reshape(Y, -1) + self.xy_dataLim.update_from_data_xy( + np.column_stack([X, Y]), not had_data) + if Z is not None: + Z = np.reshape(Z, -1) self.zz_dataLim.update_from_data_xy( - np.array([z, z]).T, not had_data) - + np.column_stack([Z, Z]), not had_data) # Let autoscale_view figure out how to use this data. self.autoscale_view()