test_setitem.py 50 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642
  1. from datetime import (
  2. date,
  3. datetime,
  4. )
  5. import numpy as np
  6. import pytest
  7. from pandas.errors import IndexingError
  8. from pandas.core.dtypes.common import is_list_like
  9. from pandas import (
  10. NA,
  11. Categorical,
  12. DataFrame,
  13. DatetimeIndex,
  14. Index,
  15. Interval,
  16. IntervalIndex,
  17. MultiIndex,
  18. NaT,
  19. Period,
  20. Series,
  21. Timedelta,
  22. Timestamp,
  23. array,
  24. concat,
  25. date_range,
  26. interval_range,
  27. period_range,
  28. timedelta_range,
  29. )
  30. import pandas._testing as tm
  31. from pandas.tseries.offsets import BDay
  32. class TestSetitemDT64Values:
  33. def test_setitem_none_nan(self):
  34. series = Series(date_range("1/1/2000", periods=10))
  35. series[3] = None
  36. assert series[3] is NaT
  37. series[3:5] = None
  38. assert series[4] is NaT
  39. series[5] = np.nan
  40. assert series[5] is NaT
  41. series[5:7] = np.nan
  42. assert series[6] is NaT
  43. def test_setitem_multiindex_empty_slice(self):
  44. # https://github.com/pandas-dev/pandas/issues/35878
  45. idx = MultiIndex.from_tuples([("a", 1), ("b", 2)])
  46. result = Series([1, 2], index=idx)
  47. expected = result.copy()
  48. result.loc[[]] = 0
  49. tm.assert_series_equal(result, expected)
  50. def test_setitem_with_string_index(self):
  51. # GH#23451
  52. ser = Series([1, 2, 3], index=["Date", "b", "other"])
  53. ser["Date"] = date.today()
  54. assert ser.Date == date.today()
  55. assert ser["Date"] == date.today()
  56. def test_setitem_tuple_with_datetimetz_values(self):
  57. # GH#20441
  58. arr = date_range("2017", periods=4, tz="US/Eastern")
  59. index = [(0, 1), (0, 2), (0, 3), (0, 4)]
  60. result = Series(arr, index=index)
  61. expected = result.copy()
  62. result[(0, 1)] = np.nan
  63. expected.iloc[0] = np.nan
  64. tm.assert_series_equal(result, expected)
  65. @pytest.mark.parametrize("tz", ["US/Eastern", "UTC", "Asia/Tokyo"])
  66. def test_setitem_with_tz(self, tz, indexer_sli):
  67. orig = Series(date_range("2016-01-01", freq="H", periods=3, tz=tz))
  68. assert orig.dtype == f"datetime64[ns, {tz}]"
  69. exp = Series(
  70. [
  71. Timestamp("2016-01-01 00:00", tz=tz),
  72. Timestamp("2011-01-01 00:00", tz=tz),
  73. Timestamp("2016-01-01 02:00", tz=tz),
  74. ]
  75. )
  76. # scalar
  77. ser = orig.copy()
  78. indexer_sli(ser)[1] = Timestamp("2011-01-01", tz=tz)
  79. tm.assert_series_equal(ser, exp)
  80. # vector
  81. vals = Series(
  82. [Timestamp("2011-01-01", tz=tz), Timestamp("2012-01-01", tz=tz)],
  83. index=[1, 2],
  84. )
  85. assert vals.dtype == f"datetime64[ns, {tz}]"
  86. exp = Series(
  87. [
  88. Timestamp("2016-01-01 00:00", tz=tz),
  89. Timestamp("2011-01-01 00:00", tz=tz),
  90. Timestamp("2012-01-01 00:00", tz=tz),
  91. ]
  92. )
  93. ser = orig.copy()
  94. indexer_sli(ser)[[1, 2]] = vals
  95. tm.assert_series_equal(ser, exp)
  96. def test_setitem_with_tz_dst(self, indexer_sli):
  97. # GH#14146 trouble setting values near DST boundary
  98. tz = "US/Eastern"
  99. orig = Series(date_range("2016-11-06", freq="H", periods=3, tz=tz))
  100. assert orig.dtype == f"datetime64[ns, {tz}]"
  101. exp = Series(
  102. [
  103. Timestamp("2016-11-06 00:00-04:00", tz=tz),
  104. Timestamp("2011-01-01 00:00-05:00", tz=tz),
  105. Timestamp("2016-11-06 01:00-05:00", tz=tz),
  106. ]
  107. )
  108. # scalar
  109. ser = orig.copy()
  110. indexer_sli(ser)[1] = Timestamp("2011-01-01", tz=tz)
  111. tm.assert_series_equal(ser, exp)
  112. # vector
  113. vals = Series(
  114. [Timestamp("2011-01-01", tz=tz), Timestamp("2012-01-01", tz=tz)],
  115. index=[1, 2],
  116. )
  117. assert vals.dtype == f"datetime64[ns, {tz}]"
  118. exp = Series(
  119. [
  120. Timestamp("2016-11-06 00:00", tz=tz),
  121. Timestamp("2011-01-01 00:00", tz=tz),
  122. Timestamp("2012-01-01 00:00", tz=tz),
  123. ]
  124. )
  125. ser = orig.copy()
  126. indexer_sli(ser)[[1, 2]] = vals
  127. tm.assert_series_equal(ser, exp)
  128. def test_object_series_setitem_dt64array_exact_match(self):
  129. # make sure the dt64 isn't cast by numpy to integers
  130. # https://github.com/numpy/numpy/issues/12550
  131. ser = Series({"X": np.nan}, dtype=object)
  132. indexer = [True]
  133. # "exact_match" -> size of array being set matches size of ser
  134. value = np.array([4], dtype="M8[ns]")
  135. ser.iloc[indexer] = value
  136. expected = Series([value[0]], index=["X"], dtype=object)
  137. assert all(isinstance(x, np.datetime64) for x in expected.values)
  138. tm.assert_series_equal(ser, expected)
  139. class TestSetitemScalarIndexer:
  140. def test_setitem_negative_out_of_bounds(self):
  141. ser = Series(tm.rands_array(5, 10), index=tm.rands_array(10, 10))
  142. msg = "index -11 is out of bounds for axis 0 with size 10"
  143. with pytest.raises(IndexError, match=msg):
  144. ser[-11] = "foo"
  145. @pytest.mark.parametrize("indexer", [tm.loc, tm.at])
  146. @pytest.mark.parametrize("ser_index", [0, 1])
  147. def test_setitem_series_object_dtype(self, indexer, ser_index):
  148. # GH#38303
  149. ser = Series([0, 0], dtype="object")
  150. idxr = indexer(ser)
  151. idxr[0] = Series([42], index=[ser_index])
  152. expected = Series([Series([42], index=[ser_index]), 0], dtype="object")
  153. tm.assert_series_equal(ser, expected)
  154. @pytest.mark.parametrize("index, exp_value", [(0, 42), (1, np.nan)])
  155. def test_setitem_series(self, index, exp_value):
  156. # GH#38303
  157. ser = Series([0, 0])
  158. ser.loc[0] = Series([42], index=[index])
  159. expected = Series([exp_value, 0])
  160. tm.assert_series_equal(ser, expected)
  161. class TestSetitemSlices:
  162. def test_setitem_slice_float_raises(self, datetime_series):
  163. msg = (
  164. "cannot do slice indexing on DatetimeIndex with these indexers "
  165. r"\[{key}\] of type float"
  166. )
  167. with pytest.raises(TypeError, match=msg.format(key=r"4\.0")):
  168. datetime_series[4.0:10.0] = 0
  169. with pytest.raises(TypeError, match=msg.format(key=r"4\.5")):
  170. datetime_series[4.5:10.0] = 0
  171. def test_setitem_slice(self):
  172. ser = Series(range(10), index=list(range(10)))
  173. ser[-12:] = 0
  174. assert (ser == 0).all()
  175. ser[:-12] = 5
  176. assert (ser == 0).all()
  177. def test_setitem_slice_integers(self):
  178. ser = Series(np.random.randn(8), index=[2, 4, 6, 8, 10, 12, 14, 16])
  179. ser[:4] = 0
  180. assert (ser[:4] == 0).all()
  181. assert not (ser[4:] == 0).any()
  182. def test_setitem_slicestep(self):
  183. # caught this bug when writing tests
  184. series = Series(tm.makeIntIndex(20).astype(float), index=tm.makeIntIndex(20))
  185. series[::2] = 0
  186. assert (series[::2] == 0).all()
  187. def test_setitem_multiindex_slice(self, indexer_sli):
  188. # GH 8856
  189. mi = MultiIndex.from_product(([0, 1], list("abcde")))
  190. result = Series(np.arange(10, dtype=np.int64), mi)
  191. indexer_sli(result)[::4] = 100
  192. expected = Series([100, 1, 2, 3, 100, 5, 6, 7, 100, 9], mi)
  193. tm.assert_series_equal(result, expected)
  194. class TestSetitemBooleanMask:
  195. def test_setitem_mask_cast(self):
  196. # GH#2746
  197. # need to upcast
  198. ser = Series([1, 2], index=[1, 2], dtype="int64")
  199. ser[[True, False]] = Series([0], index=[1], dtype="int64")
  200. expected = Series([0, 2], index=[1, 2], dtype="int64")
  201. tm.assert_series_equal(ser, expected)
  202. def test_setitem_mask_align_and_promote(self):
  203. # GH#8387: test that changing types does not break alignment
  204. ts = Series(np.random.randn(100), index=np.arange(100, 0, -1)).round(5)
  205. mask = ts > 0
  206. left = ts.copy()
  207. right = ts[mask].copy().map(str)
  208. left[mask] = right
  209. expected = ts.map(lambda t: str(t) if t > 0 else t)
  210. tm.assert_series_equal(left, expected)
  211. def test_setitem_mask_promote_strs(self):
  212. ser = Series([0, 1, 2, 0])
  213. mask = ser > 0
  214. ser2 = ser[mask].map(str)
  215. ser[mask] = ser2
  216. expected = Series([0, "1", "2", 0])
  217. tm.assert_series_equal(ser, expected)
  218. def test_setitem_mask_promote(self):
  219. ser = Series([0, "foo", "bar", 0])
  220. mask = Series([False, True, True, False])
  221. ser2 = ser[mask]
  222. ser[mask] = ser2
  223. expected = Series([0, "foo", "bar", 0])
  224. tm.assert_series_equal(ser, expected)
  225. def test_setitem_boolean(self, string_series):
  226. mask = string_series > string_series.median()
  227. # similar indexed series
  228. result = string_series.copy()
  229. result[mask] = string_series * 2
  230. expected = string_series * 2
  231. tm.assert_series_equal(result[mask], expected[mask])
  232. # needs alignment
  233. result = string_series.copy()
  234. result[mask] = (string_series * 2)[0:5]
  235. expected = (string_series * 2)[0:5].reindex_like(string_series)
  236. expected[-mask] = string_series[mask]
  237. tm.assert_series_equal(result[mask], expected[mask])
  238. def test_setitem_boolean_corner(self, datetime_series):
  239. ts = datetime_series
  240. mask_shifted = ts.shift(1, freq=BDay()) > ts.median()
  241. msg = (
  242. r"Unalignable boolean Series provided as indexer \(index of "
  243. r"the boolean Series and of the indexed object do not match"
  244. )
  245. with pytest.raises(IndexingError, match=msg):
  246. ts[mask_shifted] = 1
  247. with pytest.raises(IndexingError, match=msg):
  248. ts.loc[mask_shifted] = 1
  249. def test_setitem_boolean_different_order(self, string_series):
  250. ordered = string_series.sort_values()
  251. copy = string_series.copy()
  252. copy[ordered > 0] = 0
  253. expected = string_series.copy()
  254. expected[expected > 0] = 0
  255. tm.assert_series_equal(copy, expected)
  256. @pytest.mark.parametrize("func", [list, np.array, Series])
  257. def test_setitem_boolean_python_list(self, func):
  258. # GH19406
  259. ser = Series([None, "b", None])
  260. mask = func([True, False, True])
  261. ser[mask] = ["a", "c"]
  262. expected = Series(["a", "b", "c"])
  263. tm.assert_series_equal(ser, expected)
  264. def test_setitem_boolean_nullable_int_types(self, any_numeric_ea_dtype):
  265. # GH: 26468
  266. ser = Series([5, 6, 7, 8], dtype=any_numeric_ea_dtype)
  267. ser[ser > 6] = Series(range(4), dtype=any_numeric_ea_dtype)
  268. expected = Series([5, 6, 2, 3], dtype=any_numeric_ea_dtype)
  269. tm.assert_series_equal(ser, expected)
  270. ser = Series([5, 6, 7, 8], dtype=any_numeric_ea_dtype)
  271. ser.loc[ser > 6] = Series(range(4), dtype=any_numeric_ea_dtype)
  272. tm.assert_series_equal(ser, expected)
  273. ser = Series([5, 6, 7, 8], dtype=any_numeric_ea_dtype)
  274. loc_ser = Series(range(4), dtype=any_numeric_ea_dtype)
  275. ser.loc[ser > 6] = loc_ser.loc[loc_ser > 1]
  276. tm.assert_series_equal(ser, expected)
  277. def test_setitem_with_bool_mask_and_values_matching_n_trues_in_length(self):
  278. # GH#30567
  279. ser = Series([None] * 10)
  280. mask = [False] * 3 + [True] * 5 + [False] * 2
  281. ser[mask] = range(5)
  282. result = ser
  283. expected = Series([None] * 3 + list(range(5)) + [None] * 2).astype("object")
  284. tm.assert_series_equal(result, expected)
  285. def test_setitem_nan_with_bool(self):
  286. # GH 13034
  287. result = Series([True, False, True])
  288. result[0] = np.nan
  289. expected = Series([np.nan, False, True], dtype=object)
  290. tm.assert_series_equal(result, expected)
  291. def test_setitem_mask_smallint_upcast(self):
  292. orig = Series([1, 2, 3], dtype="int8")
  293. alt = np.array([999, 1000, 1001], dtype=np.int64)
  294. mask = np.array([True, False, True])
  295. ser = orig.copy()
  296. ser[mask] = Series(alt)
  297. expected = Series([999, 2, 1001])
  298. tm.assert_series_equal(ser, expected)
  299. ser2 = orig.copy()
  300. ser2.mask(mask, alt, inplace=True)
  301. tm.assert_series_equal(ser2, expected)
  302. ser3 = orig.copy()
  303. res = ser3.where(~mask, Series(alt))
  304. tm.assert_series_equal(res, expected)
  305. def test_setitem_mask_smallint_no_upcast(self):
  306. # like test_setitem_mask_smallint_upcast, but while we can't hold 'alt',
  307. # we *can* hold alt[mask] without casting
  308. orig = Series([1, 2, 3], dtype="uint8")
  309. alt = Series([245, 1000, 246], dtype=np.int64)
  310. mask = np.array([True, False, True])
  311. ser = orig.copy()
  312. ser[mask] = alt
  313. expected = Series([245, 2, 246], dtype="uint8")
  314. tm.assert_series_equal(ser, expected)
  315. ser2 = orig.copy()
  316. ser2.mask(mask, alt, inplace=True)
  317. tm.assert_series_equal(ser2, expected)
  318. # FIXME: don't leave commented-out
  319. # FIXME: ser.where(~mask, alt) unnecessarily upcasts to int64
  320. # ser3 = orig.copy()
  321. # res = ser3.where(~mask, alt)
  322. # tm.assert_series_equal(res, expected)
  323. class TestSetitemViewCopySemantics:
  324. def test_setitem_invalidates_datetime_index_freq(self, using_copy_on_write):
  325. # GH#24096 altering a datetime64tz Series inplace invalidates the
  326. # `freq` attribute on the underlying DatetimeIndex
  327. dti = date_range("20130101", periods=3, tz="US/Eastern")
  328. ts = dti[1]
  329. ser = Series(dti)
  330. assert ser._values is not dti
  331. if using_copy_on_write:
  332. assert ser._values._ndarray.base is dti._data._ndarray.base
  333. else:
  334. assert ser._values._ndarray.base is not dti._data._ndarray.base
  335. assert dti.freq == "D"
  336. ser.iloc[1] = NaT
  337. assert ser._values.freq is None
  338. # check that the DatetimeIndex was not altered in place
  339. assert ser._values is not dti
  340. assert ser._values._ndarray.base is not dti._data._ndarray.base
  341. assert dti[1] == ts
  342. assert dti.freq == "D"
  343. def test_dt64tz_setitem_does_not_mutate_dti(self, using_copy_on_write):
  344. # GH#21907, GH#24096
  345. dti = date_range("2016-01-01", periods=10, tz="US/Pacific")
  346. ts = dti[0]
  347. ser = Series(dti)
  348. assert ser._values is not dti
  349. if using_copy_on_write:
  350. assert ser._values._ndarray.base is dti._data._ndarray.base
  351. assert ser._mgr.arrays[0]._ndarray.base is dti._data._ndarray.base
  352. else:
  353. assert ser._values._ndarray.base is not dti._data._ndarray.base
  354. assert ser._mgr.arrays[0]._ndarray.base is not dti._data._ndarray.base
  355. assert ser._mgr.arrays[0] is not dti
  356. ser[::3] = NaT
  357. assert ser[0] is NaT
  358. assert dti[0] == ts
  359. class TestSetitemCallable:
  360. def test_setitem_callable_key(self):
  361. # GH#12533
  362. ser = Series([1, 2, 3, 4], index=list("ABCD"))
  363. ser[lambda x: "A"] = -1
  364. expected = Series([-1, 2, 3, 4], index=list("ABCD"))
  365. tm.assert_series_equal(ser, expected)
  366. def test_setitem_callable_other(self):
  367. # GH#13299
  368. inc = lambda x: x + 1
  369. ser = Series([1, 2, -1, 4])
  370. ser[ser < 0] = inc
  371. expected = Series([1, 2, inc, 4])
  372. tm.assert_series_equal(ser, expected)
  373. class TestSetitemWithExpansion:
  374. def test_setitem_empty_series(self):
  375. # GH#10193
  376. key = Timestamp("2012-01-01")
  377. series = Series(dtype=object)
  378. series[key] = 47
  379. expected = Series(47, [key])
  380. tm.assert_series_equal(series, expected)
  381. def test_setitem_empty_series_datetimeindex_preserves_freq(self):
  382. # GH#33573 our index should retain its freq
  383. series = Series([], DatetimeIndex([], freq="D"), dtype=object)
  384. key = Timestamp("2012-01-01")
  385. series[key] = 47
  386. expected = Series(47, DatetimeIndex([key], freq="D"))
  387. tm.assert_series_equal(series, expected)
  388. assert series.index.freq == expected.index.freq
  389. def test_setitem_empty_series_timestamp_preserves_dtype(self):
  390. # GH 21881
  391. timestamp = Timestamp(1412526600000000000)
  392. series = Series([timestamp], index=["timestamp"], dtype=object)
  393. expected = series["timestamp"]
  394. series = Series([], dtype=object)
  395. series["anything"] = 300.0
  396. series["timestamp"] = timestamp
  397. result = series["timestamp"]
  398. assert result == expected
  399. @pytest.mark.parametrize(
  400. "td",
  401. [
  402. Timedelta("9 days"),
  403. Timedelta("9 days").to_timedelta64(),
  404. Timedelta("9 days").to_pytimedelta(),
  405. ],
  406. )
  407. def test_append_timedelta_does_not_cast(self, td):
  408. # GH#22717 inserting a Timedelta should _not_ cast to int64
  409. expected = Series(["x", td], index=[0, "td"], dtype=object)
  410. ser = Series(["x"])
  411. ser["td"] = td
  412. tm.assert_series_equal(ser, expected)
  413. assert isinstance(ser["td"], Timedelta)
  414. ser = Series(["x"])
  415. ser.loc["td"] = Timedelta("9 days")
  416. tm.assert_series_equal(ser, expected)
  417. assert isinstance(ser["td"], Timedelta)
  418. def test_setitem_with_expansion_type_promotion(self):
  419. # GH#12599
  420. ser = Series(dtype=object)
  421. ser["a"] = Timestamp("2016-01-01")
  422. ser["b"] = 3.0
  423. ser["c"] = "foo"
  424. expected = Series([Timestamp("2016-01-01"), 3.0, "foo"], index=["a", "b", "c"])
  425. tm.assert_series_equal(ser, expected)
  426. def test_setitem_not_contained(self, string_series):
  427. # set item that's not contained
  428. ser = string_series.copy()
  429. assert "foobar" not in ser.index
  430. ser["foobar"] = 1
  431. app = Series([1], index=["foobar"], name="series")
  432. expected = concat([string_series, app])
  433. tm.assert_series_equal(ser, expected)
  434. def test_setitem_keep_precision(self, any_numeric_ea_dtype):
  435. # GH#32346
  436. ser = Series([1, 2], dtype=any_numeric_ea_dtype)
  437. ser[2] = 10
  438. expected = Series([1, 2, 10], dtype=any_numeric_ea_dtype)
  439. tm.assert_series_equal(ser, expected)
  440. @pytest.mark.parametrize("indexer", [1, 2])
  441. @pytest.mark.parametrize(
  442. "na, target_na, dtype, target_dtype",
  443. [
  444. (NA, NA, "Int64", "Int64"),
  445. (NA, np.nan, "int64", "float64"),
  446. (NaT, NaT, "int64", "object"),
  447. (np.nan, NA, "Int64", "Int64"),
  448. (np.nan, NA, "Float64", "Float64"),
  449. (np.nan, np.nan, "int64", "float64"),
  450. ],
  451. )
  452. def test_setitem_enlarge_with_na(self, na, target_na, dtype, target_dtype, indexer):
  453. # GH#32346
  454. ser = Series([1, 2], dtype=dtype)
  455. ser[indexer] = na
  456. expected_values = [1, target_na] if indexer == 1 else [1, 2, target_na]
  457. expected = Series(expected_values, dtype=target_dtype)
  458. tm.assert_series_equal(ser, expected)
  459. def test_setitem_enlargement_object_none(self, nulls_fixture):
  460. # GH#48665
  461. ser = Series(["a", "b"])
  462. ser[3] = nulls_fixture
  463. expected = Series(["a", "b", nulls_fixture], index=[0, 1, 3])
  464. tm.assert_series_equal(ser, expected)
  465. assert ser[3] is nulls_fixture
  466. def test_setitem_scalar_into_readonly_backing_data():
  467. # GH#14359: test that you cannot mutate a read only buffer
  468. array = np.zeros(5)
  469. array.flags.writeable = False # make the array immutable
  470. series = Series(array, copy=False)
  471. for n in series.index:
  472. msg = "assignment destination is read-only"
  473. with pytest.raises(ValueError, match=msg):
  474. series[n] = 1
  475. assert array[n] == 0
  476. def test_setitem_slice_into_readonly_backing_data():
  477. # GH#14359: test that you cannot mutate a read only buffer
  478. array = np.zeros(5)
  479. array.flags.writeable = False # make the array immutable
  480. series = Series(array, copy=False)
  481. msg = "assignment destination is read-only"
  482. with pytest.raises(ValueError, match=msg):
  483. series[1:3] = 1
  484. assert not array.any()
  485. def test_setitem_categorical_assigning_ops():
  486. orig = Series(Categorical(["b", "b"], categories=["a", "b"]))
  487. ser = orig.copy()
  488. ser[:] = "a"
  489. exp = Series(Categorical(["a", "a"], categories=["a", "b"]))
  490. tm.assert_series_equal(ser, exp)
  491. ser = orig.copy()
  492. ser[1] = "a"
  493. exp = Series(Categorical(["b", "a"], categories=["a", "b"]))
  494. tm.assert_series_equal(ser, exp)
  495. ser = orig.copy()
  496. ser[ser.index > 0] = "a"
  497. exp = Series(Categorical(["b", "a"], categories=["a", "b"]))
  498. tm.assert_series_equal(ser, exp)
  499. ser = orig.copy()
  500. ser[[False, True]] = "a"
  501. exp = Series(Categorical(["b", "a"], categories=["a", "b"]))
  502. tm.assert_series_equal(ser, exp)
  503. ser = orig.copy()
  504. ser.index = ["x", "y"]
  505. ser["y"] = "a"
  506. exp = Series(Categorical(["b", "a"], categories=["a", "b"]), index=["x", "y"])
  507. tm.assert_series_equal(ser, exp)
  508. def test_setitem_nan_into_categorical():
  509. # ensure that one can set something to np.nan
  510. ser = Series(Categorical([1, 2, 3]))
  511. exp = Series(Categorical([1, np.nan, 3], categories=[1, 2, 3]))
  512. ser[1] = np.nan
  513. tm.assert_series_equal(ser, exp)
  514. class TestSetitemCasting:
  515. @pytest.mark.parametrize("unique", [True, False])
  516. @pytest.mark.parametrize("val", [3, 3.0, "3"], ids=type)
  517. def test_setitem_non_bool_into_bool(self, val, indexer_sli, unique):
  518. # dont cast these 3-like values to bool
  519. ser = Series([True, False])
  520. if not unique:
  521. ser.index = [1, 1]
  522. indexer_sli(ser)[1] = val
  523. assert type(ser.iloc[1]) == type(val)
  524. expected = Series([True, val], dtype=object, index=ser.index)
  525. if not unique and indexer_sli is not tm.iloc:
  526. expected = Series([val, val], dtype=object, index=[1, 1])
  527. tm.assert_series_equal(ser, expected)
  528. def test_setitem_boolean_array_into_npbool(self):
  529. # GH#45462
  530. ser = Series([True, False, True])
  531. values = ser._values
  532. arr = array([True, False, None])
  533. ser[:2] = arr[:2] # no NAs -> can set inplace
  534. assert ser._values is values
  535. ser[1:] = arr[1:] # has an NA -> cast to boolean dtype
  536. expected = Series(arr)
  537. tm.assert_series_equal(ser, expected)
  538. class SetitemCastingEquivalents:
  539. """
  540. Check each of several methods that _should_ be equivalent to `obj[key] = val`
  541. We assume that
  542. - obj.index is the default Index(range(len(obj)))
  543. - the setitem does not expand the obj
  544. """
  545. @pytest.fixture
  546. def is_inplace(self, obj, expected):
  547. """
  548. Whether we expect the setting to be in-place or not.
  549. """
  550. try:
  551. return expected.dtype == obj.dtype
  552. except TypeError:
  553. # older numpys
  554. return False
  555. def check_indexer(self, obj, key, expected, val, indexer, is_inplace):
  556. orig = obj
  557. obj = obj.copy()
  558. arr = obj._values
  559. indexer(obj)[key] = val
  560. tm.assert_series_equal(obj, expected)
  561. self._check_inplace(is_inplace, orig, arr, obj)
  562. def _check_inplace(self, is_inplace, orig, arr, obj):
  563. if is_inplace is None:
  564. # We are not (yet) checking whether setting is inplace or not
  565. pass
  566. elif is_inplace:
  567. if arr.dtype.kind in ["m", "M"]:
  568. # We may not have the same DTA/TDA, but will have the same
  569. # underlying data
  570. assert arr._ndarray is obj._values._ndarray
  571. else:
  572. assert obj._values is arr
  573. else:
  574. # otherwise original array should be unchanged
  575. tm.assert_equal(arr, orig._values)
  576. def test_int_key(self, obj, key, expected, val, indexer_sli, is_inplace):
  577. if not isinstance(key, int):
  578. return
  579. self.check_indexer(obj, key, expected, val, indexer_sli, is_inplace)
  580. if indexer_sli is tm.loc:
  581. self.check_indexer(obj, key, expected, val, tm.at, is_inplace)
  582. elif indexer_sli is tm.iloc:
  583. self.check_indexer(obj, key, expected, val, tm.iat, is_inplace)
  584. rng = range(key, key + 1)
  585. self.check_indexer(obj, rng, expected, val, indexer_sli, is_inplace)
  586. if indexer_sli is not tm.loc:
  587. # Note: no .loc because that handles slice edges differently
  588. slc = slice(key, key + 1)
  589. self.check_indexer(obj, slc, expected, val, indexer_sli, is_inplace)
  590. ilkey = [key]
  591. self.check_indexer(obj, ilkey, expected, val, indexer_sli, is_inplace)
  592. indkey = np.array(ilkey)
  593. self.check_indexer(obj, indkey, expected, val, indexer_sli, is_inplace)
  594. genkey = (x for x in [key])
  595. self.check_indexer(obj, genkey, expected, val, indexer_sli, is_inplace)
  596. def test_slice_key(self, obj, key, expected, val, indexer_sli, is_inplace):
  597. if not isinstance(key, slice):
  598. return
  599. if indexer_sli is not tm.loc:
  600. # Note: no .loc because that handles slice edges differently
  601. self.check_indexer(obj, key, expected, val, indexer_sli, is_inplace)
  602. ilkey = list(range(len(obj)))[key]
  603. self.check_indexer(obj, ilkey, expected, val, indexer_sli, is_inplace)
  604. indkey = np.array(ilkey)
  605. self.check_indexer(obj, indkey, expected, val, indexer_sli, is_inplace)
  606. genkey = (x for x in indkey)
  607. self.check_indexer(obj, genkey, expected, val, indexer_sli, is_inplace)
  608. def test_mask_key(self, obj, key, expected, val, indexer_sli):
  609. # setitem with boolean mask
  610. mask = np.zeros(obj.shape, dtype=bool)
  611. mask[key] = True
  612. obj = obj.copy()
  613. if is_list_like(val) and len(val) < mask.sum():
  614. msg = "boolean index did not match indexed array along dimension"
  615. with pytest.raises(IndexError, match=msg):
  616. indexer_sli(obj)[mask] = val
  617. return
  618. indexer_sli(obj)[mask] = val
  619. tm.assert_series_equal(obj, expected)
  620. def test_series_where(self, obj, key, expected, val, is_inplace):
  621. mask = np.zeros(obj.shape, dtype=bool)
  622. mask[key] = True
  623. if is_list_like(val) and len(val) < len(obj):
  624. # Series.where is not valid here
  625. msg = "operands could not be broadcast together with shapes"
  626. with pytest.raises(ValueError, match=msg):
  627. obj.where(~mask, val)
  628. return
  629. orig = obj
  630. obj = obj.copy()
  631. arr = obj._values
  632. res = obj.where(~mask, val)
  633. tm.assert_series_equal(res, expected)
  634. self._check_inplace(is_inplace, orig, arr, obj)
  635. def test_index_where(self, obj, key, expected, val):
  636. mask = np.zeros(obj.shape, dtype=bool)
  637. mask[key] = True
  638. res = Index(obj).where(~mask, val)
  639. expected_idx = Index(expected, dtype=expected.dtype)
  640. tm.assert_index_equal(res, expected_idx)
  641. def test_index_putmask(self, obj, key, expected, val):
  642. mask = np.zeros(obj.shape, dtype=bool)
  643. mask[key] = True
  644. res = Index(obj).putmask(mask, val)
  645. tm.assert_index_equal(res, Index(expected, dtype=expected.dtype))
  646. @pytest.mark.parametrize(
  647. "obj,expected,key",
  648. [
  649. pytest.param(
  650. # GH#45568 setting a valid NA value into IntervalDtype[int] should
  651. # cast to IntervalDtype[float]
  652. Series(interval_range(1, 5)),
  653. Series(
  654. [Interval(1, 2), np.nan, Interval(3, 4), Interval(4, 5)],
  655. dtype="interval[float64]",
  656. ),
  657. 1,
  658. id="interval_int_na_value",
  659. ),
  660. pytest.param(
  661. # these induce dtype changes
  662. Series([2, 3, 4, 5, 6, 7, 8, 9, 10]),
  663. Series([np.nan, 3, np.nan, 5, np.nan, 7, np.nan, 9, np.nan]),
  664. slice(None, None, 2),
  665. id="int_series_slice_key_step",
  666. ),
  667. pytest.param(
  668. Series([True, True, False, False]),
  669. Series([np.nan, True, np.nan, False], dtype=object),
  670. slice(None, None, 2),
  671. id="bool_series_slice_key_step",
  672. ),
  673. pytest.param(
  674. # these induce dtype changes
  675. Series(np.arange(10)),
  676. Series([np.nan, np.nan, np.nan, np.nan, np.nan, 5, 6, 7, 8, 9]),
  677. slice(None, 5),
  678. id="int_series_slice_key",
  679. ),
  680. pytest.param(
  681. # changes dtype GH#4463
  682. Series([1, 2, 3]),
  683. Series([np.nan, 2, 3]),
  684. 0,
  685. id="int_series_int_key",
  686. ),
  687. pytest.param(
  688. # changes dtype GH#4463
  689. Series([False]),
  690. Series([np.nan], dtype=object),
  691. # TODO: maybe go to float64 since we are changing the _whole_ Series?
  692. 0,
  693. id="bool_series_int_key_change_all",
  694. ),
  695. pytest.param(
  696. # changes dtype GH#4463
  697. Series([False, True]),
  698. Series([np.nan, True], dtype=object),
  699. 0,
  700. id="bool_series_int_key",
  701. ),
  702. ],
  703. )
  704. class TestSetitemCastingEquivalents(SetitemCastingEquivalents):
  705. @pytest.fixture(params=[np.nan, np.float64("NaN"), None, NA])
  706. def val(self, request):
  707. """
  708. NA values that should generally be valid_na for *all* dtypes.
  709. Include both python float NaN and np.float64; only np.float64 has a
  710. `dtype` attribute.
  711. """
  712. return request.param
  713. class TestSetitemTimedelta64IntoNumeric(SetitemCastingEquivalents):
  714. # timedelta64 should not be treated as integers when setting into
  715. # numeric Series
  716. @pytest.fixture
  717. def val(self):
  718. td = np.timedelta64(4, "ns")
  719. return td
  720. # TODO: could also try np.full((1,), td)
  721. @pytest.fixture(params=[complex, int, float])
  722. def dtype(self, request):
  723. return request.param
  724. @pytest.fixture
  725. def obj(self, dtype):
  726. arr = np.arange(5).astype(dtype)
  727. ser = Series(arr)
  728. return ser
  729. @pytest.fixture
  730. def expected(self, dtype):
  731. arr = np.arange(5).astype(dtype)
  732. ser = Series(arr)
  733. ser = ser.astype(object)
  734. ser.iloc[0] = np.timedelta64(4, "ns")
  735. return ser
  736. @pytest.fixture
  737. def key(self):
  738. return 0
  739. class TestSetitemDT64IntoInt(SetitemCastingEquivalents):
  740. # GH#39619 dont cast dt64 to int when doing this setitem
  741. @pytest.fixture(params=["M8[ns]", "m8[ns]"])
  742. def dtype(self, request):
  743. return request.param
  744. @pytest.fixture
  745. def scalar(self, dtype):
  746. val = np.datetime64("2021-01-18 13:25:00", "ns")
  747. if dtype == "m8[ns]":
  748. val = val - val
  749. return val
  750. @pytest.fixture
  751. def expected(self, scalar):
  752. expected = Series([scalar, scalar, 3], dtype=object)
  753. assert isinstance(expected[0], type(scalar))
  754. return expected
  755. @pytest.fixture
  756. def obj(self):
  757. return Series([1, 2, 3])
  758. @pytest.fixture
  759. def key(self):
  760. return slice(None, -1)
  761. @pytest.fixture(params=[None, list, np.array])
  762. def val(self, scalar, request):
  763. box = request.param
  764. if box is None:
  765. return scalar
  766. return box([scalar, scalar])
  767. class TestSetitemNAPeriodDtype(SetitemCastingEquivalents):
  768. # Setting compatible NA values into Series with PeriodDtype
  769. @pytest.fixture
  770. def expected(self, key):
  771. exp = Series(period_range("2000-01-01", periods=10, freq="D"))
  772. exp._values.view("i8")[key] = NaT._value
  773. assert exp[key] is NaT or all(x is NaT for x in exp[key])
  774. return exp
  775. @pytest.fixture
  776. def obj(self):
  777. return Series(period_range("2000-01-01", periods=10, freq="D"))
  778. @pytest.fixture(params=[3, slice(3, 5)])
  779. def key(self, request):
  780. return request.param
  781. @pytest.fixture(params=[None, np.nan])
  782. def val(self, request):
  783. return request.param
  784. class TestSetitemNADatetimeLikeDtype(SetitemCastingEquivalents):
  785. # some nat-like values should be cast to datetime64/timedelta64 when
  786. # inserting into a datetime64/timedelta64 series. Others should coerce
  787. # to object and retain their dtypes.
  788. # GH#18586 for td64 and boolean mask case
  789. @pytest.fixture(
  790. params=["m8[ns]", "M8[ns]", "datetime64[ns, UTC]", "datetime64[ns, US/Central]"]
  791. )
  792. def dtype(self, request):
  793. return request.param
  794. @pytest.fixture
  795. def obj(self, dtype):
  796. i8vals = date_range("2016-01-01", periods=3).asi8
  797. idx = Index(i8vals, dtype=dtype)
  798. assert idx.dtype == dtype
  799. return Series(idx)
  800. @pytest.fixture(
  801. params=[
  802. None,
  803. np.nan,
  804. NaT,
  805. np.timedelta64("NaT", "ns"),
  806. np.datetime64("NaT", "ns"),
  807. ]
  808. )
  809. def val(self, request):
  810. return request.param
  811. @pytest.fixture
  812. def is_inplace(self, val, obj):
  813. # td64 -> cast to object iff val is datetime64("NaT")
  814. # dt64 -> cast to object iff val is timedelta64("NaT")
  815. # dt64tz -> cast to object with anything _but_ NaT
  816. return val is NaT or val is None or val is np.nan or obj.dtype == val.dtype
  817. @pytest.fixture
  818. def expected(self, obj, val, is_inplace):
  819. dtype = obj.dtype if is_inplace else object
  820. expected = Series([val] + list(obj[1:]), dtype=dtype)
  821. return expected
  822. @pytest.fixture
  823. def key(self):
  824. return 0
  825. class TestSetitemMismatchedTZCastsToObject(SetitemCastingEquivalents):
  826. # GH#24024
  827. @pytest.fixture
  828. def obj(self):
  829. return Series(date_range("2000", periods=2, tz="US/Central"))
  830. @pytest.fixture
  831. def val(self):
  832. return Timestamp("2000", tz="US/Eastern")
  833. @pytest.fixture
  834. def key(self):
  835. return 0
  836. @pytest.fixture
  837. def expected(self, obj, val):
  838. # pre-2.0 this would cast to object, in 2.0 we cast the val to
  839. # the target tz
  840. expected = Series(
  841. [
  842. val.tz_convert("US/Central"),
  843. Timestamp("2000-01-02 00:00:00-06:00", tz="US/Central"),
  844. ],
  845. dtype=obj.dtype,
  846. )
  847. return expected
  848. @pytest.mark.parametrize(
  849. "obj,expected",
  850. [
  851. # For numeric series, we should coerce to NaN.
  852. (Series([1, 2, 3]), Series([np.nan, 2, 3])),
  853. (Series([1.0, 2.0, 3.0]), Series([np.nan, 2.0, 3.0])),
  854. # For datetime series, we should coerce to NaT.
  855. (
  856. Series([datetime(2000, 1, 1), datetime(2000, 1, 2), datetime(2000, 1, 3)]),
  857. Series([NaT, datetime(2000, 1, 2), datetime(2000, 1, 3)]),
  858. ),
  859. # For objects, we should preserve the None value.
  860. (Series(["foo", "bar", "baz"]), Series([None, "bar", "baz"])),
  861. ],
  862. )
  863. class TestSeriesNoneCoercion(SetitemCastingEquivalents):
  864. @pytest.fixture
  865. def key(self):
  866. return 0
  867. @pytest.fixture
  868. def val(self):
  869. return None
  870. class TestSetitemFloatIntervalWithIntIntervalValues(SetitemCastingEquivalents):
  871. # GH#44201 Cast to shared IntervalDtype rather than object
  872. def test_setitem_example(self):
  873. # Just a case here to make obvious what this test class is aimed at
  874. idx = IntervalIndex.from_breaks(range(4))
  875. obj = Series(idx)
  876. val = Interval(0.5, 1.5)
  877. obj[0] = val
  878. assert obj.dtype == "Interval[float64, right]"
  879. @pytest.fixture
  880. def obj(self):
  881. idx = IntervalIndex.from_breaks(range(4))
  882. return Series(idx)
  883. @pytest.fixture
  884. def val(self):
  885. return Interval(0.5, 1.5)
  886. @pytest.fixture
  887. def key(self):
  888. return 0
  889. @pytest.fixture
  890. def expected(self, obj, val):
  891. data = [val] + list(obj[1:])
  892. idx = IntervalIndex(data, dtype="Interval[float64]")
  893. return Series(idx)
  894. class TestSetitemRangeIntoIntegerSeries(SetitemCastingEquivalents):
  895. # GH#44261 Setting a range with sufficiently-small integers into
  896. # small-itemsize integer dtypes should not need to upcast
  897. @pytest.fixture
  898. def obj(self, any_int_numpy_dtype):
  899. dtype = np.dtype(any_int_numpy_dtype)
  900. ser = Series(range(5), dtype=dtype)
  901. return ser
  902. @pytest.fixture
  903. def val(self):
  904. return range(2, 4)
  905. @pytest.fixture
  906. def key(self):
  907. return slice(0, 2)
  908. @pytest.fixture
  909. def expected(self, any_int_numpy_dtype):
  910. dtype = np.dtype(any_int_numpy_dtype)
  911. exp = Series([2, 3, 2, 3, 4], dtype=dtype)
  912. return exp
  913. @pytest.mark.parametrize(
  914. "val",
  915. [
  916. np.array([2.0, 3.0]),
  917. np.array([2.5, 3.5]),
  918. np.array([2**65, 2**65 + 1], dtype=np.float64), # all ints, but can't cast
  919. ],
  920. )
  921. class TestSetitemFloatNDarrayIntoIntegerSeries(SetitemCastingEquivalents):
  922. @pytest.fixture
  923. def obj(self):
  924. return Series(range(5), dtype=np.int64)
  925. @pytest.fixture
  926. def key(self):
  927. return slice(0, 2)
  928. @pytest.fixture
  929. def expected(self, val):
  930. if val[0] == 2:
  931. # NB: this condition is based on currently-hardcoded "val" cases
  932. dtype = np.int64
  933. else:
  934. dtype = np.float64
  935. res_values = np.array(range(5), dtype=dtype)
  936. res_values[:2] = val
  937. return Series(res_values)
  938. @pytest.mark.parametrize("val", [512, np.int16(512)])
  939. class TestSetitemIntoIntegerSeriesNeedsUpcast(SetitemCastingEquivalents):
  940. @pytest.fixture
  941. def obj(self):
  942. return Series([1, 2, 3], dtype=np.int8)
  943. @pytest.fixture
  944. def key(self):
  945. return 1
  946. @pytest.fixture
  947. def expected(self):
  948. return Series([1, 512, 3], dtype=np.int16)
  949. @pytest.mark.parametrize("val", [2**33 + 1.0, 2**33 + 1.1, 2**62])
  950. class TestSmallIntegerSetitemUpcast(SetitemCastingEquivalents):
  951. # https://github.com/pandas-dev/pandas/issues/39584#issuecomment-941212124
  952. @pytest.fixture
  953. def obj(self):
  954. return Series([1, 2, 3], dtype="i4")
  955. @pytest.fixture
  956. def key(self):
  957. return 0
  958. @pytest.fixture
  959. def expected(self, val):
  960. if val % 1 != 0:
  961. dtype = "f8"
  962. else:
  963. dtype = "i8"
  964. return Series([val, 2, 3], dtype=dtype)
  965. class CoercionTest(SetitemCastingEquivalents):
  966. # Tests ported from tests.indexing.test_coercion
  967. @pytest.fixture
  968. def key(self):
  969. return 1
  970. @pytest.fixture
  971. def expected(self, obj, key, val, exp_dtype):
  972. vals = list(obj)
  973. vals[key] = val
  974. return Series(vals, dtype=exp_dtype)
  975. @pytest.mark.parametrize(
  976. "val,exp_dtype", [(np.int32(1), np.int8), (np.int16(2**9), np.int16)]
  977. )
  978. class TestCoercionInt8(CoercionTest):
  979. # previously test_setitem_series_int8 in tests.indexing.test_coercion
  980. @pytest.fixture
  981. def obj(self):
  982. return Series([1, 2, 3, 4], dtype=np.int8)
  983. @pytest.mark.parametrize("val", [1, 1.1, 1 + 1j, True])
  984. @pytest.mark.parametrize("exp_dtype", [object])
  985. class TestCoercionObject(CoercionTest):
  986. # previously test_setitem_series_object in tests.indexing.test_coercion
  987. @pytest.fixture
  988. def obj(self):
  989. return Series(["a", "b", "c", "d"], dtype=object)
  990. @pytest.mark.parametrize(
  991. "val,exp_dtype",
  992. [(1, np.complex128), (1.1, np.complex128), (1 + 1j, np.complex128), (True, object)],
  993. )
  994. class TestCoercionComplex(CoercionTest):
  995. # previously test_setitem_series_complex128 in tests.indexing.test_coercion
  996. @pytest.fixture
  997. def obj(self):
  998. return Series([1 + 1j, 2 + 2j, 3 + 3j, 4 + 4j])
  999. @pytest.mark.parametrize(
  1000. "val,exp_dtype",
  1001. [
  1002. (1, object),
  1003. ("3", object),
  1004. (3, object),
  1005. (1.1, object),
  1006. (1 + 1j, object),
  1007. (True, bool),
  1008. ],
  1009. )
  1010. class TestCoercionBool(CoercionTest):
  1011. # previously test_setitem_series_bool in tests.indexing.test_coercion
  1012. @pytest.fixture
  1013. def obj(self):
  1014. return Series([True, False, True, False], dtype=bool)
  1015. @pytest.mark.parametrize(
  1016. "val,exp_dtype",
  1017. [(1, np.int64), (1.1, np.float64), (1 + 1j, np.complex128), (True, object)],
  1018. )
  1019. class TestCoercionInt64(CoercionTest):
  1020. # previously test_setitem_series_int64 in tests.indexing.test_coercion
  1021. @pytest.fixture
  1022. def obj(self):
  1023. return Series([1, 2, 3, 4])
  1024. @pytest.mark.parametrize(
  1025. "val,exp_dtype",
  1026. [(1, np.float64), (1.1, np.float64), (1 + 1j, np.complex128), (True, object)],
  1027. )
  1028. class TestCoercionFloat64(CoercionTest):
  1029. # previously test_setitem_series_float64 in tests.indexing.test_coercion
  1030. @pytest.fixture
  1031. def obj(self):
  1032. return Series([1.1, 2.2, 3.3, 4.4])
  1033. @pytest.mark.parametrize(
  1034. "val,exp_dtype",
  1035. [
  1036. (1, np.float32),
  1037. pytest.param(
  1038. 1.1,
  1039. np.float32,
  1040. marks=pytest.mark.xfail(
  1041. reason="np.float32(1.1) ends up as 1.100000023841858, so "
  1042. "np_can_hold_element raises and we cast to float64",
  1043. ),
  1044. ),
  1045. (1 + 1j, np.complex128),
  1046. (True, object),
  1047. (np.uint8(2), np.float32),
  1048. (np.uint32(2), np.float32),
  1049. # float32 cannot hold np.iinfo(np.uint32).max exactly
  1050. # (closest it can hold is 4294967300.0 which off by 5.0), so
  1051. # we cast to float64
  1052. (np.uint32(np.iinfo(np.uint32).max), np.float64),
  1053. (np.uint64(2), np.float32),
  1054. (np.int64(2), np.float32),
  1055. ],
  1056. )
  1057. class TestCoercionFloat32(CoercionTest):
  1058. @pytest.fixture
  1059. def obj(self):
  1060. return Series([1.1, 2.2, 3.3, 4.4], dtype=np.float32)
  1061. def test_slice_key(self, obj, key, expected, val, indexer_sli, is_inplace):
  1062. super().test_slice_key(obj, key, expected, val, indexer_sli, is_inplace)
  1063. if type(val) is float:
  1064. # the xfail would xpass bc test_slice_key short-circuits
  1065. raise AssertionError("xfail not relevant for this test.")
  1066. @pytest.mark.parametrize(
  1067. "val,exp_dtype",
  1068. [(Timestamp("2012-01-01"), "datetime64[ns]"), (1, object), ("x", object)],
  1069. )
  1070. class TestCoercionDatetime64(CoercionTest):
  1071. # previously test_setitem_series_datetime64 in tests.indexing.test_coercion
  1072. @pytest.fixture
  1073. def obj(self):
  1074. return Series(date_range("2011-01-01", freq="D", periods=4))
  1075. @pytest.mark.parametrize(
  1076. "val,exp_dtype",
  1077. [
  1078. (Timestamp("2012-01-01", tz="US/Eastern"), "datetime64[ns, US/Eastern]"),
  1079. # pre-2.0, a mis-matched tz would end up casting to object
  1080. (Timestamp("2012-01-01", tz="US/Pacific"), "datetime64[ns, US/Eastern]"),
  1081. (Timestamp("2012-01-01"), object),
  1082. (1, object),
  1083. ],
  1084. )
  1085. class TestCoercionDatetime64TZ(CoercionTest):
  1086. # previously test_setitem_series_datetime64tz in tests.indexing.test_coercion
  1087. @pytest.fixture
  1088. def obj(self):
  1089. tz = "US/Eastern"
  1090. return Series(date_range("2011-01-01", freq="D", periods=4, tz=tz))
  1091. @pytest.mark.parametrize(
  1092. "val,exp_dtype",
  1093. [(Timedelta("12 day"), "timedelta64[ns]"), (1, object), ("x", object)],
  1094. )
  1095. class TestCoercionTimedelta64(CoercionTest):
  1096. # previously test_setitem_series_timedelta64 in tests.indexing.test_coercion
  1097. @pytest.fixture
  1098. def obj(self):
  1099. return Series(timedelta_range("1 day", periods=4))
  1100. @pytest.mark.parametrize(
  1101. "val", ["foo", Period("2016", freq="Y"), Interval(1, 2, closed="both")]
  1102. )
  1103. @pytest.mark.parametrize("exp_dtype", [object])
  1104. class TestPeriodIntervalCoercion(CoercionTest):
  1105. # GH#45768
  1106. @pytest.fixture(
  1107. params=[
  1108. period_range("2016-01-01", periods=3, freq="D"),
  1109. interval_range(1, 5),
  1110. ]
  1111. )
  1112. def obj(self, request):
  1113. return Series(request.param)
  1114. def test_20643():
  1115. # closed by GH#45121
  1116. orig = Series([0, 1, 2], index=["a", "b", "c"])
  1117. expected = Series([0, 2.7, 2], index=["a", "b", "c"])
  1118. ser = orig.copy()
  1119. ser.at["b"] = 2.7
  1120. tm.assert_series_equal(ser, expected)
  1121. ser = orig.copy()
  1122. ser.loc["b"] = 2.7
  1123. tm.assert_series_equal(ser, expected)
  1124. ser = orig.copy()
  1125. ser["b"] = 2.7
  1126. tm.assert_series_equal(ser, expected)
  1127. ser = orig.copy()
  1128. ser.iat[1] = 2.7
  1129. tm.assert_series_equal(ser, expected)
  1130. ser = orig.copy()
  1131. ser.iloc[1] = 2.7
  1132. tm.assert_series_equal(ser, expected)
  1133. orig_df = orig.to_frame("A")
  1134. expected_df = expected.to_frame("A")
  1135. df = orig_df.copy()
  1136. df.at["b", "A"] = 2.7
  1137. tm.assert_frame_equal(df, expected_df)
  1138. df = orig_df.copy()
  1139. df.loc["b", "A"] = 2.7
  1140. tm.assert_frame_equal(df, expected_df)
  1141. df = orig_df.copy()
  1142. df.iloc[1, 0] = 2.7
  1143. tm.assert_frame_equal(df, expected_df)
  1144. df = orig_df.copy()
  1145. df.iat[1, 0] = 2.7
  1146. tm.assert_frame_equal(df, expected_df)
  1147. def test_20643_comment():
  1148. # https://github.com/pandas-dev/pandas/issues/20643#issuecomment-431244590
  1149. # fixed sometime prior to GH#45121
  1150. orig = Series([0, 1, 2], index=["a", "b", "c"])
  1151. expected = Series([np.nan, 1, 2], index=["a", "b", "c"])
  1152. ser = orig.copy()
  1153. ser.iat[0] = None
  1154. tm.assert_series_equal(ser, expected)
  1155. ser = orig.copy()
  1156. ser.iloc[0] = None
  1157. tm.assert_series_equal(ser, expected)
  1158. def test_15413():
  1159. # fixed by GH#45121
  1160. ser = Series([1, 2, 3])
  1161. ser[ser == 2] += 0.5
  1162. expected = Series([1, 2.5, 3])
  1163. tm.assert_series_equal(ser, expected)
  1164. ser = Series([1, 2, 3])
  1165. ser[1] += 0.5
  1166. tm.assert_series_equal(ser, expected)
  1167. ser = Series([1, 2, 3])
  1168. ser.loc[1] += 0.5
  1169. tm.assert_series_equal(ser, expected)
  1170. ser = Series([1, 2, 3])
  1171. ser.iloc[1] += 0.5
  1172. tm.assert_series_equal(ser, expected)
  1173. ser = Series([1, 2, 3])
  1174. ser.iat[1] += 0.5
  1175. tm.assert_series_equal(ser, expected)
  1176. ser = Series([1, 2, 3])
  1177. ser.at[1] += 0.5
  1178. tm.assert_series_equal(ser, expected)
  1179. def test_32878_int_itemsize():
  1180. # Fixed by GH#45121
  1181. arr = np.arange(5).astype("i4")
  1182. ser = Series(arr)
  1183. val = np.int64(np.iinfo(np.int64).max)
  1184. ser[0] = val
  1185. expected = Series([val, 1, 2, 3, 4], dtype=np.int64)
  1186. tm.assert_series_equal(ser, expected)
  1187. def test_32878_complex_itemsize():
  1188. arr = np.arange(5).astype("c8")
  1189. ser = Series(arr)
  1190. val = np.finfo(np.float64).max
  1191. val = val.astype("c16")
  1192. # GH#32878 used to coerce val to inf+0.000000e+00j
  1193. ser[0] = val
  1194. assert ser[0] == val
  1195. expected = Series([val, 1, 2, 3, 4], dtype="c16")
  1196. tm.assert_series_equal(ser, expected)
  1197. def test_37692(indexer_al):
  1198. # GH#37692
  1199. ser = Series([1, 2, 3], index=["a", "b", "c"])
  1200. indexer_al(ser)["b"] = "test"
  1201. expected = Series([1, "test", 3], index=["a", "b", "c"], dtype=object)
  1202. tm.assert_series_equal(ser, expected)
  1203. def test_setitem_bool_int_float_consistency(indexer_sli):
  1204. # GH#21513
  1205. # bool-with-int and bool-with-float both upcast to object
  1206. # int-with-float and float-with-int are both non-casting so long
  1207. # as the setitem can be done losslessly
  1208. for dtype in [np.float64, np.int64]:
  1209. ser = Series(0, index=range(3), dtype=dtype)
  1210. indexer_sli(ser)[0] = True
  1211. assert ser.dtype == object
  1212. ser = Series(0, index=range(3), dtype=bool)
  1213. ser[0] = dtype(1)
  1214. assert ser.dtype == object
  1215. # 1.0 can be held losslessly, so no casting
  1216. ser = Series(0, index=range(3), dtype=np.int64)
  1217. indexer_sli(ser)[0] = np.float64(1.0)
  1218. assert ser.dtype == np.int64
  1219. # 1 can be held losslessly, so no casting
  1220. ser = Series(0, index=range(3), dtype=np.float64)
  1221. indexer_sli(ser)[0] = np.int64(1)
  1222. def test_setitem_positional_with_casting():
  1223. # GH#45070 case where in __setitem__ we get a KeyError, then when
  1224. # we fallback we *also* get a ValueError if we try to set inplace.
  1225. ser = Series([1, 2, 3], index=["a", "b", "c"])
  1226. ser[0] = "X"
  1227. expected = Series(["X", 2, 3], index=["a", "b", "c"], dtype=object)
  1228. tm.assert_series_equal(ser, expected)
  1229. def test_setitem_positional_float_into_int_coerces():
  1230. # Case where we hit a KeyError and then trying to set in-place incorrectly
  1231. # casts a float to an int
  1232. ser = Series([1, 2, 3], index=["a", "b", "c"])
  1233. ser[0] = 1.5
  1234. expected = Series([1.5, 2, 3], index=["a", "b", "c"])
  1235. tm.assert_series_equal(ser, expected)
  1236. def test_setitem_int_not_positional():
  1237. # GH#42215 deprecated falling back to positional on __setitem__ with an
  1238. # int not contained in the index; enforced in 2.0
  1239. ser = Series([1, 2, 3, 4], index=[1.1, 2.1, 3.0, 4.1])
  1240. assert not ser.index._should_fallback_to_positional
  1241. # assert not ser.index.astype(object)._should_fallback_to_positional
  1242. # 3.0 is in our index, so post-enforcement behavior is unchanged
  1243. ser[3] = 10
  1244. expected = Series([1, 2, 10, 4], index=ser.index)
  1245. tm.assert_series_equal(ser, expected)
  1246. # pre-enforcement `ser[5] = 5` raised IndexError
  1247. ser[5] = 5
  1248. expected = Series([1, 2, 10, 4, 5], index=[1.1, 2.1, 3.0, 4.1, 5.0])
  1249. tm.assert_series_equal(ser, expected)
  1250. ii = IntervalIndex.from_breaks(range(10))[::2]
  1251. ser2 = Series(range(len(ii)), index=ii)
  1252. exp_index = ii.astype(object).append(Index([4]))
  1253. expected2 = Series([0, 1, 2, 3, 4, 9], index=exp_index)
  1254. # pre-enforcement `ser2[4] = 9` interpreted 4 as positional
  1255. ser2[4] = 9
  1256. tm.assert_series_equal(ser2, expected2)
  1257. mi = MultiIndex.from_product([ser.index, ["A", "B"]])
  1258. ser3 = Series(range(len(mi)), index=mi)
  1259. expected3 = ser3.copy()
  1260. expected3.loc[4] = 99
  1261. # pre-enforcement `ser3[4] = 99` interpreted 4 as positional
  1262. ser3[4] = 99
  1263. tm.assert_series_equal(ser3, expected3)
  1264. def test_setitem_with_bool_indexer():
  1265. # GH#42530
  1266. df = DataFrame({"a": [1, 2, 3], "b": [4, 5, 6]})
  1267. result = df.pop("b")
  1268. result[[True, False, False]] = 9
  1269. expected = Series(data=[9, 5, 6], name="b")
  1270. tm.assert_series_equal(result, expected)
  1271. df.loc[[True, False, False], "a"] = 10
  1272. expected = DataFrame({"a": [10, 2, 3]})
  1273. tm.assert_frame_equal(df, expected)
  1274. @pytest.mark.parametrize("size", range(2, 6))
  1275. @pytest.mark.parametrize(
  1276. "mask", [[True, False, False, False, False], [True, False], [False]]
  1277. )
  1278. @pytest.mark.parametrize(
  1279. "item", [2.0, np.nan, np.finfo(float).max, np.finfo(float).min]
  1280. )
  1281. # Test numpy arrays, lists and tuples as the input to be
  1282. # broadcast
  1283. @pytest.mark.parametrize(
  1284. "box", [lambda x: np.array([x]), lambda x: [x], lambda x: (x,)]
  1285. )
  1286. def test_setitem_bool_indexer_dont_broadcast_length1_values(size, mask, item, box):
  1287. # GH#44265
  1288. # see also tests.series.indexing.test_where.test_broadcast
  1289. selection = np.resize(mask, size)
  1290. data = np.arange(size, dtype=float)
  1291. ser = Series(data)
  1292. if selection.sum() != 1:
  1293. msg = (
  1294. "cannot set using a list-like indexer with a different "
  1295. "length than the value"
  1296. )
  1297. with pytest.raises(ValueError, match=msg):
  1298. # GH#44265
  1299. ser[selection] = box(item)
  1300. else:
  1301. # In this corner case setting is equivalent to setting with the unboxed
  1302. # item
  1303. ser[selection] = box(item)
  1304. expected = Series(np.arange(size, dtype=float))
  1305. expected[selection] = item
  1306. tm.assert_series_equal(ser, expected)
  1307. def test_setitem_empty_mask_dont_upcast_dt64():
  1308. dti = date_range("2016-01-01", periods=3)
  1309. ser = Series(dti)
  1310. orig = ser.copy()
  1311. mask = np.zeros(3, dtype=bool)
  1312. ser[mask] = "foo"
  1313. assert ser.dtype == dti.dtype # no-op -> dont upcast
  1314. tm.assert_series_equal(ser, orig)
  1315. ser.mask(mask, "foo", inplace=True)
  1316. assert ser.dtype == dti.dtype # no-op -> dont upcast
  1317. tm.assert_series_equal(ser, orig)