diff --git a/doc/source/whatsnew/v0.25.2.rst b/doc/source/whatsnew/v0.25.2.rst index 69f324211e5b2..de411ef63680a 100644 --- a/doc/source/whatsnew/v0.25.2.rst +++ b/doc/source/whatsnew/v0.25.2.rst @@ -52,7 +52,6 @@ Indexing - - - -- Missing ^^^^^^^ diff --git a/doc/source/whatsnew/v1.0.0.rst b/doc/source/whatsnew/v1.0.0.rst index 77807a338d21f..2913d88fcd15f 100644 --- a/doc/source/whatsnew/v1.0.0.rst +++ b/doc/source/whatsnew/v1.0.0.rst @@ -170,6 +170,7 @@ Indexing - Bug in assignment using a reverse slicer (:issue:`26939`) - Bug in reindexing a :meth:`PeriodIndex` with another type of index that contained a `Period` (:issue:`28323`) (:issue:`28337`) +- Fix assignment of column via `.loc` with numpy non-ns datetime type (:issue:`27395`) Missing ^^^^^^^ diff --git a/pandas/core/dtypes/cast.py b/pandas/core/dtypes/cast.py index 749dd735e159d..ac9b57dc8d342 100644 --- a/pandas/core/dtypes/cast.py +++ b/pandas/core/dtypes/cast.py @@ -1029,7 +1029,10 @@ def maybe_cast_to_datetime(value, dtype, errors="raise"): ) if is_datetime64 and not is_dtype_equal(dtype, _NS_DTYPE): - if dtype.name in ("datetime64", "datetime64[ns]"): + + # pandas supports dtype whose granularity is less than [ns] + # e.g., [ps], [fs], [as] + if dtype <= np.dtype("M8[ns]"): if dtype.name == "datetime64": raise ValueError(msg.format(dtype=dtype.name)) dtype = _NS_DTYPE @@ -1047,7 +1050,10 @@ def maybe_cast_to_datetime(value, dtype, errors="raise"): value = [value] elif is_timedelta64 and not is_dtype_equal(dtype, _TD_DTYPE): - if dtype.name in ("timedelta64", "timedelta64[ns]"): + + # pandas supports dtype whose granularity is less than [ns] + # e.g., [ps], [fs], [as] + if dtype <= np.dtype("m8[ns]"): if dtype.name == "timedelta64": raise ValueError(msg.format(dtype=dtype.name)) dtype = _TD_DTYPE diff --git a/pandas/tests/indexing/test_loc.py b/pandas/tests/indexing/test_loc.py index 9845b1ac3a4b9..35291efecd1ac 100644 --- a/pandas/tests/indexing/test_loc.py +++ b/pandas/tests/indexing/test_loc.py @@ -690,6 +690,28 @@ def test_loc_setitem_consistency_slice_column_len(self): ) tm.assert_series_equal(df[("Respondent", "Duration")], expected) + @pytest.mark.parametrize("unit", ["Y", "M", "D", "h", "m", "s", "ms", "us"]) + def test_loc_assign_non_ns_datetime(self, unit): + # GH 27395, non-ns dtype assignment via .loc should work + # and return the same result when using simple assignment + df = DataFrame( + { + "timestamp": [ + np.datetime64("2017-02-11 12:41:29"), + np.datetime64("1991-11-07 04:22:37"), + ] + } + ) + + df.loc[:, unit] = df.loc[:, "timestamp"].values.astype( + "datetime64[{unit}]".format(unit=unit) + ) + df["expected"] = df.loc[:, "timestamp"].values.astype( + "datetime64[{unit}]".format(unit=unit) + ) + expected = Series(df.loc[:, "expected"], name=unit) + tm.assert_series_equal(df.loc[:, unit], expected) + def test_loc_setitem_frame(self): df = self.frame_labels