From b296811f0330e15846b3c627de7e1d69b7197a98 Mon Sep 17 00:00:00 2001 From: Huanghz2001 <1536277295@qq.com> Date: Sun, 24 Dec 2023 17:36:54 +0800 Subject: [PATCH 1/6] Add raising between datetime and timedelta dtype in _MergeOperation --- pandas/core/reshape/merge.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pandas/core/reshape/merge.py b/pandas/core/reshape/merge.py index 690e3c2700c6c..80478bb50d7b8 100644 --- a/pandas/core/reshape/merge.py +++ b/pandas/core/reshape/merge.py @@ -61,6 +61,8 @@ is_object_dtype, is_string_dtype, needs_i8_conversion, + is_datetime64_dtype, + is_timedelta64_dtype, ) from pandas.core.dtypes.dtypes import ( CategoricalDtype, @@ -1526,6 +1528,11 @@ def _maybe_coerce_merge_keys(self) -> None: ) or (lk.dtype.kind == "M" and rk.dtype.kind == "M"): # allows datetime with different resolutions continue + # datetime and timedelta not allowed + elif is_datetime64_dtype(lk.dtype) and is_timedelta64_dtype(rk.dtype): + raise ValueError(msg) + elif is_timedelta64_dtype(lk.dtype) and is_datetime64_dtype(rk.dtype): + raise ValueError(msg) elif is_object_dtype(lk.dtype) and is_object_dtype(rk.dtype): continue From 7c73dbeb8b6c246eefcb1d4f31adea193667e96f Mon Sep 17 00:00:00 2001 From: Huanghz2001 <1536277295@qq.com> Date: Sun, 24 Dec 2023 19:21:33 +0800 Subject: [PATCH 2/6] Add entry in release notes --- doc/source/whatsnew/v2.2.0.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/source/whatsnew/v2.2.0.rst b/doc/source/whatsnew/v2.2.0.rst index d1481639ca5a0..419bd3ee36143 100644 --- a/doc/source/whatsnew/v2.2.0.rst +++ b/doc/source/whatsnew/v2.2.0.rst @@ -839,6 +839,7 @@ Reshaping - Bug in :func:`merge_asof` raising ``TypeError`` when ``by`` dtype is not ``object``, ``int64``, or ``uint64`` (:issue:`22794`) - Bug in :func:`merge_asof` raising incorrect error for string dtype (:issue:`56444`) - Bug in :func:`merge_asof` when using a :class:`Timedelta` tolerance on a :class:`ArrowDtype` column (:issue:`56486`) +- Bug in :func:`merge` not raising when merging datetime columns with timedelta columns (:issue:`56455`) - Bug in :func:`merge` not raising when merging string columns with numeric columns (:issue:`56441`) - Bug in :func:`merge` returning columns in incorrect order when left and/or right is empty (:issue:`51929`) - Bug in :meth:`DataFrame.melt` where an exception was raised if ``var_name`` was not a string (:issue:`55948`) From 534e738e5ffec68586772ce7b53f4ffc4aed3d51 Mon Sep 17 00:00:00 2001 From: Huanghz2001 <1536277295@qq.com> Date: Sun, 24 Dec 2023 22:25:31 +0800 Subject: [PATCH 3/6] Add test --- pandas/tests/reshape/merge/test_merge.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/pandas/tests/reshape/merge/test_merge.py b/pandas/tests/reshape/merge/test_merge.py index d7a343ae9f152..7760b621ed49a 100644 --- a/pandas/tests/reshape/merge/test_merge.py +++ b/pandas/tests/reshape/merge/test_merge.py @@ -2988,3 +2988,23 @@ def test_merge_empty_frames_column_order(left_empty, right_empty): elif right_empty: expected.loc[:, ["C", "D"]] = np.nan tm.assert_frame_equal(result, expected) + + +@pytest.mark.parametrize("how", ["left", "right", "inner", "outer"]) +def test_merge_datetime_and_timedelta(how): + left = DataFrame({"key": Series([1, None], dtype="datetime64[ns]")}) + right = DataFrame({"key": Series([1], dtype="timedelta64[ns]")}) + + msg = ( + f"You are trying to merge on {left['key'].dtype} and {right['key'].dtype} " + "columns for key 'key'. If you wish to proceed you should use pd.concat" + ) + with pytest.raises(ValueError, match=msg): + left.merge(right, on="key", how=how) + + msg = ( + f"You are trying to merge on {right['key'].dtype} and {left['key'].dtype} " + "columns for key 'key'. If you wish to proceed you should use pd.concat" + ) + with pytest.raises(ValueError, match=msg): + right.merge(left, on="key", how=how) From c93a90c672976a70ca983dae4f3aebe9495fa0c2 Mon Sep 17 00:00:00 2001 From: Huanghz2001 <1536277295@qq.com> Date: Sun, 24 Dec 2023 23:17:24 +0800 Subject: [PATCH 4/6] Fixed import order --- pandas/core/reshape/merge.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/core/reshape/merge.py b/pandas/core/reshape/merge.py index 80478bb50d7b8..5c00a76745c2b 100644 --- a/pandas/core/reshape/merge.py +++ b/pandas/core/reshape/merge.py @@ -52,6 +52,7 @@ ensure_object, is_bool, is_bool_dtype, + is_datetime64_dtype, is_float_dtype, is_integer, is_integer_dtype, @@ -60,9 +61,8 @@ is_numeric_dtype, is_object_dtype, is_string_dtype, - needs_i8_conversion, - is_datetime64_dtype, is_timedelta64_dtype, + needs_i8_conversion, ) from pandas.core.dtypes.dtypes import ( CategoricalDtype, From e7ee40e0d869ed7dec0d81a5236c820594e00e59 Mon Sep 17 00:00:00 2001 From: Huanghz2001 <1536277295@qq.com> Date: Sun, 24 Dec 2023 23:53:41 +0800 Subject: [PATCH 5/6] Modified test to avoid exception message treated as regex --- pandas/tests/reshape/merge/test_merge.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pandas/tests/reshape/merge/test_merge.py b/pandas/tests/reshape/merge/test_merge.py index 7760b621ed49a..ab8d22e567d27 100644 --- a/pandas/tests/reshape/merge/test_merge.py +++ b/pandas/tests/reshape/merge/test_merge.py @@ -2999,12 +2999,12 @@ def test_merge_datetime_and_timedelta(how): f"You are trying to merge on {left['key'].dtype} and {right['key'].dtype} " "columns for key 'key'. If you wish to proceed you should use pd.concat" ) - with pytest.raises(ValueError, match=msg): + with pytest.raises(ValueError, match=re.escape(msg)): left.merge(right, on="key", how=how) msg = ( f"You are trying to merge on {right['key'].dtype} and {left['key'].dtype} " "columns for key 'key'. If you wish to proceed you should use pd.concat" ) - with pytest.raises(ValueError, match=msg): + with pytest.raises(ValueError, match=re.escape(msg)): right.merge(left, on="key", how=how) From f7cb5d5f366eb539b8705d854062140724f17d2a Mon Sep 17 00:00:00 2001 From: Huanghz2001 <1536277295@qq.com> Date: Thu, 28 Dec 2023 14:10:55 +0800 Subject: [PATCH 6/6] Check dtype.kind instead of using is_datetime64_dtype() and is_timedelta64_dtype() methods --- pandas/core/reshape/merge.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pandas/core/reshape/merge.py b/pandas/core/reshape/merge.py index 5c00a76745c2b..320e4e33a29fb 100644 --- a/pandas/core/reshape/merge.py +++ b/pandas/core/reshape/merge.py @@ -52,7 +52,6 @@ ensure_object, is_bool, is_bool_dtype, - is_datetime64_dtype, is_float_dtype, is_integer, is_integer_dtype, @@ -61,7 +60,6 @@ is_numeric_dtype, is_object_dtype, is_string_dtype, - is_timedelta64_dtype, needs_i8_conversion, ) from pandas.core.dtypes.dtypes import ( @@ -1529,9 +1527,9 @@ def _maybe_coerce_merge_keys(self) -> None: # allows datetime with different resolutions continue # datetime and timedelta not allowed - elif is_datetime64_dtype(lk.dtype) and is_timedelta64_dtype(rk.dtype): + elif lk.dtype.kind == "M" and rk.dtype.kind == "m": raise ValueError(msg) - elif is_timedelta64_dtype(lk.dtype) and is_datetime64_dtype(rk.dtype): + elif lk.dtype.kind == "m" and rk.dtype.kind == "M": raise ValueError(msg) elif is_object_dtype(lk.dtype) and is_object_dtype(rk.dtype):