From 8e3cce0c793c1d0206f784879c76e6de3e8636c5 Mon Sep 17 00:00:00 2001 From: Brock Date: Fri, 16 Oct 2020 11:37:21 -0700 Subject: [PATCH] ENH: __repr__ for 2D DTA/TDA --- pandas/core/arrays/_mixins.py | 21 +++++++++++++++++ pandas/core/arrays/datetimes.py | 3 ++- pandas/core/arrays/timedeltas.py | 3 ++- pandas/tests/arrays/test_datetimelike.py | 30 ++++++++++++++++++++++++ 4 files changed, 55 insertions(+), 2 deletions(-) diff --git a/pandas/core/arrays/_mixins.py b/pandas/core/arrays/_mixins.py index 948ffdc1f7c01..4ac32b409883d 100644 --- a/pandas/core/arrays/_mixins.py +++ b/pandas/core/arrays/_mixins.py @@ -240,3 +240,24 @@ def _reduce(self, name: str, skipna: bool = True, **kwargs): else: msg = f"'{type(self).__name__}' does not implement reduction '{name}'" raise TypeError(msg) + + # ------------------------------------------------------------------------ + + def __repr__(self) -> str: + if self.ndim == 1: + return super().__repr__() + + from pandas.io.formats.printing import format_object_summary + + # the short repr has no trailing newline, while the truncated + # repr does. So we include a newline in our template, and strip + # any trailing newlines from format_object_summary + lines = [ + format_object_summary(x, self._formatter(), indent_for_name=False).rstrip( + ", \n" + ) + for x in self + ] + data = ",\n".join(lines) + class_name = f"<{type(self).__name__}>" + return f"{class_name}\n[\n{data}\n]\nShape: {self.shape}, dtype: {self.dtype}" diff --git a/pandas/core/arrays/datetimes.py b/pandas/core/arrays/datetimes.py index b1b8b513320e9..4ce959f2b833f 100644 --- a/pandas/core/arrays/datetimes.py +++ b/pandas/core/arrays/datetimes.py @@ -564,7 +564,8 @@ def __iter__(self): tstamp : Timestamp """ if self.ndim > 1: - return (self[n] for n in range(len(self))) + for i in range(len(self)): + yield self[i] else: # convert in chunks of 10k for efficiency data = self.asi8 diff --git a/pandas/core/arrays/timedeltas.py b/pandas/core/arrays/timedeltas.py index 82cd54182a33d..adade2460e8d3 100644 --- a/pandas/core/arrays/timedeltas.py +++ b/pandas/core/arrays/timedeltas.py @@ -352,7 +352,8 @@ def astype(self, dtype, copy: bool = True): def __iter__(self): if self.ndim > 1: - return (self[n] for n in range(len(self))) + for i in range(len(self)): + yield self[i] else: # convert in chunks of 10k for efficiency data = self.asi8 diff --git a/pandas/tests/arrays/test_datetimelike.py b/pandas/tests/arrays/test_datetimelike.py index a961cf14b2e5c..2aa2fbe59cbbf 100644 --- a/pandas/tests/arrays/test_datetimelike.py +++ b/pandas/tests/arrays/test_datetimelike.py @@ -328,11 +328,41 @@ def test_iter_2d(self, arr1d): data2d = arr1d._data[:3, np.newaxis] arr2d = type(arr1d)._simple_new(data2d, dtype=arr1d.dtype) result = list(arr2d) + assert len(result) == 3 for x in result: assert isinstance(x, type(arr1d)) assert x.ndim == 1 assert x.dtype == arr1d.dtype + def test_repr_2d(self, arr1d): + data2d = arr1d._data[:3, np.newaxis] + arr2d = type(arr1d)._simple_new(data2d, dtype=arr1d.dtype) + + result = repr(arr2d) + + if isinstance(arr2d, TimedeltaArray): + expected = ( + f"<{type(arr2d).__name__}>\n" + "[\n" + f"['{arr1d[0]._repr_base()}'],\n" + f"['{arr1d[1]._repr_base()}'],\n" + f"['{arr1d[2]._repr_base()}']\n" + "]\n" + f"Shape: (3, 1), dtype: {arr1d.dtype}" + ) + else: + expected = ( + f"<{type(arr2d).__name__}>\n" + "[\n" + f"['{arr1d[0]}'],\n" + f"['{arr1d[1]}'],\n" + f"['{arr1d[2]}']\n" + "]\n" + f"Shape: (3, 1), dtype: {arr1d.dtype}" + ) + + assert result == expected + def test_setitem(self): data = np.arange(10, dtype="i8") * 24 * 3600 * 10 ** 9 arr = self.array_cls(data, freq="D")