From bb8386489cba431ffc9502aae15f971ada5e54ba Mon Sep 17 00:00:00 2001 From: Bryan Cutler Date: Wed, 9 Dec 2020 11:17:42 -0800 Subject: [PATCH 01/12] Add tests for Datetime64 formatter --- pandas/tests/io/formats/test_format.py | 37 ++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/pandas/tests/io/formats/test_format.py b/pandas/tests/io/formats/test_format.py index fe85849c6dcca..a6dec6c2f4de8 100644 --- a/pandas/tests/io/formats/test_format.py +++ b/pandas/tests/io/formats/test_format.py @@ -3106,6 +3106,43 @@ def format_func(x): result = formatter.get_result() assert result == ["10:10", "12:12"] + def test_datetime64formatter_2d_array(self): + x = pd.date_range('2018-01-01', periods=10, freq='H').to_numpy() + + formatter = fmt.Datetime64Formatter(x.reshape((5, 2))) + result = formatter.get_result() + assert len(result) == 5 + assert result[0].strip() == "[2018-01-01 00:00:00, 2018-01-01 01:00:00]" + assert result[4].strip() == "[2018-01-01 08:00:00, 2018-01-01 09:00:00]" + + formatter = fmt.Datetime64Formatter(x.reshape((2, 5))) + result = formatter.get_result() + assert len(result) == 2 + assert result[0].strip() == "[2018-01-01 00:00:00, 2018-01-01 01:00:00, 201..." + assert result[1].strip() == "[2018-01-01 05:00:00, 2018-01-01 06:00:00, 201..." + + def test_datetime64formatter_3d_array(self): + x = pd.date_range('2018-01-01', periods=10, freq='H').to_numpy() + + formatter = fmt.Datetime64Formatter(x.reshape((10, 1, 1))) + result = formatter.get_result() + assert len(result) == 10 + assert result[0].strip() == "[[2018-01-01 00:00:00]]" + assert result[9].strip() == "[[2018-01-01 09:00:00]]" + + def test_datetime64formatter_2d_array_format_func(self): + x = pd.date_range('2018-01-01', periods=24, freq='H').to_numpy() + + def format_func(t): + return t.strftime("%H-%m") + + formatter = fmt.Datetime64Formatter(x.reshape((4, 2, 3)), + formatter=format_func) + result = formatter.get_result() + assert len(result) == 4 + assert result[0].strip() == "[[00-01, 01-01, 02-01], [03-01, 04-01, 05-01]]" + assert result[3].strip() == "[[18-01, 19-01, 20-01], [21-01, 22-01, 23-01]]" + class TestNaTFormatting: def test_repr(self): From 460f33c919f2fcb67139b4ecb4c5128212d97901 Mon Sep 17 00:00:00 2001 From: Bryan Cutler Date: Wed, 9 Dec 2020 11:18:15 -0800 Subject: [PATCH 02/12] Fix Datetime64Formatter to handle values with ndim > 1 --- pandas/io/formats/format.py | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 527ee51873631..061ad103908bc 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -1515,17 +1515,26 @@ def __init__( def _format_strings(self) -> List[str]: """ we by definition have DO NOT have a TZ """ values = self.values + flat_values = values.ravel() - if not isinstance(values, DatetimeIndex): - values = DatetimeIndex(values) + if not isinstance(flat_values, DatetimeIndex): + flat_values = DatetimeIndex(flat_values) if self.formatter is not None and callable(self.formatter): - return [self.formatter(x) for x in values] + flat_str_values = np.array([self.formatter(x) for x in flat_values]) + fmt_values = flat_str_values.reshape(values.shape) + else: + fmt_values = flat_values._data._format_native_types( + na_rep=self.nat_rep, date_format=self.date_format + ).reshape(values.shape) - fmt_values = values._data._format_native_types( - na_rep=self.nat_rep, date_format=self.date_format - ) - return fmt_values.tolist() + if len(fmt_values.shape) > 1: + nested_string_formatter = GenericArrayFormatter(fmt_values) + fmt_values = nested_string_formatter.get_result() + else: + fmt_values = fmt_values.tolist() + + return fmt_values class ExtensionArrayFormatter(GenericArrayFormatter): From 39171e91351b649949a5da0e14ae7553574f25a4 Mon Sep 17 00:00:00 2001 From: Bryan Cutler Date: Wed, 9 Dec 2020 14:55:48 -0800 Subject: [PATCH 03/12] Fix formatting --- pandas/tests/io/formats/test_format.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/pandas/tests/io/formats/test_format.py b/pandas/tests/io/formats/test_format.py index a6dec6c2f4de8..96e03bf1aa633 100644 --- a/pandas/tests/io/formats/test_format.py +++ b/pandas/tests/io/formats/test_format.py @@ -3107,7 +3107,7 @@ def format_func(x): assert result == ["10:10", "12:12"] def test_datetime64formatter_2d_array(self): - x = pd.date_range('2018-01-01', periods=10, freq='H').to_numpy() + x = pd.date_range("2018-01-01", periods=10, freq="H").to_numpy() formatter = fmt.Datetime64Formatter(x.reshape((5, 2))) result = formatter.get_result() @@ -3122,7 +3122,7 @@ def test_datetime64formatter_2d_array(self): assert result[1].strip() == "[2018-01-01 05:00:00, 2018-01-01 06:00:00, 201..." def test_datetime64formatter_3d_array(self): - x = pd.date_range('2018-01-01', periods=10, freq='H').to_numpy() + x = pd.date_range("2018-01-01", periods=10, freq="H").to_numpy() formatter = fmt.Datetime64Formatter(x.reshape((10, 1, 1))) result = formatter.get_result() @@ -3131,13 +3131,12 @@ def test_datetime64formatter_3d_array(self): assert result[9].strip() == "[[2018-01-01 09:00:00]]" def test_datetime64formatter_2d_array_format_func(self): - x = pd.date_range('2018-01-01', periods=24, freq='H').to_numpy() + x = pd.date_range("2018-01-01", periods=24, freq="H").to_numpy() def format_func(t): return t.strftime("%H-%m") - formatter = fmt.Datetime64Formatter(x.reshape((4, 2, 3)), - formatter=format_func) + formatter = fmt.Datetime64Formatter(x.reshape((4, 2, 3)), formatter=format_func) result = formatter.get_result() assert len(result) == 4 assert result[0].strip() == "[[00-01, 01-01, 02-01], [03-01, 04-01, 05-01]]" From 3ba09255619303c4ef903a61cb3c7b8eff1c576f Mon Sep 17 00:00:00 2001 From: Bryan Cutler Date: Fri, 11 Dec 2020 14:21:16 -0800 Subject: [PATCH 04/12] Add tests for Datetime64TZFormatter --- pandas/tests/io/formats/test_format.py | 40 ++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/pandas/tests/io/formats/test_format.py b/pandas/tests/io/formats/test_format.py index 96e03bf1aa633..60acb2ace08b7 100644 --- a/pandas/tests/io/formats/test_format.py +++ b/pandas/tests/io/formats/test_format.py @@ -3143,6 +3143,46 @@ def format_func(t): assert result[3].strip() == "[[18-01, 19-01, 20-01], [21-01, 22-01, 23-01]]" +class TestDatetime64TZFormatter: + def test_mixed(self): + utc = dateutil.tz.tzutc() + x = Series([datetime(2013, 1, 1, tzinfo=utc), datetime(2013, 1, 1, 12, tzinfo=utc), pd.NaT]) + result = fmt.Datetime64TZFormatter(x).get_result() + assert len(result) == 3 + assert result[0].strip() == "2013-01-01 00:00:00+00:00" + assert result[1].strip() == "2013-01-01 12:00:00+00:00" + assert result[2].strip() == "NaT" + + def test_datetime64formatter_1d_array(self): + x = pd.date_range("2018-01-01", periods=3, freq="H", tz="US/Pacific").to_numpy() + formatter = fmt.Datetime64TZFormatter(x) + result = formatter.get_result() + assert len(result) == 3 + assert result[0].strip() == "2018-01-01 00:00:00-08:00" + assert result[1].strip() == "2018-01-01 01:00:00-08:00" + assert result[2].strip() == "2018-01-01 02:00:00-08:00" + + def test_datetime64formatter_2d_array(self): + x = pd.date_range("2018-01-01", periods=10, freq="H", tz="US/Pacific").to_numpy() + formatter = fmt.Datetime64TZFormatter(x.reshape((5, 2))) + result = formatter.get_result() + assert len(result) == 5 + assert result[0].strip() == "[2018-01-01 00:00:00-08:00, 2018-01-01 01:00:00-08:00]" + assert result[4].strip() == "[2018-01-01 08:00:00-08:00, 2018-01-01 09:00:00-08:00]" + + def test_datetime64formatter_2d_array_format_func(self): + x = pd.date_range("2018-01-01", periods=16, freq="H", tz="US/Pacific").to_numpy() + + def format_func(t): + return t.strftime("%H-%m %Z") + + formatter = fmt.Datetime64TZFormatter(x.reshape((4, 2, 2)), formatter=format_func) + result = formatter.get_result() + assert len(result) == 4 + assert result[0].strip() == "[[00-01, 01-01, 02-01], [03-01, 04-01, 05-01]]" + assert result[3].strip() == "[[18-01, 19-01, 20-01], [21-01, 22-01, 23-01]]" + + class TestNaTFormatting: def test_repr(self): assert repr(pd.NaT) == "NaT" From aac5f1bd5cddaa3a9419621b5e529a00becd0596 Mon Sep 17 00:00:00 2001 From: Bryan Cutler Date: Mon, 14 Dec 2020 16:30:52 -0800 Subject: [PATCH 05/12] Corrected tests --- pandas/io/formats/format.py | 19 +++++++++++++++---- pandas/tests/io/formats/test_format.py | 8 ++++---- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 061ad103908bc..1c249ca9ccf9d 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -1522,11 +1522,12 @@ def _format_strings(self) -> List[str]: if self.formatter is not None and callable(self.formatter): flat_str_values = np.array([self.formatter(x) for x in flat_values]) - fmt_values = flat_str_values.reshape(values.shape) + fmt_values = flat_str_values else: fmt_values = flat_values._data._format_native_types( na_rep=self.nat_rep, date_format=self.date_format - ).reshape(values.shape) + ) + fmt_values = fmt_values.reshape(values.shape) if len(fmt_values.shape) > 1: nested_string_formatter = GenericArrayFormatter(fmt_values) @@ -1709,11 +1710,21 @@ class Datetime64TZFormatter(Datetime64Formatter): def _format_strings(self) -> List[str]: """ we by definition have a TZ """ values = self.values.astype(object) - ido = is_dates_only(values) + flat_values = values.ravel() + + ido = is_dates_only(flat_values) + formatter = self.formatter or get_format_datetime64( ido, date_format=self.date_format ) - fmt_values = [formatter(x) for x in values] + + fmt_values = np.array([formatter(x) for x in flat_values]).reshape(values.shape) + + if len(fmt_values.shape) > 1: + nested_string_formatter = GenericArrayFormatter(fmt_values) + fmt_values = nested_string_formatter.get_result() + else: + fmt_values = fmt_values.tolist() return fmt_values diff --git a/pandas/tests/io/formats/test_format.py b/pandas/tests/io/formats/test_format.py index 60acb2ace08b7..0aef0308d86fc 100644 --- a/pandas/tests/io/formats/test_format.py +++ b/pandas/tests/io/formats/test_format.py @@ -3167,8 +3167,8 @@ def test_datetime64formatter_2d_array(self): formatter = fmt.Datetime64TZFormatter(x.reshape((5, 2))) result = formatter.get_result() assert len(result) == 5 - assert result[0].strip() == "[2018-01-01 00:00:00-08:00, 2018-01-01 01:00:00-08:00]" - assert result[4].strip() == "[2018-01-01 08:00:00-08:00, 2018-01-01 09:00:00-08:00]" + assert result[0].strip() == "[2018-01-01 00:00:00-08:00, 2018-01-01 01:00:0..." + assert result[4].strip() == "[2018-01-01 08:00:00-08:00, 2018-01-01 09:00:0..." def test_datetime64formatter_2d_array_format_func(self): x = pd.date_range("2018-01-01", periods=16, freq="H", tz="US/Pacific").to_numpy() @@ -3179,8 +3179,8 @@ def format_func(t): formatter = fmt.Datetime64TZFormatter(x.reshape((4, 2, 2)), formatter=format_func) result = formatter.get_result() assert len(result) == 4 - assert result[0].strip() == "[[00-01, 01-01, 02-01], [03-01, 04-01, 05-01]]" - assert result[3].strip() == "[[18-01, 19-01, 20-01], [21-01, 22-01, 23-01]]" + assert result[0].strip() == "[[00-01 PST, 01-01 PST], [02-01 PST, 03-01 PST]]" + assert result[3].strip() == "[[12-01 PST, 13-01 PST], [14-01 PST, 15-01 PST]]" class TestNaTFormatting: From 14c64cd0b7fc36380762b778202cc332fb5735bd Mon Sep 17 00:00:00 2001 From: Bryan Cutler Date: Tue, 15 Dec 2020 00:11:40 -0800 Subject: [PATCH 06/12] Add tests and fix for Datetime64TZFormatter --- pandas/io/formats/format.py | 25 +++++++++++-------------- pandas/tests/io/formats/test_format.py | 20 ++++++++++++++++---- 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 1c249ca9ccf9d..c9ed4fd1bbb6b 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -1521,18 +1521,17 @@ def _format_strings(self) -> List[str]: flat_values = DatetimeIndex(flat_values) if self.formatter is not None and callable(self.formatter): - flat_str_values = np.array([self.formatter(x) for x in flat_values]) - fmt_values = flat_str_values + fmt_values = [self.formatter(x) for x in flat_values] else: fmt_values = flat_values._data._format_native_types( na_rep=self.nat_rep, date_format=self.date_format ) - fmt_values = fmt_values.reshape(values.shape) - if len(fmt_values.shape) > 1: - nested_string_formatter = GenericArrayFormatter(fmt_values) - fmt_values = nested_string_formatter.get_result() - else: + if len(values.shape) > 1: + fmt_values = np.asarray(fmt_values).reshape(values.shape) + nested_formatter = GenericArrayFormatter(fmt_values) + fmt_values = nested_formatter.get_result() + elif isinstance(fmt_values, np.ndarray): fmt_values = fmt_values.tolist() return fmt_values @@ -1713,18 +1712,16 @@ def _format_strings(self) -> List[str]: flat_values = values.ravel() ido = is_dates_only(flat_values) - formatter = self.formatter or get_format_datetime64( ido, date_format=self.date_format ) - fmt_values = np.array([formatter(x) for x in flat_values]).reshape(values.shape) + fmt_values = [formatter(x) for x in flat_values] - if len(fmt_values.shape) > 1: - nested_string_formatter = GenericArrayFormatter(fmt_values) - fmt_values = nested_string_formatter.get_result() - else: - fmt_values = fmt_values.tolist() + if len(values.shape) > 1: + fmt_values = np.asarray(fmt_values).reshape(values.shape) + nested_formatter = GenericArrayFormatter(fmt_values) + fmt_values = nested_formatter.get_result() return fmt_values diff --git a/pandas/tests/io/formats/test_format.py b/pandas/tests/io/formats/test_format.py index 0aef0308d86fc..42256333bd3a1 100644 --- a/pandas/tests/io/formats/test_format.py +++ b/pandas/tests/io/formats/test_format.py @@ -3146,7 +3146,13 @@ def format_func(t): class TestDatetime64TZFormatter: def test_mixed(self): utc = dateutil.tz.tzutc() - x = Series([datetime(2013, 1, 1, tzinfo=utc), datetime(2013, 1, 1, 12, tzinfo=utc), pd.NaT]) + x = Series( + [ + datetime(2013, 1, 1, tzinfo=utc), + datetime(2013, 1, 1, 12, tzinfo=utc), + pd.NaT, + ] + ) result = fmt.Datetime64TZFormatter(x).get_result() assert len(result) == 3 assert result[0].strip() == "2013-01-01 00:00:00+00:00" @@ -3163,7 +3169,9 @@ def test_datetime64formatter_1d_array(self): assert result[2].strip() == "2018-01-01 02:00:00-08:00" def test_datetime64formatter_2d_array(self): - x = pd.date_range("2018-01-01", periods=10, freq="H", tz="US/Pacific").to_numpy() + x = pd.date_range( + "2018-01-01", periods=10, freq="H", tz="US/Pacific" + ).to_numpy() formatter = fmt.Datetime64TZFormatter(x.reshape((5, 2))) result = formatter.get_result() assert len(result) == 5 @@ -3171,12 +3179,16 @@ def test_datetime64formatter_2d_array(self): assert result[4].strip() == "[2018-01-01 08:00:00-08:00, 2018-01-01 09:00:0..." def test_datetime64formatter_2d_array_format_func(self): - x = pd.date_range("2018-01-01", periods=16, freq="H", tz="US/Pacific").to_numpy() + x = pd.date_range( + "2018-01-01", periods=16, freq="H", tz="US/Pacific" + ).to_numpy() def format_func(t): return t.strftime("%H-%m %Z") - formatter = fmt.Datetime64TZFormatter(x.reshape((4, 2, 2)), formatter=format_func) + formatter = fmt.Datetime64TZFormatter( + x.reshape((4, 2, 2)), formatter=format_func + ) result = formatter.get_result() assert len(result) == 4 assert result[0].strip() == "[[00-01 PST, 01-01 PST], [02-01 PST, 03-01 PST]]" From 12dd2339fce89a7217ce5cb22bc7d3bcf2c23e39 Mon Sep 17 00:00:00 2001 From: Bryan Cutler Date: Tue, 15 Dec 2020 00:21:21 -0800 Subject: [PATCH 07/12] Use DatetimeArray instead of DatetimeIndex --- pandas/io/formats/format.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index c9ed4fd1bbb6b..0664cb0a97198 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -1517,13 +1517,13 @@ def _format_strings(self) -> List[str]: values = self.values flat_values = values.ravel() - if not isinstance(flat_values, DatetimeIndex): - flat_values = DatetimeIndex(flat_values) + if not isinstance(flat_values, DatetimeArray): + flat_values = DatetimeArray(flat_values) if self.formatter is not None and callable(self.formatter): fmt_values = [self.formatter(x) for x in flat_values] else: - fmt_values = flat_values._data._format_native_types( + fmt_values = flat_values._format_native_types( na_rep=self.nat_rep, date_format=self.date_format ) From ec0aa1537a52bad8ccf36018b226daa9856a2867 Mon Sep 17 00:00:00 2001 From: Bryan Cutler Date: Tue, 15 Dec 2020 10:12:38 -0800 Subject: [PATCH 08/12] Avoid calling ravel on Index --- pandas/io/formats/format.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 0664cb0a97198..14341ddf9f567 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -1514,11 +1514,8 @@ def __init__( def _format_strings(self) -> List[str]: """ we by definition have DO NOT have a TZ """ - values = self.values - flat_values = values.ravel() - - if not isinstance(flat_values, DatetimeArray): - flat_values = DatetimeArray(flat_values) + values = np.asarray(self.values) + flat_values = DatetimeArray(values.ravel()) if self.formatter is not None and callable(self.formatter): fmt_values = [self.formatter(x) for x in flat_values] From 6da6c68923b3f43e5a9461491f2af066171e4e63 Mon Sep 17 00:00:00 2001 From: Bryan Cutler Date: Tue, 15 Dec 2020 11:15:22 -0800 Subject: [PATCH 09/12] Conditionally call ravel() if ndim > 1 --- pandas/io/formats/format.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 14341ddf9f567..3db0c82140f04 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -1515,7 +1515,8 @@ def __init__( def _format_strings(self) -> List[str]: """ we by definition have DO NOT have a TZ """ values = np.asarray(self.values) - flat_values = DatetimeArray(values.ravel()) + flat_values = values.ravel() if len(values.shape) > 1 else values + flat_values = DatetimeArray(flat_values) if self.formatter is not None and callable(self.formatter): fmt_values = [self.formatter(x) for x in flat_values] @@ -1706,7 +1707,7 @@ class Datetime64TZFormatter(Datetime64Formatter): def _format_strings(self) -> List[str]: """ we by definition have a TZ """ values = self.values.astype(object) - flat_values = values.ravel() + flat_values = values.ravel() if len(values.shape) > 1 else values ido = is_dates_only(flat_values) formatter = self.formatter or get_format_datetime64( From da0f037c40a4bfa8f78efe16e1dc65daf7a24508 Mon Sep 17 00:00:00 2001 From: Bryan Cutler Date: Tue, 15 Dec 2020 11:15:40 -0800 Subject: [PATCH 10/12] Added whatsnew entry --- doc/source/whatsnew/v1.2.0.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/source/whatsnew/v1.2.0.rst b/doc/source/whatsnew/v1.2.0.rst index e2521cedb64cc..636a2a7b0d965 100644 --- a/doc/source/whatsnew/v1.2.0.rst +++ b/doc/source/whatsnew/v1.2.0.rst @@ -747,6 +747,7 @@ I/O - :meth:`DataFrame.to_html` was ignoring ``formatters`` argument for ``ExtensionDtype`` columns (:issue:`36525`) - Bumped minimum xarray version to 0.12.3 to avoid reference to the removed ``Panel`` class (:issue:`27101`) - :meth:`DataFrame.to_csv` was re-opening file-like handles that also implement ``os.PathLike`` (:issue:`38125`) +- Bug in :class:`Datetime64Formatter` that caused error on string representation with extension types of datetime64 values and ndim > 1 (:issue:`38390`) Period ^^^^^^ From 4140453a28a446797d353b2956354e5b281f5954 Mon Sep 17 00:00:00 2001 From: Bryan Cutler Date: Fri, 18 Dec 2020 14:47:47 -0800 Subject: [PATCH 11/12] Changed to use values.ndim > 1 --- pandas/io/formats/format.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pandas/io/formats/format.py b/pandas/io/formats/format.py index 3db0c82140f04..cebce0229da6b 100644 --- a/pandas/io/formats/format.py +++ b/pandas/io/formats/format.py @@ -1515,7 +1515,7 @@ def __init__( def _format_strings(self) -> List[str]: """ we by definition have DO NOT have a TZ """ values = np.asarray(self.values) - flat_values = values.ravel() if len(values.shape) > 1 else values + flat_values = values.ravel() if values.ndim > 1 else values flat_values = DatetimeArray(flat_values) if self.formatter is not None and callable(self.formatter): @@ -1525,7 +1525,7 @@ def _format_strings(self) -> List[str]: na_rep=self.nat_rep, date_format=self.date_format ) - if len(values.shape) > 1: + if values.ndim > 1: fmt_values = np.asarray(fmt_values).reshape(values.shape) nested_formatter = GenericArrayFormatter(fmt_values) fmt_values = nested_formatter.get_result() @@ -1707,7 +1707,7 @@ class Datetime64TZFormatter(Datetime64Formatter): def _format_strings(self) -> List[str]: """ we by definition have a TZ """ values = self.values.astype(object) - flat_values = values.ravel() if len(values.shape) > 1 else values + flat_values = values.ravel() if values.ndim > 1 else values ido = is_dates_only(flat_values) formatter = self.formatter or get_format_datetime64( @@ -1716,7 +1716,7 @@ def _format_strings(self) -> List[str]: fmt_values = [formatter(x) for x in flat_values] - if len(values.shape) > 1: + if values.ndim > 1: fmt_values = np.asarray(fmt_values).reshape(values.shape) nested_formatter = GenericArrayFormatter(fmt_values) fmt_values = nested_formatter.get_result() From 1563c4d02db3eaaa3708b8eff11546addd7c4183 Mon Sep 17 00:00:00 2001 From: Bryan Cutler Date: Mon, 21 Dec 2020 22:38:01 -0800 Subject: [PATCH 12/12] Move whatsnew entry to 1.3.0 --- doc/source/whatsnew/v1.2.0.rst | 1 - doc/source/whatsnew/v1.3.0.rst | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/source/whatsnew/v1.2.0.rst b/doc/source/whatsnew/v1.2.0.rst index 636a2a7b0d965..e2521cedb64cc 100644 --- a/doc/source/whatsnew/v1.2.0.rst +++ b/doc/source/whatsnew/v1.2.0.rst @@ -747,7 +747,6 @@ I/O - :meth:`DataFrame.to_html` was ignoring ``formatters`` argument for ``ExtensionDtype`` columns (:issue:`36525`) - Bumped minimum xarray version to 0.12.3 to avoid reference to the removed ``Panel`` class (:issue:`27101`) - :meth:`DataFrame.to_csv` was re-opening file-like handles that also implement ``os.PathLike`` (:issue:`38125`) -- Bug in :class:`Datetime64Formatter` that caused error on string representation with extension types of datetime64 values and ndim > 1 (:issue:`38390`) Period ^^^^^^ diff --git a/doc/source/whatsnew/v1.3.0.rst b/doc/source/whatsnew/v1.3.0.rst index 57dd1d05a274e..ad44078d051f6 100644 --- a/doc/source/whatsnew/v1.3.0.rst +++ b/doc/source/whatsnew/v1.3.0.rst @@ -242,6 +242,7 @@ I/O - Bug in :func:`read_csv` not accepting ``usecols`` with different length than ``names`` for ``engine="python"`` (:issue:`16469`) - Bug in :func:`read_csv` raising ``TypeError`` when ``names`` and ``parse_dates`` is specified for ``engine="c"`` (:issue:`33699`) - Allow custom error values for parse_dates argument of :func:`read_sql`, :func:`read_sql_query` and :func:`read_sql_table` (:issue:`35185`) +- Bug in :class:`Datetime64Formatter` that caused error on string representation with extension types of datetime64 values and ndim > 1 (:issue:`38390`) - Period