test_numeric.py 54 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567
  1. # Arithmetic tests for DataFrame/Series/Index/Array classes that should
  2. # behave identically.
  3. # Specifically for numeric dtypes
  4. from __future__ import annotations
  5. from collections import abc
  6. from datetime import timedelta
  7. from decimal import Decimal
  8. import operator
  9. import numpy as np
  10. import pytest
  11. import pandas as pd
  12. from pandas import (
  13. Index,
  14. RangeIndex,
  15. Series,
  16. Timedelta,
  17. TimedeltaIndex,
  18. array,
  19. date_range,
  20. )
  21. import pandas._testing as tm
  22. from pandas.core import ops
  23. from pandas.core.computation import expressions as expr
  24. from pandas.tests.arithmetic.common import (
  25. assert_invalid_addsub_type,
  26. assert_invalid_comparison,
  27. )
  28. @pytest.fixture(autouse=True, params=[0, 1000000], ids=["numexpr", "python"])
  29. def switch_numexpr_min_elements(request, monkeypatch):
  30. with monkeypatch.context() as m:
  31. m.setattr(expr, "_MIN_ELEMENTS", request.param)
  32. yield request.param
  33. @pytest.fixture(params=[Index, Series, tm.to_array])
  34. def box_pandas_1d_array(request):
  35. """
  36. Fixture to test behavior for Index, Series and tm.to_array classes
  37. """
  38. return request.param
  39. @pytest.fixture(
  40. params=[
  41. # TODO: add more dtypes here
  42. Index(np.arange(5, dtype="float64")),
  43. Index(np.arange(5, dtype="int64")),
  44. Index(np.arange(5, dtype="uint64")),
  45. RangeIndex(5),
  46. ],
  47. ids=lambda x: type(x).__name__,
  48. )
  49. def numeric_idx(request):
  50. """
  51. Several types of numeric-dtypes Index objects
  52. """
  53. return request.param
  54. @pytest.fixture(
  55. params=[Index, Series, tm.to_array, np.array, list], ids=lambda x: x.__name__
  56. )
  57. def box_1d_array(request):
  58. """
  59. Fixture to test behavior for Index, Series, tm.to_array, numpy Array and list
  60. classes
  61. """
  62. return request.param
  63. def adjust_negative_zero(zero, expected):
  64. """
  65. Helper to adjust the expected result if we are dividing by -0.0
  66. as opposed to 0.0
  67. """
  68. if np.signbit(np.array(zero)).any():
  69. # All entries in the `zero` fixture should be either
  70. # all-negative or no-negative.
  71. assert np.signbit(np.array(zero)).all()
  72. expected *= -1
  73. return expected
  74. def compare_op(series, other, op):
  75. left = np.abs(series) if op in (ops.rpow, operator.pow) else series
  76. right = np.abs(other) if op in (ops.rpow, operator.pow) else other
  77. cython_or_numpy = op(left, right)
  78. python = left.combine(right, op)
  79. if isinstance(other, Series) and not other.index.equals(series.index):
  80. python.index = python.index._with_freq(None)
  81. tm.assert_series_equal(cython_or_numpy, python)
  82. # TODO: remove this kludge once mypy stops giving false positives here
  83. # List comprehension has incompatible type List[PandasObject]; expected List[RangeIndex]
  84. # See GH#29725
  85. _ldtypes = ["i1", "i2", "i4", "i8", "u1", "u2", "u4", "u8", "f2", "f4", "f8"]
  86. lefts: list[Index | Series] = [RangeIndex(10, 40, 10)]
  87. lefts.extend([Series([10, 20, 30], dtype=dtype) for dtype in _ldtypes])
  88. lefts.extend([Index([10, 20, 30], dtype=dtype) for dtype in _ldtypes if dtype != "f2"])
  89. # ------------------------------------------------------------------
  90. # Comparisons
  91. class TestNumericComparisons:
  92. def test_operator_series_comparison_zerorank(self):
  93. # GH#13006
  94. result = np.float64(0) > Series([1, 2, 3])
  95. expected = 0.0 > Series([1, 2, 3])
  96. tm.assert_series_equal(result, expected)
  97. result = Series([1, 2, 3]) < np.float64(0)
  98. expected = Series([1, 2, 3]) < 0.0
  99. tm.assert_series_equal(result, expected)
  100. result = np.array([0, 1, 2])[0] > Series([0, 1, 2])
  101. expected = 0.0 > Series([1, 2, 3])
  102. tm.assert_series_equal(result, expected)
  103. def test_df_numeric_cmp_dt64_raises(self, box_with_array, fixed_now_ts):
  104. # GH#8932, GH#22163
  105. ts = fixed_now_ts
  106. obj = np.array(range(5))
  107. obj = tm.box_expected(obj, box_with_array)
  108. assert_invalid_comparison(obj, ts, box_with_array)
  109. def test_compare_invalid(self):
  110. # GH#8058
  111. # ops testing
  112. a = Series(np.random.default_rng(2).standard_normal(5), name=0)
  113. b = Series(np.random.default_rng(2).standard_normal(5))
  114. b.name = pd.Timestamp("2000-01-01")
  115. tm.assert_series_equal(a / b, 1 / (b / a))
  116. def test_numeric_cmp_string_numexpr_path(self, box_with_array, monkeypatch):
  117. # GH#36377, GH#35700
  118. box = box_with_array
  119. xbox = box if box is not Index else np.ndarray
  120. obj = Series(np.random.default_rng(2).standard_normal(51))
  121. obj = tm.box_expected(obj, box, transpose=False)
  122. with monkeypatch.context() as m:
  123. m.setattr(expr, "_MIN_ELEMENTS", 50)
  124. result = obj == "a"
  125. expected = Series(np.zeros(51, dtype=bool))
  126. expected = tm.box_expected(expected, xbox, transpose=False)
  127. tm.assert_equal(result, expected)
  128. with monkeypatch.context() as m:
  129. m.setattr(expr, "_MIN_ELEMENTS", 50)
  130. result = obj != "a"
  131. tm.assert_equal(result, ~expected)
  132. msg = "Invalid comparison between dtype=float64 and str"
  133. with pytest.raises(TypeError, match=msg):
  134. obj < "a"
  135. # ------------------------------------------------------------------
  136. # Numeric dtypes Arithmetic with Datetime/Timedelta Scalar
  137. class TestNumericArraylikeArithmeticWithDatetimeLike:
  138. @pytest.mark.parametrize("box_cls", [np.array, Index, Series])
  139. @pytest.mark.parametrize(
  140. "left", lefts, ids=lambda x: type(x).__name__ + str(x.dtype)
  141. )
  142. def test_mul_td64arr(self, left, box_cls):
  143. # GH#22390
  144. right = np.array([1, 2, 3], dtype="m8[s]")
  145. right = box_cls(right)
  146. expected = TimedeltaIndex(["10s", "40s", "90s"], dtype=right.dtype)
  147. if isinstance(left, Series) or box_cls is Series:
  148. expected = Series(expected)
  149. assert expected.dtype == right.dtype
  150. result = left * right
  151. tm.assert_equal(result, expected)
  152. result = right * left
  153. tm.assert_equal(result, expected)
  154. @pytest.mark.parametrize("box_cls", [np.array, Index, Series])
  155. @pytest.mark.parametrize(
  156. "left", lefts, ids=lambda x: type(x).__name__ + str(x.dtype)
  157. )
  158. def test_div_td64arr(self, left, box_cls):
  159. # GH#22390
  160. right = np.array([10, 40, 90], dtype="m8[s]")
  161. right = box_cls(right)
  162. expected = TimedeltaIndex(["1s", "2s", "3s"], dtype=right.dtype)
  163. if isinstance(left, Series) or box_cls is Series:
  164. expected = Series(expected)
  165. assert expected.dtype == right.dtype
  166. result = right / left
  167. tm.assert_equal(result, expected)
  168. result = right // left
  169. tm.assert_equal(result, expected)
  170. # (true_) needed for min-versions build 2022-12-26
  171. msg = "ufunc '(true_)?divide' cannot use operands with types"
  172. with pytest.raises(TypeError, match=msg):
  173. left / right
  174. msg = "ufunc 'floor_divide' cannot use operands with types"
  175. with pytest.raises(TypeError, match=msg):
  176. left // right
  177. # TODO: also test Tick objects;
  178. # see test_numeric_arr_rdiv_tdscalar for note on these failing
  179. @pytest.mark.parametrize(
  180. "scalar_td",
  181. [
  182. Timedelta(days=1),
  183. Timedelta(days=1).to_timedelta64(),
  184. Timedelta(days=1).to_pytimedelta(),
  185. Timedelta(days=1).to_timedelta64().astype("timedelta64[s]"),
  186. Timedelta(days=1).to_timedelta64().astype("timedelta64[ms]"),
  187. ],
  188. ids=lambda x: type(x).__name__,
  189. )
  190. def test_numeric_arr_mul_tdscalar(self, scalar_td, numeric_idx, box_with_array):
  191. # GH#19333
  192. box = box_with_array
  193. index = numeric_idx
  194. expected = TimedeltaIndex([Timedelta(days=n) for n in range(len(index))])
  195. if isinstance(scalar_td, np.timedelta64):
  196. dtype = scalar_td.dtype
  197. expected = expected.astype(dtype)
  198. elif type(scalar_td) is timedelta:
  199. expected = expected.astype("m8[us]")
  200. index = tm.box_expected(index, box)
  201. expected = tm.box_expected(expected, box)
  202. result = index * scalar_td
  203. tm.assert_equal(result, expected)
  204. commute = scalar_td * index
  205. tm.assert_equal(commute, expected)
  206. @pytest.mark.parametrize(
  207. "scalar_td",
  208. [
  209. Timedelta(days=1),
  210. Timedelta(days=1).to_timedelta64(),
  211. Timedelta(days=1).to_pytimedelta(),
  212. ],
  213. ids=lambda x: type(x).__name__,
  214. )
  215. @pytest.mark.parametrize("dtype", [np.int64, np.float64])
  216. def test_numeric_arr_mul_tdscalar_numexpr_path(
  217. self, dtype, scalar_td, box_with_array
  218. ):
  219. # GH#44772 for the float64 case
  220. box = box_with_array
  221. arr_i8 = np.arange(2 * 10**4).astype(np.int64, copy=False)
  222. arr = arr_i8.astype(dtype, copy=False)
  223. obj = tm.box_expected(arr, box, transpose=False)
  224. expected = arr_i8.view("timedelta64[D]").astype("timedelta64[ns]")
  225. if type(scalar_td) is timedelta:
  226. expected = expected.astype("timedelta64[us]")
  227. expected = tm.box_expected(expected, box, transpose=False)
  228. result = obj * scalar_td
  229. tm.assert_equal(result, expected)
  230. result = scalar_td * obj
  231. tm.assert_equal(result, expected)
  232. def test_numeric_arr_rdiv_tdscalar(self, three_days, numeric_idx, box_with_array):
  233. box = box_with_array
  234. index = numeric_idx[1:3]
  235. expected = TimedeltaIndex(["3 Days", "36 Hours"])
  236. if isinstance(three_days, np.timedelta64):
  237. dtype = three_days.dtype
  238. if dtype < np.dtype("m8[s]"):
  239. # i.e. resolution is lower -> use lowest supported resolution
  240. dtype = np.dtype("m8[s]")
  241. expected = expected.astype(dtype)
  242. elif type(three_days) is timedelta:
  243. expected = expected.astype("m8[us]")
  244. elif isinstance(
  245. three_days,
  246. (pd.offsets.Day, pd.offsets.Hour, pd.offsets.Minute, pd.offsets.Second),
  247. ):
  248. # closest reso is Second
  249. expected = expected.astype("m8[s]")
  250. index = tm.box_expected(index, box)
  251. expected = tm.box_expected(expected, box)
  252. result = three_days / index
  253. tm.assert_equal(result, expected)
  254. msg = "cannot use operands with types dtype"
  255. with pytest.raises(TypeError, match=msg):
  256. index / three_days
  257. @pytest.mark.parametrize(
  258. "other",
  259. [
  260. Timedelta(hours=31),
  261. Timedelta(hours=31).to_pytimedelta(),
  262. Timedelta(hours=31).to_timedelta64(),
  263. Timedelta(hours=31).to_timedelta64().astype("m8[h]"),
  264. np.timedelta64("NaT"),
  265. np.timedelta64("NaT", "D"),
  266. pd.offsets.Minute(3),
  267. pd.offsets.Second(0),
  268. # GH#28080 numeric+datetimelike should raise; Timestamp used
  269. # to raise NullFrequencyError but that behavior was removed in 1.0
  270. pd.Timestamp("2021-01-01", tz="Asia/Tokyo"),
  271. pd.Timestamp("2021-01-01"),
  272. pd.Timestamp("2021-01-01").to_pydatetime(),
  273. pd.Timestamp("2021-01-01", tz="UTC").to_pydatetime(),
  274. pd.Timestamp("2021-01-01").to_datetime64(),
  275. np.datetime64("NaT", "ns"),
  276. pd.NaT,
  277. ],
  278. ids=repr,
  279. )
  280. def test_add_sub_datetimedeltalike_invalid(
  281. self, numeric_idx, other, box_with_array
  282. ):
  283. box = box_with_array
  284. left = tm.box_expected(numeric_idx, box)
  285. msg = "|".join(
  286. [
  287. "unsupported operand type",
  288. "Addition/subtraction of integers and integer-arrays",
  289. "Instead of adding/subtracting",
  290. "cannot use operands with types dtype",
  291. "Concatenation operation is not implemented for NumPy arrays",
  292. "Cannot (add|subtract) NaT (to|from) ndarray",
  293. # pd.array vs np.datetime64 case
  294. r"operand type\(s\) all returned NotImplemented from __array_ufunc__",
  295. "can only perform ops with numeric values",
  296. "cannot subtract DatetimeArray from ndarray",
  297. # pd.Timedelta(1) + Index([0, 1, 2])
  298. "Cannot add or subtract Timedelta from integers",
  299. ]
  300. )
  301. assert_invalid_addsub_type(left, other, msg)
  302. # ------------------------------------------------------------------
  303. # Arithmetic
  304. class TestDivisionByZero:
  305. def test_div_zero(self, zero, numeric_idx):
  306. idx = numeric_idx
  307. expected = Index([np.nan, np.inf, np.inf, np.inf, np.inf], dtype=np.float64)
  308. # We only adjust for Index, because Series does not yet apply
  309. # the adjustment correctly.
  310. expected2 = adjust_negative_zero(zero, expected)
  311. result = idx / zero
  312. tm.assert_index_equal(result, expected2)
  313. ser_compat = Series(idx).astype("i8") / np.array(zero).astype("i8")
  314. tm.assert_series_equal(ser_compat, Series(expected))
  315. def test_floordiv_zero(self, zero, numeric_idx):
  316. idx = numeric_idx
  317. expected = Index([np.nan, np.inf, np.inf, np.inf, np.inf], dtype=np.float64)
  318. # We only adjust for Index, because Series does not yet apply
  319. # the adjustment correctly.
  320. expected2 = adjust_negative_zero(zero, expected)
  321. result = idx // zero
  322. tm.assert_index_equal(result, expected2)
  323. ser_compat = Series(idx).astype("i8") // np.array(zero).astype("i8")
  324. tm.assert_series_equal(ser_compat, Series(expected))
  325. def test_mod_zero(self, zero, numeric_idx):
  326. idx = numeric_idx
  327. expected = Index([np.nan, np.nan, np.nan, np.nan, np.nan], dtype=np.float64)
  328. result = idx % zero
  329. tm.assert_index_equal(result, expected)
  330. ser_compat = Series(idx).astype("i8") % np.array(zero).astype("i8")
  331. tm.assert_series_equal(ser_compat, Series(result))
  332. def test_divmod_zero(self, zero, numeric_idx):
  333. idx = numeric_idx
  334. exleft = Index([np.nan, np.inf, np.inf, np.inf, np.inf], dtype=np.float64)
  335. exright = Index([np.nan, np.nan, np.nan, np.nan, np.nan], dtype=np.float64)
  336. exleft = adjust_negative_zero(zero, exleft)
  337. result = divmod(idx, zero)
  338. tm.assert_index_equal(result[0], exleft)
  339. tm.assert_index_equal(result[1], exright)
  340. @pytest.mark.parametrize("op", [operator.truediv, operator.floordiv])
  341. def test_div_negative_zero(self, zero, numeric_idx, op):
  342. # Check that -1 / -0.0 returns np.inf, not -np.inf
  343. if numeric_idx.dtype == np.uint64:
  344. pytest.skip(f"Div by negative 0 not relevant for {numeric_idx.dtype}")
  345. idx = numeric_idx - 3
  346. expected = Index([-np.inf, -np.inf, -np.inf, np.nan, np.inf], dtype=np.float64)
  347. expected = adjust_negative_zero(zero, expected)
  348. result = op(idx, zero)
  349. tm.assert_index_equal(result, expected)
  350. # ------------------------------------------------------------------
  351. @pytest.mark.parametrize("dtype1", [np.int64, np.float64, np.uint64])
  352. def test_ser_div_ser(
  353. self,
  354. switch_numexpr_min_elements,
  355. dtype1,
  356. any_real_numpy_dtype,
  357. ):
  358. # no longer do integer div for any ops, but deal with the 0's
  359. dtype2 = any_real_numpy_dtype
  360. first = Series([3, 4, 5, 8], name="first").astype(dtype1)
  361. second = Series([0, 0, 0, 3], name="second").astype(dtype2)
  362. with np.errstate(all="ignore"):
  363. expected = Series(
  364. first.values.astype(np.float64) / second.values,
  365. dtype="float64",
  366. name=None,
  367. )
  368. expected.iloc[0:3] = np.inf
  369. if first.dtype == "int64" and second.dtype == "float32":
  370. # when using numexpr, the casting rules are slightly different
  371. # and int64/float32 combo results in float32 instead of float64
  372. if expr.USE_NUMEXPR and switch_numexpr_min_elements == 0:
  373. expected = expected.astype("float32")
  374. result = first / second
  375. tm.assert_series_equal(result, expected)
  376. assert not result.equals(second / first)
  377. @pytest.mark.parametrize("dtype1", [np.int64, np.float64, np.uint64])
  378. def test_ser_divmod_zero(self, dtype1, any_real_numpy_dtype):
  379. # GH#26987
  380. dtype2 = any_real_numpy_dtype
  381. left = Series([1, 1]).astype(dtype1)
  382. right = Series([0, 2]).astype(dtype2)
  383. # GH#27321 pandas convention is to set 1 // 0 to np.inf, as opposed
  384. # to numpy which sets to np.nan; patch `expected[0]` below
  385. expected = left // right, left % right
  386. expected = list(expected)
  387. expected[0] = expected[0].astype(np.float64)
  388. expected[0][0] = np.inf
  389. result = divmod(left, right)
  390. tm.assert_series_equal(result[0], expected[0])
  391. tm.assert_series_equal(result[1], expected[1])
  392. # rdivmod case
  393. result = divmod(left.values, right)
  394. tm.assert_series_equal(result[0], expected[0])
  395. tm.assert_series_equal(result[1], expected[1])
  396. def test_ser_divmod_inf(self):
  397. left = Series([np.inf, 1.0])
  398. right = Series([np.inf, 2.0])
  399. expected = left // right, left % right
  400. result = divmod(left, right)
  401. tm.assert_series_equal(result[0], expected[0])
  402. tm.assert_series_equal(result[1], expected[1])
  403. # rdivmod case
  404. result = divmod(left.values, right)
  405. tm.assert_series_equal(result[0], expected[0])
  406. tm.assert_series_equal(result[1], expected[1])
  407. def test_rdiv_zero_compat(self):
  408. # GH#8674
  409. zero_array = np.array([0] * 5)
  410. data = np.random.default_rng(2).standard_normal(5)
  411. expected = Series([0.0] * 5)
  412. result = zero_array / Series(data)
  413. tm.assert_series_equal(result, expected)
  414. result = Series(zero_array) / data
  415. tm.assert_series_equal(result, expected)
  416. result = Series(zero_array) / Series(data)
  417. tm.assert_series_equal(result, expected)
  418. def test_div_zero_inf_signs(self):
  419. # GH#9144, inf signing
  420. ser = Series([-1, 0, 1], name="first")
  421. expected = Series([-np.inf, np.nan, np.inf], name="first")
  422. result = ser / 0
  423. tm.assert_series_equal(result, expected)
  424. def test_rdiv_zero(self):
  425. # GH#9144
  426. ser = Series([-1, 0, 1], name="first")
  427. expected = Series([0.0, np.nan, 0.0], name="first")
  428. result = 0 / ser
  429. tm.assert_series_equal(result, expected)
  430. def test_floordiv_div(self):
  431. # GH#9144
  432. ser = Series([-1, 0, 1], name="first")
  433. result = ser // 0
  434. expected = Series([-np.inf, np.nan, np.inf], name="first")
  435. tm.assert_series_equal(result, expected)
  436. def test_df_div_zero_df(self):
  437. # integer div, but deal with the 0's (GH#9144)
  438. df = pd.DataFrame({"first": [3, 4, 5, 8], "second": [0, 0, 0, 3]})
  439. result = df / df
  440. first = Series([1.0, 1.0, 1.0, 1.0])
  441. second = Series([np.nan, np.nan, np.nan, 1])
  442. expected = pd.DataFrame({"first": first, "second": second})
  443. tm.assert_frame_equal(result, expected)
  444. def test_df_div_zero_array(self):
  445. # integer div, but deal with the 0's (GH#9144)
  446. df = pd.DataFrame({"first": [3, 4, 5, 8], "second": [0, 0, 0, 3]})
  447. first = Series([1.0, 1.0, 1.0, 1.0])
  448. second = Series([np.nan, np.nan, np.nan, 1])
  449. expected = pd.DataFrame({"first": first, "second": second})
  450. with np.errstate(all="ignore"):
  451. arr = df.values.astype("float") / df.values
  452. result = pd.DataFrame(arr, index=df.index, columns=df.columns)
  453. tm.assert_frame_equal(result, expected)
  454. def test_df_div_zero_int(self):
  455. # integer div, but deal with the 0's (GH#9144)
  456. df = pd.DataFrame({"first": [3, 4, 5, 8], "second": [0, 0, 0, 3]})
  457. result = df / 0
  458. expected = pd.DataFrame(np.inf, index=df.index, columns=df.columns)
  459. expected.iloc[0:3, 1] = np.nan
  460. tm.assert_frame_equal(result, expected)
  461. # numpy has a slightly different (wrong) treatment
  462. with np.errstate(all="ignore"):
  463. arr = df.values.astype("float64") / 0
  464. result2 = pd.DataFrame(arr, index=df.index, columns=df.columns)
  465. tm.assert_frame_equal(result2, expected)
  466. def test_df_div_zero_series_does_not_commute(self):
  467. # integer div, but deal with the 0's (GH#9144)
  468. df = pd.DataFrame(np.random.default_rng(2).standard_normal((10, 5)))
  469. ser = df[0]
  470. res = ser / df
  471. res2 = df / ser
  472. assert not res.fillna(0).equals(res2.fillna(0))
  473. # ------------------------------------------------------------------
  474. # Mod By Zero
  475. def test_df_mod_zero_df(self, using_array_manager):
  476. # GH#3590, modulo as ints
  477. df = pd.DataFrame({"first": [3, 4, 5, 8], "second": [0, 0, 0, 3]})
  478. # this is technically wrong, as the integer portion is coerced to float
  479. first = Series([0, 0, 0, 0])
  480. if not using_array_manager:
  481. # INFO(ArrayManager) BlockManager doesn't preserve dtype per column
  482. # while ArrayManager performs op column-wisedoes and thus preserves
  483. # dtype if possible
  484. first = first.astype("float64")
  485. second = Series([np.nan, np.nan, np.nan, 0])
  486. expected = pd.DataFrame({"first": first, "second": second})
  487. result = df % df
  488. tm.assert_frame_equal(result, expected)
  489. # GH#38939 If we dont pass copy=False, df is consolidated and
  490. # result["first"] is float64 instead of int64
  491. df = pd.DataFrame({"first": [3, 4, 5, 8], "second": [0, 0, 0, 3]}, copy=False)
  492. first = Series([0, 0, 0, 0], dtype="int64")
  493. second = Series([np.nan, np.nan, np.nan, 0])
  494. expected = pd.DataFrame({"first": first, "second": second})
  495. result = df % df
  496. tm.assert_frame_equal(result, expected)
  497. def test_df_mod_zero_array(self):
  498. # GH#3590, modulo as ints
  499. df = pd.DataFrame({"first": [3, 4, 5, 8], "second": [0, 0, 0, 3]})
  500. # this is technically wrong, as the integer portion is coerced to float
  501. # ###
  502. first = Series([0, 0, 0, 0], dtype="float64")
  503. second = Series([np.nan, np.nan, np.nan, 0])
  504. expected = pd.DataFrame({"first": first, "second": second})
  505. # numpy has a slightly different (wrong) treatment
  506. with np.errstate(all="ignore"):
  507. arr = df.values % df.values
  508. result2 = pd.DataFrame(arr, index=df.index, columns=df.columns, dtype="float64")
  509. result2.iloc[0:3, 1] = np.nan
  510. tm.assert_frame_equal(result2, expected)
  511. def test_df_mod_zero_int(self):
  512. # GH#3590, modulo as ints
  513. df = pd.DataFrame({"first": [3, 4, 5, 8], "second": [0, 0, 0, 3]})
  514. result = df % 0
  515. expected = pd.DataFrame(np.nan, index=df.index, columns=df.columns)
  516. tm.assert_frame_equal(result, expected)
  517. # numpy has a slightly different (wrong) treatment
  518. with np.errstate(all="ignore"):
  519. arr = df.values.astype("float64") % 0
  520. result2 = pd.DataFrame(arr, index=df.index, columns=df.columns)
  521. tm.assert_frame_equal(result2, expected)
  522. def test_df_mod_zero_series_does_not_commute(self):
  523. # GH#3590, modulo as ints
  524. # not commutative with series
  525. df = pd.DataFrame(np.random.default_rng(2).standard_normal((10, 5)))
  526. ser = df[0]
  527. res = ser % df
  528. res2 = df % ser
  529. assert not res.fillna(0).equals(res2.fillna(0))
  530. class TestMultiplicationDivision:
  531. # __mul__, __rmul__, __div__, __rdiv__, __floordiv__, __rfloordiv__
  532. # for non-timestamp/timedelta/period dtypes
  533. def test_divide_decimal(self, box_with_array):
  534. # resolves issue GH#9787
  535. box = box_with_array
  536. ser = Series([Decimal(10)])
  537. expected = Series([Decimal(5)])
  538. ser = tm.box_expected(ser, box)
  539. expected = tm.box_expected(expected, box)
  540. result = ser / Decimal(2)
  541. tm.assert_equal(result, expected)
  542. result = ser // Decimal(2)
  543. tm.assert_equal(result, expected)
  544. def test_div_equiv_binop(self):
  545. # Test Series.div as well as Series.__div__
  546. # float/integer issue
  547. # GH#7785
  548. first = Series([1, 0], name="first")
  549. second = Series([-0.01, -0.02], name="second")
  550. expected = Series([-0.01, -np.inf])
  551. result = second.div(first)
  552. tm.assert_series_equal(result, expected, check_names=False)
  553. result = second / first
  554. tm.assert_series_equal(result, expected)
  555. def test_div_int(self, numeric_idx):
  556. idx = numeric_idx
  557. result = idx / 1
  558. expected = idx.astype("float64")
  559. tm.assert_index_equal(result, expected)
  560. result = idx / 2
  561. expected = Index(idx.values / 2)
  562. tm.assert_index_equal(result, expected)
  563. @pytest.mark.parametrize("op", [operator.mul, ops.rmul, operator.floordiv])
  564. def test_mul_int_identity(self, op, numeric_idx, box_with_array):
  565. idx = numeric_idx
  566. idx = tm.box_expected(idx, box_with_array)
  567. result = op(idx, 1)
  568. tm.assert_equal(result, idx)
  569. def test_mul_int_array(self, numeric_idx):
  570. idx = numeric_idx
  571. didx = idx * idx
  572. result = idx * np.array(5, dtype="int64")
  573. tm.assert_index_equal(result, idx * 5)
  574. arr_dtype = "uint64" if idx.dtype == np.uint64 else "int64"
  575. result = idx * np.arange(5, dtype=arr_dtype)
  576. tm.assert_index_equal(result, didx)
  577. def test_mul_int_series(self, numeric_idx):
  578. idx = numeric_idx
  579. didx = idx * idx
  580. arr_dtype = "uint64" if idx.dtype == np.uint64 else "int64"
  581. result = idx * Series(np.arange(5, dtype=arr_dtype))
  582. tm.assert_series_equal(result, Series(didx))
  583. def test_mul_float_series(self, numeric_idx):
  584. idx = numeric_idx
  585. rng5 = np.arange(5, dtype="float64")
  586. result = idx * Series(rng5 + 0.1)
  587. expected = Series(rng5 * (rng5 + 0.1))
  588. tm.assert_series_equal(result, expected)
  589. def test_mul_index(self, numeric_idx):
  590. idx = numeric_idx
  591. result = idx * idx
  592. tm.assert_index_equal(result, idx**2)
  593. def test_mul_datelike_raises(self, numeric_idx):
  594. idx = numeric_idx
  595. msg = "cannot perform __rmul__ with this index type"
  596. with pytest.raises(TypeError, match=msg):
  597. idx * date_range("20130101", periods=5)
  598. def test_mul_size_mismatch_raises(self, numeric_idx):
  599. idx = numeric_idx
  600. msg = "operands could not be broadcast together"
  601. with pytest.raises(ValueError, match=msg):
  602. idx * idx[0:3]
  603. with pytest.raises(ValueError, match=msg):
  604. idx * np.array([1, 2])
  605. @pytest.mark.parametrize("op", [operator.pow, ops.rpow])
  606. def test_pow_float(self, op, numeric_idx, box_with_array):
  607. # test power calculations both ways, GH#14973
  608. box = box_with_array
  609. idx = numeric_idx
  610. expected = Index(op(idx.values, 2.0))
  611. idx = tm.box_expected(idx, box)
  612. expected = tm.box_expected(expected, box)
  613. result = op(idx, 2.0)
  614. tm.assert_equal(result, expected)
  615. def test_modulo(self, numeric_idx, box_with_array):
  616. # GH#9244
  617. box = box_with_array
  618. idx = numeric_idx
  619. expected = Index(idx.values % 2)
  620. idx = tm.box_expected(idx, box)
  621. expected = tm.box_expected(expected, box)
  622. result = idx % 2
  623. tm.assert_equal(result, expected)
  624. def test_divmod_scalar(self, numeric_idx):
  625. idx = numeric_idx
  626. result = divmod(idx, 2)
  627. with np.errstate(all="ignore"):
  628. div, mod = divmod(idx.values, 2)
  629. expected = Index(div), Index(mod)
  630. for r, e in zip(result, expected):
  631. tm.assert_index_equal(r, e)
  632. def test_divmod_ndarray(self, numeric_idx):
  633. idx = numeric_idx
  634. other = np.ones(idx.values.shape, dtype=idx.values.dtype) * 2
  635. result = divmod(idx, other)
  636. with np.errstate(all="ignore"):
  637. div, mod = divmod(idx.values, other)
  638. expected = Index(div), Index(mod)
  639. for r, e in zip(result, expected):
  640. tm.assert_index_equal(r, e)
  641. def test_divmod_series(self, numeric_idx):
  642. idx = numeric_idx
  643. other = np.ones(idx.values.shape, dtype=idx.values.dtype) * 2
  644. result = divmod(idx, Series(other))
  645. with np.errstate(all="ignore"):
  646. div, mod = divmod(idx.values, other)
  647. expected = Series(div), Series(mod)
  648. for r, e in zip(result, expected):
  649. tm.assert_series_equal(r, e)
  650. @pytest.mark.parametrize("other", [np.nan, 7, -23, 2.718, -3.14, np.inf])
  651. def test_ops_np_scalar(self, other):
  652. vals = np.random.default_rng(2).standard_normal((5, 3))
  653. f = lambda x: pd.DataFrame(
  654. x, index=list("ABCDE"), columns=["jim", "joe", "jolie"]
  655. )
  656. df = f(vals)
  657. tm.assert_frame_equal(df / np.array(other), f(vals / other))
  658. tm.assert_frame_equal(np.array(other) * df, f(vals * other))
  659. tm.assert_frame_equal(df + np.array(other), f(vals + other))
  660. tm.assert_frame_equal(np.array(other) - df, f(other - vals))
  661. # TODO: This came from series.test.test_operators, needs cleanup
  662. def test_operators_frame(self):
  663. # rpow does not work with DataFrame
  664. ts = Series(
  665. np.arange(10, dtype=np.float64),
  666. index=date_range("2020-01-01", periods=10),
  667. name="ts",
  668. )
  669. ts.name = "ts"
  670. df = pd.DataFrame({"A": ts})
  671. tm.assert_series_equal(ts + ts, ts + df["A"], check_names=False)
  672. tm.assert_series_equal(ts**ts, ts ** df["A"], check_names=False)
  673. tm.assert_series_equal(ts < ts, ts < df["A"], check_names=False)
  674. tm.assert_series_equal(ts / ts, ts / df["A"], check_names=False)
  675. # TODO: this came from tests.series.test_analytics, needs cleanup and
  676. # de-duplication with test_modulo above
  677. def test_modulo2(self):
  678. with np.errstate(all="ignore"):
  679. # GH#3590, modulo as ints
  680. p = pd.DataFrame({"first": [3, 4, 5, 8], "second": [0, 0, 0, 3]})
  681. result = p["first"] % p["second"]
  682. expected = Series(p["first"].values % p["second"].values, dtype="float64")
  683. expected.iloc[0:3] = np.nan
  684. tm.assert_series_equal(result, expected)
  685. result = p["first"] % 0
  686. expected = Series(np.nan, index=p.index, name="first")
  687. tm.assert_series_equal(result, expected)
  688. p = p.astype("float64")
  689. result = p["first"] % p["second"]
  690. expected = Series(p["first"].values % p["second"].values)
  691. tm.assert_series_equal(result, expected)
  692. p = p.astype("float64")
  693. result = p["first"] % p["second"]
  694. result2 = p["second"] % p["first"]
  695. assert not result.equals(result2)
  696. def test_modulo_zero_int(self):
  697. # GH#9144
  698. with np.errstate(all="ignore"):
  699. s = Series([0, 1])
  700. result = s % 0
  701. expected = Series([np.nan, np.nan])
  702. tm.assert_series_equal(result, expected)
  703. result = 0 % s
  704. expected = Series([np.nan, 0.0])
  705. tm.assert_series_equal(result, expected)
  706. class TestAdditionSubtraction:
  707. # __add__, __sub__, __radd__, __rsub__, __iadd__, __isub__
  708. # for non-timestamp/timedelta/period dtypes
  709. @pytest.mark.parametrize(
  710. "first, second, expected",
  711. [
  712. (
  713. Series([1, 2, 3], index=list("ABC"), name="x"),
  714. Series([2, 2, 2], index=list("ABD"), name="x"),
  715. Series([3.0, 4.0, np.nan, np.nan], index=list("ABCD"), name="x"),
  716. ),
  717. (
  718. Series([1, 2, 3], index=list("ABC"), name="x"),
  719. Series([2, 2, 2, 2], index=list("ABCD"), name="x"),
  720. Series([3, 4, 5, np.nan], index=list("ABCD"), name="x"),
  721. ),
  722. ],
  723. )
  724. def test_add_series(self, first, second, expected):
  725. # GH#1134
  726. tm.assert_series_equal(first + second, expected)
  727. tm.assert_series_equal(second + first, expected)
  728. @pytest.mark.parametrize(
  729. "first, second, expected",
  730. [
  731. (
  732. pd.DataFrame({"x": [1, 2, 3]}, index=list("ABC")),
  733. pd.DataFrame({"x": [2, 2, 2]}, index=list("ABD")),
  734. pd.DataFrame({"x": [3.0, 4.0, np.nan, np.nan]}, index=list("ABCD")),
  735. ),
  736. (
  737. pd.DataFrame({"x": [1, 2, 3]}, index=list("ABC")),
  738. pd.DataFrame({"x": [2, 2, 2, 2]}, index=list("ABCD")),
  739. pd.DataFrame({"x": [3, 4, 5, np.nan]}, index=list("ABCD")),
  740. ),
  741. ],
  742. )
  743. def test_add_frames(self, first, second, expected):
  744. # GH#1134
  745. tm.assert_frame_equal(first + second, expected)
  746. tm.assert_frame_equal(second + first, expected)
  747. # TODO: This came from series.test.test_operators, needs cleanup
  748. def test_series_frame_radd_bug(self, fixed_now_ts):
  749. # GH#353
  750. vals = Series([str(i) for i in range(5)])
  751. result = "foo_" + vals
  752. expected = vals.map(lambda x: "foo_" + x)
  753. tm.assert_series_equal(result, expected)
  754. frame = pd.DataFrame({"vals": vals})
  755. result = "foo_" + frame
  756. expected = pd.DataFrame({"vals": vals.map(lambda x: "foo_" + x)})
  757. tm.assert_frame_equal(result, expected)
  758. ts = Series(
  759. np.arange(10, dtype=np.float64),
  760. index=date_range("2020-01-01", periods=10),
  761. name="ts",
  762. )
  763. # really raise this time
  764. fix_now = fixed_now_ts.to_pydatetime()
  765. msg = "|".join(
  766. [
  767. "unsupported operand type",
  768. # wrong error message, see https://github.com/numpy/numpy/issues/18832
  769. "Concatenation operation",
  770. ]
  771. )
  772. with pytest.raises(TypeError, match=msg):
  773. fix_now + ts
  774. with pytest.raises(TypeError, match=msg):
  775. ts + fix_now
  776. # TODO: This came from series.test.test_operators, needs cleanup
  777. def test_datetime64_with_index(self):
  778. # arithmetic integer ops with an index
  779. ser = Series(np.random.default_rng(2).standard_normal(5))
  780. expected = ser - ser.index.to_series()
  781. result = ser - ser.index
  782. tm.assert_series_equal(result, expected)
  783. # GH#4629
  784. # arithmetic datetime64 ops with an index
  785. ser = Series(
  786. date_range("20130101", periods=5),
  787. index=date_range("20130101", periods=5),
  788. )
  789. expected = ser - ser.index.to_series()
  790. result = ser - ser.index
  791. tm.assert_series_equal(result, expected)
  792. msg = "cannot subtract PeriodArray from DatetimeArray"
  793. with pytest.raises(TypeError, match=msg):
  794. # GH#18850
  795. result = ser - ser.index.to_period()
  796. df = pd.DataFrame(
  797. np.random.default_rng(2).standard_normal((5, 2)),
  798. index=date_range("20130101", periods=5),
  799. )
  800. df["date"] = pd.Timestamp("20130102")
  801. df["expected"] = df["date"] - df.index.to_series()
  802. df["result"] = df["date"] - df.index
  803. tm.assert_series_equal(df["result"], df["expected"], check_names=False)
  804. # TODO: taken from tests.frame.test_operators, needs cleanup
  805. def test_frame_operators(self, float_frame):
  806. frame = float_frame
  807. garbage = np.random.default_rng(2).random(4)
  808. colSeries = Series(garbage, index=np.array(frame.columns))
  809. idSum = frame + frame
  810. seriesSum = frame + colSeries
  811. for col, series in idSum.items():
  812. for idx, val in series.items():
  813. origVal = frame[col][idx] * 2
  814. if not np.isnan(val):
  815. assert val == origVal
  816. else:
  817. assert np.isnan(origVal)
  818. for col, series in seriesSum.items():
  819. for idx, val in series.items():
  820. origVal = frame[col][idx] + colSeries[col]
  821. if not np.isnan(val):
  822. assert val == origVal
  823. else:
  824. assert np.isnan(origVal)
  825. def test_frame_operators_col_align(self, float_frame):
  826. frame2 = pd.DataFrame(float_frame, columns=["D", "C", "B", "A"])
  827. added = frame2 + frame2
  828. expected = frame2 * 2
  829. tm.assert_frame_equal(added, expected)
  830. def test_frame_operators_none_to_nan(self):
  831. df = pd.DataFrame({"a": ["a", None, "b"]})
  832. tm.assert_frame_equal(df + df, pd.DataFrame({"a": ["aa", np.nan, "bb"]}))
  833. @pytest.mark.parametrize("dtype", ("float", "int64"))
  834. def test_frame_operators_empty_like(self, dtype):
  835. # Test for issue #10181
  836. frames = [
  837. pd.DataFrame(dtype=dtype),
  838. pd.DataFrame(columns=["A"], dtype=dtype),
  839. pd.DataFrame(index=[0], dtype=dtype),
  840. ]
  841. for df in frames:
  842. assert (df + df).equals(df)
  843. tm.assert_frame_equal(df + df, df)
  844. @pytest.mark.parametrize(
  845. "func",
  846. [lambda x: x * 2, lambda x: x[::2], lambda x: 5],
  847. ids=["multiply", "slice", "constant"],
  848. )
  849. def test_series_operators_arithmetic(self, all_arithmetic_functions, func):
  850. op = all_arithmetic_functions
  851. series = Series(
  852. np.arange(10, dtype=np.float64),
  853. index=date_range("2020-01-01", periods=10),
  854. name="ts",
  855. )
  856. other = func(series)
  857. compare_op(series, other, op)
  858. @pytest.mark.parametrize(
  859. "func", [lambda x: x + 1, lambda x: 5], ids=["add", "constant"]
  860. )
  861. def test_series_operators_compare(self, comparison_op, func):
  862. op = comparison_op
  863. series = Series(
  864. np.arange(10, dtype=np.float64),
  865. index=date_range("2020-01-01", periods=10),
  866. name="ts",
  867. )
  868. other = func(series)
  869. compare_op(series, other, op)
  870. @pytest.mark.parametrize(
  871. "func",
  872. [lambda x: x * 2, lambda x: x[::2], lambda x: 5],
  873. ids=["multiply", "slice", "constant"],
  874. )
  875. def test_divmod(self, func):
  876. series = Series(
  877. np.arange(10, dtype=np.float64),
  878. index=date_range("2020-01-01", periods=10),
  879. name="ts",
  880. )
  881. other = func(series)
  882. results = divmod(series, other)
  883. if isinstance(other, abc.Iterable) and len(series) != len(other):
  884. # if the lengths don't match, this is the test where we use
  885. # `tser[::2]`. Pad every other value in `other_np` with nan.
  886. other_np = []
  887. for n in other:
  888. other_np.append(n)
  889. other_np.append(np.nan)
  890. else:
  891. other_np = other
  892. other_np = np.asarray(other_np)
  893. with np.errstate(all="ignore"):
  894. expecteds = divmod(series.values, np.asarray(other_np))
  895. for result, expected in zip(results, expecteds):
  896. # check the values, name, and index separately
  897. tm.assert_almost_equal(np.asarray(result), expected)
  898. assert result.name == series.name
  899. tm.assert_index_equal(result.index, series.index._with_freq(None))
  900. def test_series_divmod_zero(self):
  901. # Check that divmod uses pandas convention for division by zero,
  902. # which does not match numpy.
  903. # pandas convention has
  904. # 1/0 == np.inf
  905. # -1/0 == -np.inf
  906. # 1/-0.0 == -np.inf
  907. # -1/-0.0 == np.inf
  908. tser = Series(
  909. np.arange(1, 11, dtype=np.float64),
  910. index=date_range("2020-01-01", periods=10),
  911. name="ts",
  912. )
  913. other = tser * 0
  914. result = divmod(tser, other)
  915. exp1 = Series([np.inf] * len(tser), index=tser.index, name="ts")
  916. exp2 = Series([np.nan] * len(tser), index=tser.index, name="ts")
  917. tm.assert_series_equal(result[0], exp1)
  918. tm.assert_series_equal(result[1], exp2)
  919. class TestUFuncCompat:
  920. # TODO: add more dtypes
  921. @pytest.mark.parametrize("holder", [Index, RangeIndex, Series])
  922. @pytest.mark.parametrize("dtype", [np.int64, np.uint64, np.float64])
  923. def test_ufunc_compat(self, holder, dtype):
  924. box = Series if holder is Series else Index
  925. if holder is RangeIndex:
  926. if dtype != np.int64:
  927. pytest.skip(f"dtype {dtype} not relevant for RangeIndex")
  928. idx = RangeIndex(0, 5, name="foo")
  929. else:
  930. idx = holder(np.arange(5, dtype=dtype), name="foo")
  931. result = np.sin(idx)
  932. expected = box(np.sin(np.arange(5, dtype=dtype)), name="foo")
  933. tm.assert_equal(result, expected)
  934. # TODO: add more dtypes
  935. @pytest.mark.parametrize("holder", [Index, Series])
  936. @pytest.mark.parametrize("dtype", [np.int64, np.uint64, np.float64])
  937. def test_ufunc_coercions(self, holder, dtype):
  938. idx = holder([1, 2, 3, 4, 5], dtype=dtype, name="x")
  939. box = Series if holder is Series else Index
  940. result = np.sqrt(idx)
  941. assert result.dtype == "f8" and isinstance(result, box)
  942. exp = Index(np.sqrt(np.array([1, 2, 3, 4, 5], dtype=np.float64)), name="x")
  943. exp = tm.box_expected(exp, box)
  944. tm.assert_equal(result, exp)
  945. result = np.divide(idx, 2.0)
  946. assert result.dtype == "f8" and isinstance(result, box)
  947. exp = Index([0.5, 1.0, 1.5, 2.0, 2.5], dtype=np.float64, name="x")
  948. exp = tm.box_expected(exp, box)
  949. tm.assert_equal(result, exp)
  950. # _evaluate_numeric_binop
  951. result = idx + 2.0
  952. assert result.dtype == "f8" and isinstance(result, box)
  953. exp = Index([3.0, 4.0, 5.0, 6.0, 7.0], dtype=np.float64, name="x")
  954. exp = tm.box_expected(exp, box)
  955. tm.assert_equal(result, exp)
  956. result = idx - 2.0
  957. assert result.dtype == "f8" and isinstance(result, box)
  958. exp = Index([-1.0, 0.0, 1.0, 2.0, 3.0], dtype=np.float64, name="x")
  959. exp = tm.box_expected(exp, box)
  960. tm.assert_equal(result, exp)
  961. result = idx * 1.0
  962. assert result.dtype == "f8" and isinstance(result, box)
  963. exp = Index([1.0, 2.0, 3.0, 4.0, 5.0], dtype=np.float64, name="x")
  964. exp = tm.box_expected(exp, box)
  965. tm.assert_equal(result, exp)
  966. result = idx / 2.0
  967. assert result.dtype == "f8" and isinstance(result, box)
  968. exp = Index([0.5, 1.0, 1.5, 2.0, 2.5], dtype=np.float64, name="x")
  969. exp = tm.box_expected(exp, box)
  970. tm.assert_equal(result, exp)
  971. # TODO: add more dtypes
  972. @pytest.mark.parametrize("holder", [Index, Series])
  973. @pytest.mark.parametrize("dtype", [np.int64, np.uint64, np.float64])
  974. def test_ufunc_multiple_return_values(self, holder, dtype):
  975. obj = holder([1, 2, 3], dtype=dtype, name="x")
  976. box = Series if holder is Series else Index
  977. result = np.modf(obj)
  978. assert isinstance(result, tuple)
  979. exp1 = Index([0.0, 0.0, 0.0], dtype=np.float64, name="x")
  980. exp2 = Index([1.0, 2.0, 3.0], dtype=np.float64, name="x")
  981. tm.assert_equal(result[0], tm.box_expected(exp1, box))
  982. tm.assert_equal(result[1], tm.box_expected(exp2, box))
  983. def test_ufunc_at(self):
  984. s = Series([0, 1, 2], index=[1, 2, 3], name="x")
  985. np.add.at(s, [0, 2], 10)
  986. expected = Series([10, 1, 12], index=[1, 2, 3], name="x")
  987. tm.assert_series_equal(s, expected)
  988. class TestObjectDtypeEquivalence:
  989. # Tests that arithmetic operations match operations executed elementwise
  990. @pytest.mark.parametrize("dtype", [None, object])
  991. def test_numarr_with_dtype_add_nan(self, dtype, box_with_array):
  992. box = box_with_array
  993. ser = Series([1, 2, 3], dtype=dtype)
  994. expected = Series([np.nan, np.nan, np.nan], dtype=dtype)
  995. ser = tm.box_expected(ser, box)
  996. expected = tm.box_expected(expected, box)
  997. result = np.nan + ser
  998. tm.assert_equal(result, expected)
  999. result = ser + np.nan
  1000. tm.assert_equal(result, expected)
  1001. @pytest.mark.parametrize("dtype", [None, object])
  1002. def test_numarr_with_dtype_add_int(self, dtype, box_with_array):
  1003. box = box_with_array
  1004. ser = Series([1, 2, 3], dtype=dtype)
  1005. expected = Series([2, 3, 4], dtype=dtype)
  1006. ser = tm.box_expected(ser, box)
  1007. expected = tm.box_expected(expected, box)
  1008. result = 1 + ser
  1009. tm.assert_equal(result, expected)
  1010. result = ser + 1
  1011. tm.assert_equal(result, expected)
  1012. # TODO: moved from tests.series.test_operators; needs cleanup
  1013. @pytest.mark.parametrize(
  1014. "op",
  1015. [operator.add, operator.sub, operator.mul, operator.truediv, operator.floordiv],
  1016. )
  1017. def test_operators_reverse_object(self, op):
  1018. # GH#56
  1019. arr = Series(
  1020. np.random.default_rng(2).standard_normal(10),
  1021. index=np.arange(10),
  1022. dtype=object,
  1023. )
  1024. result = op(1.0, arr)
  1025. expected = op(1.0, arr.astype(float))
  1026. tm.assert_series_equal(result.astype(float), expected)
  1027. class TestNumericArithmeticUnsorted:
  1028. # Tests in this class have been moved from type-specific test modules
  1029. # but not yet sorted, parametrized, and de-duplicated
  1030. @pytest.mark.parametrize(
  1031. "op",
  1032. [
  1033. operator.add,
  1034. operator.sub,
  1035. operator.mul,
  1036. operator.floordiv,
  1037. operator.truediv,
  1038. ],
  1039. )
  1040. @pytest.mark.parametrize(
  1041. "idx1",
  1042. [
  1043. RangeIndex(0, 10, 1),
  1044. RangeIndex(0, 20, 2),
  1045. RangeIndex(-10, 10, 2),
  1046. RangeIndex(5, -5, -1),
  1047. ],
  1048. )
  1049. @pytest.mark.parametrize(
  1050. "idx2",
  1051. [
  1052. RangeIndex(0, 10, 1),
  1053. RangeIndex(0, 20, 2),
  1054. RangeIndex(-10, 10, 2),
  1055. RangeIndex(5, -5, -1),
  1056. ],
  1057. )
  1058. def test_binops_index(self, op, idx1, idx2):
  1059. idx1 = idx1._rename("foo")
  1060. idx2 = idx2._rename("bar")
  1061. result = op(idx1, idx2)
  1062. expected = op(Index(idx1.to_numpy()), Index(idx2.to_numpy()))
  1063. tm.assert_index_equal(result, expected, exact="equiv")
  1064. @pytest.mark.parametrize(
  1065. "op",
  1066. [
  1067. operator.add,
  1068. operator.sub,
  1069. operator.mul,
  1070. operator.floordiv,
  1071. operator.truediv,
  1072. ],
  1073. )
  1074. @pytest.mark.parametrize(
  1075. "idx",
  1076. [
  1077. RangeIndex(0, 10, 1),
  1078. RangeIndex(0, 20, 2),
  1079. RangeIndex(-10, 10, 2),
  1080. RangeIndex(5, -5, -1),
  1081. ],
  1082. )
  1083. @pytest.mark.parametrize("scalar", [-1, 1, 2])
  1084. def test_binops_index_scalar(self, op, idx, scalar):
  1085. result = op(idx, scalar)
  1086. expected = op(Index(idx.to_numpy()), scalar)
  1087. tm.assert_index_equal(result, expected, exact="equiv")
  1088. @pytest.mark.parametrize("idx1", [RangeIndex(0, 10, 1), RangeIndex(0, 20, 2)])
  1089. @pytest.mark.parametrize("idx2", [RangeIndex(0, 10, 1), RangeIndex(0, 20, 2)])
  1090. def test_binops_index_pow(self, idx1, idx2):
  1091. # numpy does not allow powers of negative integers so test separately
  1092. # https://github.com/numpy/numpy/pull/8127
  1093. idx1 = idx1._rename("foo")
  1094. idx2 = idx2._rename("bar")
  1095. result = pow(idx1, idx2)
  1096. expected = pow(Index(idx1.to_numpy()), Index(idx2.to_numpy()))
  1097. tm.assert_index_equal(result, expected, exact="equiv")
  1098. @pytest.mark.parametrize("idx", [RangeIndex(0, 10, 1), RangeIndex(0, 20, 2)])
  1099. @pytest.mark.parametrize("scalar", [1, 2])
  1100. def test_binops_index_scalar_pow(self, idx, scalar):
  1101. # numpy does not allow powers of negative integers so test separately
  1102. # https://github.com/numpy/numpy/pull/8127
  1103. result = pow(idx, scalar)
  1104. expected = pow(Index(idx.to_numpy()), scalar)
  1105. tm.assert_index_equal(result, expected, exact="equiv")
  1106. # TODO: divmod?
  1107. @pytest.mark.parametrize(
  1108. "op",
  1109. [
  1110. operator.add,
  1111. operator.sub,
  1112. operator.mul,
  1113. operator.floordiv,
  1114. operator.truediv,
  1115. operator.pow,
  1116. operator.mod,
  1117. ],
  1118. )
  1119. def test_arithmetic_with_frame_or_series(self, op):
  1120. # check that we return NotImplemented when operating with Series
  1121. # or DataFrame
  1122. index = RangeIndex(5)
  1123. other = Series(np.random.default_rng(2).standard_normal(5))
  1124. expected = op(Series(index), other)
  1125. result = op(index, other)
  1126. tm.assert_series_equal(result, expected)
  1127. other = pd.DataFrame(np.random.default_rng(2).standard_normal((2, 5)))
  1128. expected = op(pd.DataFrame([index, index]), other)
  1129. result = op(index, other)
  1130. tm.assert_frame_equal(result, expected)
  1131. def test_numeric_compat2(self):
  1132. # validate that we are handling the RangeIndex overrides to numeric ops
  1133. # and returning RangeIndex where possible
  1134. idx = RangeIndex(0, 10, 2)
  1135. result = idx * 2
  1136. expected = RangeIndex(0, 20, 4)
  1137. tm.assert_index_equal(result, expected, exact=True)
  1138. result = idx + 2
  1139. expected = RangeIndex(2, 12, 2)
  1140. tm.assert_index_equal(result, expected, exact=True)
  1141. result = idx - 2
  1142. expected = RangeIndex(-2, 8, 2)
  1143. tm.assert_index_equal(result, expected, exact=True)
  1144. result = idx / 2
  1145. expected = RangeIndex(0, 5, 1).astype("float64")
  1146. tm.assert_index_equal(result, expected, exact=True)
  1147. result = idx / 4
  1148. expected = RangeIndex(0, 10, 2) / 4
  1149. tm.assert_index_equal(result, expected, exact=True)
  1150. result = idx // 1
  1151. expected = idx
  1152. tm.assert_index_equal(result, expected, exact=True)
  1153. # __mul__
  1154. result = idx * idx
  1155. expected = Index(idx.values * idx.values)
  1156. tm.assert_index_equal(result, expected, exact=True)
  1157. # __pow__
  1158. idx = RangeIndex(0, 1000, 2)
  1159. result = idx**2
  1160. expected = Index(idx._values) ** 2
  1161. tm.assert_index_equal(Index(result.values), expected, exact=True)
  1162. @pytest.mark.parametrize(
  1163. "idx, div, expected",
  1164. [
  1165. # TODO: add more dtypes
  1166. (RangeIndex(0, 1000, 2), 2, RangeIndex(0, 500, 1)),
  1167. (RangeIndex(-99, -201, -3), -3, RangeIndex(33, 67, 1)),
  1168. (
  1169. RangeIndex(0, 1000, 1),
  1170. 2,
  1171. Index(RangeIndex(0, 1000, 1)._values) // 2,
  1172. ),
  1173. (
  1174. RangeIndex(0, 100, 1),
  1175. 2.0,
  1176. Index(RangeIndex(0, 100, 1)._values) // 2.0,
  1177. ),
  1178. (RangeIndex(0), 50, RangeIndex(0)),
  1179. (RangeIndex(2, 4, 2), 3, RangeIndex(0, 1, 1)),
  1180. (RangeIndex(-5, -10, -6), 4, RangeIndex(-2, -1, 1)),
  1181. (RangeIndex(-100, -200, 3), 2, RangeIndex(0)),
  1182. ],
  1183. )
  1184. def test_numeric_compat2_floordiv(self, idx, div, expected):
  1185. # __floordiv__
  1186. tm.assert_index_equal(idx // div, expected, exact=True)
  1187. @pytest.mark.parametrize("dtype", [np.int64, np.float64])
  1188. @pytest.mark.parametrize("delta", [1, 0, -1])
  1189. def test_addsub_arithmetic(self, dtype, delta):
  1190. # GH#8142
  1191. delta = dtype(delta)
  1192. index = Index([10, 11, 12], dtype=dtype)
  1193. result = index + delta
  1194. expected = Index(index.values + delta, dtype=dtype)
  1195. tm.assert_index_equal(result, expected)
  1196. # this subtraction used to fail
  1197. result = index - delta
  1198. expected = Index(index.values - delta, dtype=dtype)
  1199. tm.assert_index_equal(result, expected)
  1200. tm.assert_index_equal(index + index, 2 * index)
  1201. tm.assert_index_equal(index - index, 0 * index)
  1202. assert not (index - index).empty
  1203. def test_pow_nan_with_zero(self, box_with_array):
  1204. left = Index([np.nan, np.nan, np.nan])
  1205. right = Index([0, 0, 0])
  1206. expected = Index([1.0, 1.0, 1.0])
  1207. left = tm.box_expected(left, box_with_array)
  1208. right = tm.box_expected(right, box_with_array)
  1209. expected = tm.box_expected(expected, box_with_array)
  1210. result = left**right
  1211. tm.assert_equal(result, expected)
  1212. def test_fill_value_inf_masking():
  1213. # GH #27464 make sure we mask 0/1 with Inf and not NaN
  1214. df = pd.DataFrame({"A": [0, 1, 2], "B": [1.1, None, 1.1]})
  1215. other = pd.DataFrame({"A": [1.1, 1.2, 1.3]}, index=[0, 2, 3])
  1216. result = df.rfloordiv(other, fill_value=1)
  1217. expected = pd.DataFrame(
  1218. {"A": [np.inf, 1.0, 0.0, 1.0], "B": [0.0, np.nan, 0.0, np.nan]}
  1219. )
  1220. tm.assert_frame_equal(result, expected)
  1221. def test_dataframe_div_silenced():
  1222. # GH#26793
  1223. pdf1 = pd.DataFrame(
  1224. {
  1225. "A": np.arange(10),
  1226. "B": [np.nan, 1, 2, 3, 4] * 2,
  1227. "C": [np.nan] * 10,
  1228. "D": np.arange(10),
  1229. },
  1230. index=list("abcdefghij"),
  1231. columns=list("ABCD"),
  1232. )
  1233. pdf2 = pd.DataFrame(
  1234. np.random.default_rng(2).standard_normal((10, 4)),
  1235. index=list("abcdefghjk"),
  1236. columns=list("ABCX"),
  1237. )
  1238. with tm.assert_produces_warning(None):
  1239. pdf1.div(pdf2, fill_value=0)
  1240. @pytest.mark.parametrize(
  1241. "data, expected_data",
  1242. [([0, 1, 2], [0, 2, 4])],
  1243. )
  1244. def test_integer_array_add_list_like(
  1245. box_pandas_1d_array, box_1d_array, data, expected_data
  1246. ):
  1247. # GH22606 Verify operators with IntegerArray and list-likes
  1248. arr = array(data, dtype="Int64")
  1249. container = box_pandas_1d_array(arr)
  1250. left = container + box_1d_array(data)
  1251. right = box_1d_array(data) + container
  1252. if Series in [box_1d_array, box_pandas_1d_array]:
  1253. cls = Series
  1254. elif Index in [box_1d_array, box_pandas_1d_array]:
  1255. cls = Index
  1256. else:
  1257. cls = array
  1258. expected = cls(expected_data, dtype="Int64")
  1259. tm.assert_equal(left, expected)
  1260. tm.assert_equal(right, expected)
  1261. def test_sub_multiindex_swapped_levels():
  1262. # GH 9952
  1263. df = pd.DataFrame(
  1264. {"a": np.random.default_rng(2).standard_normal(6)},
  1265. index=pd.MultiIndex.from_product(
  1266. [["a", "b"], [0, 1, 2]], names=["levA", "levB"]
  1267. ),
  1268. )
  1269. df2 = df.copy()
  1270. df2.index = df2.index.swaplevel(0, 1)
  1271. result = df - df2
  1272. expected = pd.DataFrame([0.0] * 6, columns=["a"], index=df.index)
  1273. tm.assert_frame_equal(result, expected)
  1274. @pytest.mark.parametrize("power", [1, 2, 5])
  1275. @pytest.mark.parametrize("string_size", [0, 1, 2, 5])
  1276. def test_empty_str_comparison(power, string_size):
  1277. # GH 37348
  1278. a = np.array(range(10**power))
  1279. right = pd.DataFrame(a, dtype=np.int64)
  1280. left = " " * string_size
  1281. result = right == left
  1282. expected = pd.DataFrame(np.zeros(right.shape, dtype=bool))
  1283. tm.assert_frame_equal(result, expected)
  1284. def test_series_add_sub_with_UInt64():
  1285. # GH 22023
  1286. series1 = Series([1, 2, 3])
  1287. series2 = Series([2, 1, 3], dtype="UInt64")
  1288. result = series1 + series2
  1289. expected = Series([3, 3, 6], dtype="Float64")
  1290. tm.assert_series_equal(result, expected)
  1291. result = series1 - series2
  1292. expected = Series([-1, 1, 0], dtype="Float64")
  1293. tm.assert_series_equal(result, expected)