from datetime import date import numpy as np import pytest from pandas import ( DatetimeIndex, MultiIndex, NaT, Series, Timestamp, date_range, period_range, ) from pandas.core.indexing import IndexingError import pandas.testing as tm from pandas.tseries.offsets import BDay class TestSetitemDT64Values: def test_setitem_none_nan(self): series = Series(date_range("1/1/2000", periods=10)) series[3] = None assert series[3] is NaT series[3:5] = None assert series[4] is NaT series[5] = np.nan assert series[5] is NaT series[5:7] = np.nan assert series[6] is NaT def test_setitem_multiindex_empty_slice(self): # https://github.com/pandas-dev/pandas/issues/35878 idx = MultiIndex.from_tuples([("a", 1), ("b", 2)]) result = Series([1, 2], index=idx) expected = result.copy() result.loc[[]] = 0 tm.assert_series_equal(result, expected) def test_setitem_with_string_index(self): # GH#23451 ser = Series([1, 2, 3], index=["Date", "b", "other"]) ser["Date"] = date.today() assert ser.Date == date.today() assert ser["Date"] == date.today() def test_setitem_with_different_tz_casts_to_object(self): # GH#24024 ser = Series(date_range("2000", periods=2, tz="US/Central")) ser[0] = Timestamp("2000", tz="US/Eastern") expected = Series( [ Timestamp("2000-01-01 00:00:00-05:00", tz="US/Eastern"), Timestamp("2000-01-02 00:00:00-06:00", tz="US/Central"), ], dtype=object, ) tm.assert_series_equal(ser, expected) def test_setitem_tuple_with_datetimetz_values(self): # GH#20441 arr = date_range("2017", periods=4, tz="US/Eastern") index = [(0, 1), (0, 2), (0, 3), (0, 4)] result = Series(arr, index=index) expected = result.copy() result[(0, 1)] = np.nan expected.iloc[0] = np.nan tm.assert_series_equal(result, expected) class TestSetitemPeriodDtype: @pytest.mark.parametrize("na_val", [None, np.nan]) def test_setitem_na_period_dtype_casts_to_nat(self, na_val): ser = Series(period_range("2000-01-01", periods=10, freq="D")) ser[3] = na_val assert ser[3] is NaT ser[3:5] = na_val assert ser[4] is NaT class TestSetitemBooleanMask: def test_setitem_boolean(self, string_series): mask = string_series > string_series.median() # similar indexed series result = string_series.copy() result[mask] = string_series * 2 expected = string_series * 2 tm.assert_series_equal(result[mask], expected[mask]) # needs alignment result = string_series.copy() result[mask] = (string_series * 2)[0:5] expected = (string_series * 2)[0:5].reindex_like(string_series) expected[-mask] = string_series[mask] tm.assert_series_equal(result[mask], expected[mask]) def test_setitem_boolean_corner(self, datetime_series): ts = datetime_series mask_shifted = ts.shift(1, freq=BDay()) > ts.median() msg = ( r"Unalignable boolean Series provided as indexer \(index of " r"the boolean Series and of the indexed object do not match" ) with pytest.raises(IndexingError, match=msg): ts[mask_shifted] = 1 with pytest.raises(IndexingError, match=msg): ts.loc[mask_shifted] = 1 def test_setitem_boolean_different_order(self, string_series): ordered = string_series.sort_values() copy = string_series.copy() copy[ordered > 0] = 0 expected = string_series.copy() expected[expected > 0] = 0 tm.assert_series_equal(copy, expected) @pytest.mark.parametrize("func", [list, np.array, Series]) def test_setitem_boolean_python_list(self, func): # GH19406 ser = Series([None, "b", None]) mask = func([True, False, True]) ser[mask] = ["a", "c"] expected = Series(["a", "b", "c"]) tm.assert_series_equal(ser, expected) @pytest.mark.parametrize("value", [None, NaT, np.nan]) def test_setitem_boolean_td64_values_cast_na(self, value): # GH#18586 series = Series([0, 1, 2], dtype="timedelta64[ns]") mask = series == series[0] series[mask] = value expected = Series([NaT, 1, 2], dtype="timedelta64[ns]") tm.assert_series_equal(series, expected) def test_setitem_boolean_nullable_int_types(self, any_numeric_dtype): # GH: 26468 ser = Series([5, 6, 7, 8], dtype=any_numeric_dtype) ser[ser > 6] = Series(range(4), dtype=any_numeric_dtype) expected = Series([5, 6, 2, 3], dtype=any_numeric_dtype) tm.assert_series_equal(ser, expected) ser = Series([5, 6, 7, 8], dtype=any_numeric_dtype) ser.loc[ser > 6] = Series(range(4), dtype=any_numeric_dtype) tm.assert_series_equal(ser, expected) ser = Series([5, 6, 7, 8], dtype=any_numeric_dtype) loc_ser = Series(range(4), dtype=any_numeric_dtype) ser.loc[ser > 6] = loc_ser.loc[loc_ser > 1] tm.assert_series_equal(ser, expected) class TestSetitemViewCopySemantics: def test_setitem_invalidates_datetime_index_freq(self): # GH#24096 altering a datetime64tz Series inplace invalidates the # `freq` attribute on the underlying DatetimeIndex dti = date_range("20130101", periods=3, tz="US/Eastern") ts = dti[1] ser = Series(dti) assert ser._values is not dti assert ser._values._data.base is not dti._data._data.base assert dti.freq == "D" ser.iloc[1] = NaT assert ser._values.freq is None # check that the DatetimeIndex was not altered in place assert ser._values is not dti assert ser._values._data.base is not dti._data._data.base assert dti[1] == ts assert dti.freq == "D" def test_dt64tz_setitem_does_not_mutate_dti(self): # GH#21907, GH#24096 dti = date_range("2016-01-01", periods=10, tz="US/Pacific") ts = dti[0] ser = Series(dti) assert ser._values is not dti assert ser._values._data.base is not dti._data._data.base assert ser._mgr.blocks[0].values is not dti assert ser._mgr.blocks[0].values._data.base is not dti._data._data.base ser[::3] = NaT assert ser[0] is NaT assert dti[0] == ts class TestSetitemCallable: def test_setitem_callable_key(self): # GH#12533 ser = Series([1, 2, 3, 4], index=list("ABCD")) ser[lambda x: "A"] = -1 expected = Series([-1, 2, 3, 4], index=list("ABCD")) tm.assert_series_equal(ser, expected) def test_setitem_callable_other(self): # GH#13299 inc = lambda x: x + 1 ser = Series([1, 2, -1, 4]) ser[ser < 0] = inc expected = Series([1, 2, inc, 4]) tm.assert_series_equal(ser, expected) class TestSetitemCasting: def test_setitem_nan_casts(self): # these induce dtype changes expected = Series([np.nan, 3, np.nan, 5, np.nan, 7, np.nan, 9, np.nan]) ser = Series([2, 3, 4, 5, 6, 7, 8, 9, 10]) ser[::2] = np.nan tm.assert_series_equal(ser, expected) # gets coerced to float, right? expected = Series([np.nan, 1, np.nan, 0]) ser = Series([True, True, False, False]) ser[::2] = np.nan tm.assert_series_equal(ser, expected) expected = Series([np.nan, np.nan, np.nan, np.nan, np.nan, 5, 6, 7, 8, 9]) ser = Series(np.arange(10)) ser[:5] = np.nan tm.assert_series_equal(ser, expected) class TestSetitemWithExpansion: def test_setitem_empty_series(self): # GH#10193 key = Timestamp("2012-01-01") series = Series(dtype=object) series[key] = 47 expected = Series(47, [key]) tm.assert_series_equal(series, expected) def test_setitem_empty_series_datetimeindex_preserves_freq(self): # GH#33573 our index should retain its freq series = Series([], DatetimeIndex([], freq="D"), dtype=object) key = Timestamp("2012-01-01") series[key] = 47 expected = Series(47, DatetimeIndex([key], freq="D")) tm.assert_series_equal(series, expected) assert series.index.freq == expected.index.freq def test_setitem_scalar_into_readonly_backing_data(): # GH#14359: test that you cannot mutate a read only buffer array = np.zeros(5) array.flags.writeable = False # make the array immutable series = Series(array) for n in range(len(series)): msg = "assignment destination is read-only" with pytest.raises(ValueError, match=msg): series[n] = 1 assert array[n] == 0 def test_setitem_slice_into_readonly_backing_data(): # GH#14359: test that you cannot mutate a read only buffer array = np.zeros(5) array.flags.writeable = False # make the array immutable series = Series(array) msg = "assignment destination is read-only" with pytest.raises(ValueError, match=msg): series[1:3] = 1 assert not array.any()