diff --git a/asv_bench/benchmarks/series_methods.py b/asv_bench/benchmarks/series_methods.py index d8578ed604ae3..d43314f0d461f 100644 --- a/asv_bench/benchmarks/series_methods.py +++ b/asv_bench/benchmarks/series_methods.py @@ -3,6 +3,7 @@ import numpy as np from pandas import ( + Index, NaT, Series, date_range, @@ -12,20 +13,23 @@ class SeriesConstructor: - - params = [None, "dict"] - param_names = ["data"] - - def setup(self, data): + def setup(self): self.idx = date_range( start=datetime(2015, 10, 26), end=datetime(2016, 1, 1), freq="50s" ) - dict_data = dict(zip(self.idx, range(len(self.idx)))) - self.data = None if data is None else dict_data + self.data = dict(zip(self.idx, range(len(self.idx)))) + self.array = np.array([1, 2, 3]) + self.idx2 = Index(["a", "b", "c"]) - def time_constructor(self, data): + def time_constructor_dict(self): Series(data=self.data, index=self.idx) + def time_constructor_no_data(self): + Series(data=None, index=self.idx) + + def time_constructor_fastpath(self): + Series(self.array, index=self.idx2, name="name", fastpath=True) + class ToFrame: params = [["int64", "datetime64[ns]", "category", "Int64"], [None, "foo"]] diff --git a/doc/source/whatsnew/v1.5.0.rst b/doc/source/whatsnew/v1.5.0.rst index fa19a49b7ff45..729d08f7d659b 100644 --- a/doc/source/whatsnew/v1.5.0.rst +++ b/doc/source/whatsnew/v1.5.0.rst @@ -216,6 +216,7 @@ Indexing - Bug in :meth:`Series.__setitem__` with a non-integer :class:`Index` when using an integer key to set a value that cannot be set inplace where a ``ValueError`` was raised insead of casting to a common dtype (:issue:`45070`) - Bug when setting a value too large for a :class:`Series` dtype failing to coerce to a common type (:issue:`26049`, :issue:`32878`) - Bug in :meth:`Series.__setitem__` where setting :attr:`NA` into a numeric-dtpye :class:`Series` would incorrectly upcast to object-dtype rather than treating the value as ``np.nan`` (:issue:`44199`) +- Bug in getting a column from a DataFrame with an object-dtype row index with datetime-like values: the resulting Series now preserves the exact object-dtype Index from the parent DataFrame (:issue:`42950`) - Missing diff --git a/pandas/core/series.py b/pandas/core/series.py index cc60cd63ba3ab..c0080789a277b 100644 --- a/pandas/core/series.py +++ b/pandas/core/series.py @@ -457,8 +457,12 @@ def __init__( data = SingleArrayManager.from_array(data, index) NDFrame.__init__(self, data) - self.name = name - self._set_axis(0, index, fastpath=True) + if fastpath: + # skips validation of the name + object.__setattr__(self, "_name", name) + else: + self.name = name + self._set_axis(0, index) def _init_dict( self, data, index: Index | None = None, dtype: DtypeObj | None = None @@ -539,15 +543,14 @@ def _constructor_expanddim(self) -> type[DataFrame]: def _can_hold_na(self) -> bool: return self._mgr._can_hold_na - def _set_axis(self, axis: int, labels, fastpath: bool = False) -> None: + def _set_axis(self, axis: int, labels) -> None: """ Override generic, we want to set the _typ here. This is called from the cython code when we set the `index` attribute directly, e.g. `series.index = [1, 2, 3]`. """ - if not fastpath: - labels = ensure_index(labels) + labels = ensure_index(labels) if labels._is_all_dates: deep_labels = labels @@ -559,17 +562,13 @@ def _set_axis(self, axis: int, labels, fastpath: bool = False) -> None: ): try: labels = DatetimeIndex(labels) - # need to set here because we changed the index - if fastpath: - self._mgr.set_axis(axis, labels) except (tslibs.OutOfBoundsDatetime, ValueError): # labels may exceeds datetime bounds, # or not be a DatetimeIndex pass - if not fastpath: - # The ensure_index call above ensures we have an Index object - self._mgr.set_axis(axis, labels) + # The ensure_index call above ensures we have an Index object + self._mgr.set_axis(axis, labels) # ndarray compatibility @property diff --git a/pandas/tests/frame/indexing/test_indexing.py b/pandas/tests/frame/indexing/test_indexing.py index d84a2a3d18e81..5a97c591fd621 100644 --- a/pandas/tests/frame/indexing/test_indexing.py +++ b/pandas/tests/frame/indexing/test_indexing.py @@ -1526,6 +1526,21 @@ def test_loc_iloc_setitem_non_categorical_rhs( with pytest.raises(TypeError, match=msg1): indexer(df)[key] = ["c", "c"] + @pytest.mark.parametrize("indexer", [tm.getitem, tm.loc, tm.iloc]) + def test_getitem_preserve_object_index_with_dates(self, indexer): + # https://github.com/pandas-dev/pandas/pull/42950 - when selecting a column + # from dataframe, don't try to infer object dtype index on Series construction + idx = date_range("2012", periods=3).astype(object) + df = DataFrame({0: [1, 2, 3]}, index=idx) + assert df.index.dtype == object + + if indexer is tm.getitem: + ser = indexer(df)[0] + else: + ser = indexer(df)[:, 0] + + assert ser.index.dtype == object + class TestDepreactedIndexers: @pytest.mark.parametrize(