Skip to content

Commit

Permalink
Backport PR pandas-dev#53641: BUG: pd.concat dataframes with differ…
Browse files Browse the repository at this point in the history
…ent datetime64 resolutions
  • Loading branch information
Charlie-XIAO authored and lithomas1 committed Dec 4, 2023
1 parent f99a1fc commit c463cfb
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 14 deletions.
11 changes: 10 additions & 1 deletion pandas/core/dtypes/concat.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,16 @@ def concat_compat(
# i.e. isinstance(to_concat[0], ExtensionArray)
to_concat_eas = cast("Sequence[ExtensionArray]", to_concat)
cls = type(to_concat[0])
return cls._concat_same_type(to_concat_eas)
# GH#53640: eg. for datetime array, axis=1 but 0 is default
# However, class method `_concat_same_type()` for some classes
# may not support the `axis` keyword
if ea_compat_axis or axis == 0:
return cls._concat_same_type(to_concat_eas)
else:
return cls._concat_same_type(
to_concat_eas,
axis=axis, # type: ignore[call-arg]
)
else:
to_concat_arrs = cast("Sequence[np.ndarray]", to_concat)
result = np.concatenate(to_concat_arrs, axis=axis)
Expand Down
54 changes: 41 additions & 13 deletions pandas/tests/reshape/concat/test_datetimes.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,22 @@
)
import pandas._testing as tm

UNITS = ["s", "ms", "us", "ns"]


@pytest.fixture(params=UNITS)
def unit(request):
return request.param


unit2 = unit


def _get_finer_unit(unit, unit2):
if UNITS.index(unit) >= UNITS.index(unit2):
return unit
return unit2


class TestDatetimeConcat:
def test_concat_datetime64_block(self):
Expand Down Expand Up @@ -305,46 +321,58 @@ def test_concat_tz_series(self):
result = concat([x, y], ignore_index=True)
tm.assert_series_equal(result, expected)

def test_concat_tz_series3(self, unit, unit2):
# see gh-12217 and gh-12306
# Concatenating two UTC times
first = DataFrame([[datetime(2016, 1, 1)]])
first = DataFrame([[datetime(2016, 1, 1)]], dtype=f"M8[{unit}]")
first[0] = first[0].dt.tz_localize("UTC")

second = DataFrame([[datetime(2016, 1, 2)]])
second = DataFrame([[datetime(2016, 1, 2)]], dtype=f"M8[{unit2}]")
second[0] = second[0].dt.tz_localize("UTC")

result = concat([first, second])
assert result[0].dtype == "datetime64[ns, UTC]"
exp_unit = _get_finer_unit(unit, unit2)
assert result[0].dtype == f"datetime64[{exp_unit}, UTC]"

def test_concat_tz_series4(self, unit, unit2):
# Concatenating two London times
first = DataFrame([[datetime(2016, 1, 1)]])
first = DataFrame([[datetime(2016, 1, 1)]], dtype=f"M8[{unit}]")
first[0] = first[0].dt.tz_localize("Europe/London")

second = DataFrame([[datetime(2016, 1, 2)]])
second = DataFrame([[datetime(2016, 1, 2)]], dtype=f"M8[{unit2}]")
second[0] = second[0].dt.tz_localize("Europe/London")

result = concat([first, second])
assert result[0].dtype == "datetime64[ns, Europe/London]"
exp_unit = _get_finer_unit(unit, unit2)
assert result[0].dtype == f"datetime64[{exp_unit}, Europe/London]"

def test_concat_tz_series5(self, unit, unit2):
# Concatenating 2+1 London times
first = DataFrame([[datetime(2016, 1, 1)], [datetime(2016, 1, 2)]])
first = DataFrame(
[[datetime(2016, 1, 1)], [datetime(2016, 1, 2)]], dtype=f"M8[{unit}]"
)
first[0] = first[0].dt.tz_localize("Europe/London")

second = DataFrame([[datetime(2016, 1, 3)]])
second = DataFrame([[datetime(2016, 1, 3)]], dtype=f"M8[{unit2}]")
second[0] = second[0].dt.tz_localize("Europe/London")

result = concat([first, second])
assert result[0].dtype == "datetime64[ns, Europe/London]"
exp_unit = _get_finer_unit(unit, unit2)
assert result[0].dtype == f"datetime64[{exp_unit}, Europe/London]"

# Concat'ing 1+2 London times
first = DataFrame([[datetime(2016, 1, 1)]])
def test_concat_tz_series6(self, unit, unit2):
# Concatenating 1+2 London times
first = DataFrame([[datetime(2016, 1, 1)]], dtype=f"M8[{unit}]")
first[0] = first[0].dt.tz_localize("Europe/London")

second = DataFrame([[datetime(2016, 1, 2)], [datetime(2016, 1, 3)]])
second = DataFrame(
[[datetime(2016, 1, 2)], [datetime(2016, 1, 3)]], dtype=f"M8[{unit2}]"
)
second[0] = second[0].dt.tz_localize("Europe/London")

result = concat([first, second])
assert result[0].dtype == "datetime64[ns, Europe/London]"
exp_unit = _get_finer_unit(unit, unit2)
assert result[0].dtype == f"datetime64[{exp_unit}, Europe/London]"

def test_concat_tz_series_tzlocal(self):
# see gh-13583
Expand Down

0 comments on commit c463cfb

Please sign in to comment.