test_arithmetic.py 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971
  1. from datetime import (
  2. date,
  3. timedelta,
  4. timezone,
  5. )
  6. from decimal import Decimal
  7. import operator
  8. import numpy as np
  9. import pytest
  10. from pandas._libs import lib
  11. from pandas._libs.tslibs import IncompatibleFrequency
  12. import pandas as pd
  13. from pandas import (
  14. Categorical,
  15. DatetimeTZDtype,
  16. Index,
  17. Series,
  18. Timedelta,
  19. bdate_range,
  20. date_range,
  21. isna,
  22. )
  23. import pandas._testing as tm
  24. from pandas.core import ops
  25. from pandas.core.computation import expressions as expr
  26. from pandas.core.computation.check import NUMEXPR_INSTALLED
  27. @pytest.fixture(autouse=True, params=[0, 1000000], ids=["numexpr", "python"])
  28. def switch_numexpr_min_elements(request, monkeypatch):
  29. with monkeypatch.context() as m:
  30. m.setattr(expr, "_MIN_ELEMENTS", request.param)
  31. yield
  32. def _permute(obj):
  33. return obj.take(np.random.default_rng(2).permutation(len(obj)))
  34. class TestSeriesFlexArithmetic:
  35. @pytest.mark.parametrize(
  36. "ts",
  37. [
  38. (lambda x: x, lambda x: x * 2, False),
  39. (lambda x: x, lambda x: x[::2], False),
  40. (lambda x: x, lambda x: 5, True),
  41. (
  42. lambda x: Series(range(10), dtype=np.float64),
  43. lambda x: Series(range(10), dtype=np.float64),
  44. True,
  45. ),
  46. ],
  47. )
  48. @pytest.mark.parametrize(
  49. "opname", ["add", "sub", "mul", "floordiv", "truediv", "pow"]
  50. )
  51. def test_flex_method_equivalence(self, opname, ts):
  52. # check that Series.{opname} behaves like Series.__{opname}__,
  53. tser = Series(
  54. np.arange(20, dtype=np.float64),
  55. index=date_range("2020-01-01", periods=20),
  56. name="ts",
  57. )
  58. series = ts[0](tser)
  59. other = ts[1](tser)
  60. check_reverse = ts[2]
  61. op = getattr(Series, opname)
  62. alt = getattr(operator, opname)
  63. result = op(series, other)
  64. expected = alt(series, other)
  65. tm.assert_almost_equal(result, expected)
  66. if check_reverse:
  67. rop = getattr(Series, "r" + opname)
  68. result = rop(series, other)
  69. expected = alt(other, series)
  70. tm.assert_almost_equal(result, expected)
  71. def test_flex_method_subclass_metadata_preservation(self, all_arithmetic_operators):
  72. # GH 13208
  73. class MySeries(Series):
  74. _metadata = ["x"]
  75. @property
  76. def _constructor(self):
  77. return MySeries
  78. opname = all_arithmetic_operators
  79. op = getattr(Series, opname)
  80. m = MySeries([1, 2, 3], name="test")
  81. m.x = 42
  82. result = op(m, 1)
  83. assert result.x == 42
  84. def test_flex_add_scalar_fill_value(self):
  85. # GH12723
  86. ser = Series([0, 1, np.nan, 3, 4, 5])
  87. exp = ser.fillna(0).add(2)
  88. res = ser.add(2, fill_value=0)
  89. tm.assert_series_equal(res, exp)
  90. pairings = [(Series.div, operator.truediv, 1), (Series.rdiv, ops.rtruediv, 1)]
  91. for op in ["add", "sub", "mul", "pow", "truediv", "floordiv"]:
  92. fv = 0
  93. lop = getattr(Series, op)
  94. lequiv = getattr(operator, op)
  95. rop = getattr(Series, "r" + op)
  96. # bind op at definition time...
  97. requiv = lambda x, y, op=op: getattr(operator, op)(y, x)
  98. pairings.append((lop, lequiv, fv))
  99. pairings.append((rop, requiv, fv))
  100. @pytest.mark.parametrize("op, equiv_op, fv", pairings)
  101. def test_operators_combine(self, op, equiv_op, fv):
  102. def _check_fill(meth, op, a, b, fill_value=0):
  103. exp_index = a.index.union(b.index)
  104. a = a.reindex(exp_index)
  105. b = b.reindex(exp_index)
  106. amask = isna(a)
  107. bmask = isna(b)
  108. exp_values = []
  109. for i in range(len(exp_index)):
  110. with np.errstate(all="ignore"):
  111. if amask[i]:
  112. if bmask[i]:
  113. exp_values.append(np.nan)
  114. continue
  115. exp_values.append(op(fill_value, b[i]))
  116. elif bmask[i]:
  117. if amask[i]:
  118. exp_values.append(np.nan)
  119. continue
  120. exp_values.append(op(a[i], fill_value))
  121. else:
  122. exp_values.append(op(a[i], b[i]))
  123. result = meth(a, b, fill_value=fill_value)
  124. expected = Series(exp_values, exp_index)
  125. tm.assert_series_equal(result, expected)
  126. a = Series([np.nan, 1.0, 2.0, 3.0, np.nan], index=np.arange(5))
  127. b = Series([np.nan, 1, np.nan, 3, np.nan, 4.0], index=np.arange(6))
  128. result = op(a, b)
  129. exp = equiv_op(a, b)
  130. tm.assert_series_equal(result, exp)
  131. _check_fill(op, equiv_op, a, b, fill_value=fv)
  132. # should accept axis=0 or axis='rows'
  133. op(a, b, axis=0)
  134. class TestSeriesArithmetic:
  135. # Some of these may end up in tests/arithmetic, but are not yet sorted
  136. def test_add_series_with_period_index(self):
  137. rng = pd.period_range("1/1/2000", "1/1/2010", freq="Y")
  138. ts = Series(np.random.default_rng(2).standard_normal(len(rng)), index=rng)
  139. result = ts + ts[::2]
  140. expected = ts + ts
  141. expected.iloc[1::2] = np.nan
  142. tm.assert_series_equal(result, expected)
  143. result = ts + _permute(ts[::2])
  144. tm.assert_series_equal(result, expected)
  145. msg = "Input has different freq=D from Period\\(freq=Y-DEC\\)"
  146. with pytest.raises(IncompatibleFrequency, match=msg):
  147. ts + ts.asfreq("D", how="end")
  148. @pytest.mark.parametrize(
  149. "target_add,input_value,expected_value",
  150. [
  151. ("!", ["hello", "world"], ["hello!", "world!"]),
  152. ("m", ["hello", "world"], ["hellom", "worldm"]),
  153. ],
  154. )
  155. def test_string_addition(self, target_add, input_value, expected_value):
  156. # GH28658 - ensure adding 'm' does not raise an error
  157. a = Series(input_value)
  158. result = a + target_add
  159. expected = Series(expected_value)
  160. tm.assert_series_equal(result, expected)
  161. def test_divmod(self):
  162. # GH#25557
  163. a = Series([1, 1, 1, np.nan], index=["a", "b", "c", "d"])
  164. b = Series([2, np.nan, 1, np.nan], index=["a", "b", "d", "e"])
  165. result = a.divmod(b)
  166. expected = divmod(a, b)
  167. tm.assert_series_equal(result[0], expected[0])
  168. tm.assert_series_equal(result[1], expected[1])
  169. result = a.rdivmod(b)
  170. expected = divmod(b, a)
  171. tm.assert_series_equal(result[0], expected[0])
  172. tm.assert_series_equal(result[1], expected[1])
  173. @pytest.mark.parametrize("index", [None, range(9)])
  174. def test_series_integer_mod(self, index):
  175. # GH#24396
  176. s1 = Series(range(1, 10))
  177. s2 = Series("foo", index=index)
  178. msg = "not all arguments converted during string formatting|'mod' not supported"
  179. with pytest.raises(TypeError, match=msg):
  180. s2 % s1
  181. def test_add_with_duplicate_index(self):
  182. # GH14227
  183. s1 = Series([1, 2], index=[1, 1])
  184. s2 = Series([10, 10], index=[1, 2])
  185. result = s1 + s2
  186. expected = Series([11, 12, np.nan], index=[1, 1, 2])
  187. tm.assert_series_equal(result, expected)
  188. def test_add_na_handling(self):
  189. ser = Series(
  190. [Decimal("1.3"), Decimal("2.3")], index=[date(2012, 1, 1), date(2012, 1, 2)]
  191. )
  192. result = ser + ser.shift(1)
  193. result2 = ser.shift(1) + ser
  194. assert isna(result.iloc[0])
  195. assert isna(result2.iloc[0])
  196. def test_add_corner_cases(self, datetime_series):
  197. empty = Series([], index=Index([]), dtype=np.float64)
  198. result = datetime_series + empty
  199. assert np.isnan(result).all()
  200. result = empty + empty.copy()
  201. assert len(result) == 0
  202. def test_add_float_plus_int(self, datetime_series):
  203. # float + int
  204. int_ts = datetime_series.astype(int)[:-5]
  205. added = datetime_series + int_ts
  206. expected = Series(
  207. datetime_series.values[:-5] + int_ts.values,
  208. index=datetime_series.index[:-5],
  209. name="ts",
  210. )
  211. tm.assert_series_equal(added[:-5], expected)
  212. def test_mul_empty_int_corner_case(self):
  213. s1 = Series([], [], dtype=np.int32)
  214. s2 = Series({"x": 0.0})
  215. tm.assert_series_equal(s1 * s2, Series([np.nan], index=["x"]))
  216. def test_sub_datetimelike_align(self):
  217. # GH#7500
  218. # datetimelike ops need to align
  219. dt = Series(date_range("2012-1-1", periods=3, freq="D"))
  220. dt.iloc[2] = np.nan
  221. dt2 = dt[::-1]
  222. expected = Series([timedelta(0), timedelta(0), pd.NaT])
  223. # name is reset
  224. result = dt2 - dt
  225. tm.assert_series_equal(result, expected)
  226. expected = Series(expected, name=0)
  227. result = (dt2.to_frame() - dt.to_frame())[0]
  228. tm.assert_series_equal(result, expected)
  229. def test_alignment_doesnt_change_tz(self):
  230. # GH#33671
  231. dti = date_range("2016-01-01", periods=10, tz="CET")
  232. dti_utc = dti.tz_convert("UTC")
  233. ser = Series(10, index=dti)
  234. ser_utc = Series(10, index=dti_utc)
  235. # we don't care about the result, just that original indexes are unchanged
  236. ser * ser_utc
  237. assert ser.index is dti
  238. assert ser_utc.index is dti_utc
  239. def test_alignment_categorical(self):
  240. # GH13365
  241. cat = Categorical(["3z53", "3z53", "LoJG", "LoJG", "LoJG", "N503"])
  242. ser1 = Series(2, index=cat)
  243. ser2 = Series(2, index=cat[:-1])
  244. result = ser1 * ser2
  245. exp_index = ["3z53"] * 4 + ["LoJG"] * 9 + ["N503"]
  246. exp_index = pd.CategoricalIndex(exp_index, categories=cat.categories)
  247. exp_values = [4.0] * 13 + [np.nan]
  248. expected = Series(exp_values, exp_index)
  249. tm.assert_series_equal(result, expected)
  250. def test_arithmetic_with_duplicate_index(self):
  251. # GH#8363
  252. # integer ops with a non-unique index
  253. index = [2, 2, 3, 3, 4]
  254. ser = Series(np.arange(1, 6, dtype="int64"), index=index)
  255. other = Series(np.arange(5, dtype="int64"), index=index)
  256. result = ser - other
  257. expected = Series(1, index=[2, 2, 3, 3, 4])
  258. tm.assert_series_equal(result, expected)
  259. # GH#8363
  260. # datetime ops with a non-unique index
  261. ser = Series(date_range("20130101 09:00:00", periods=5), index=index)
  262. other = Series(date_range("20130101", periods=5), index=index)
  263. result = ser - other
  264. expected = Series(Timedelta("9 hours"), index=[2, 2, 3, 3, 4])
  265. tm.assert_series_equal(result, expected)
  266. def test_masked_and_non_masked_propagate_na(self):
  267. # GH#45810
  268. ser1 = Series([0, np.nan], dtype="float")
  269. ser2 = Series([0, 1], dtype="Int64")
  270. result = ser1 * ser2
  271. expected = Series([0, pd.NA], dtype="Float64")
  272. tm.assert_series_equal(result, expected)
  273. def test_mask_div_propagate_na_for_non_na_dtype(self):
  274. # GH#42630
  275. ser1 = Series([15, pd.NA, 5, 4], dtype="Int64")
  276. ser2 = Series([15, 5, np.nan, 4])
  277. result = ser1 / ser2
  278. expected = Series([1.0, pd.NA, pd.NA, 1.0], dtype="Float64")
  279. tm.assert_series_equal(result, expected)
  280. result = ser2 / ser1
  281. tm.assert_series_equal(result, expected)
  282. @pytest.mark.parametrize("val, dtype", [(3, "Int64"), (3.5, "Float64")])
  283. def test_add_list_to_masked_array(self, val, dtype):
  284. # GH#22962
  285. ser = Series([1, None, 3], dtype="Int64")
  286. result = ser + [1, None, val]
  287. expected = Series([2, None, 3 + val], dtype=dtype)
  288. tm.assert_series_equal(result, expected)
  289. result = [1, None, val] + ser
  290. tm.assert_series_equal(result, expected)
  291. def test_add_list_to_masked_array_boolean(self, request):
  292. # GH#22962
  293. warning = (
  294. UserWarning
  295. if request.node.callspec.id == "numexpr" and NUMEXPR_INSTALLED
  296. else None
  297. )
  298. ser = Series([True, None, False], dtype="boolean")
  299. with tm.assert_produces_warning(warning):
  300. result = ser + [True, None, True]
  301. expected = Series([True, None, True], dtype="boolean")
  302. tm.assert_series_equal(result, expected)
  303. with tm.assert_produces_warning(warning):
  304. result = [True, None, True] + ser
  305. tm.assert_series_equal(result, expected)
  306. # ------------------------------------------------------------------
  307. # Comparisons
  308. class TestSeriesFlexComparison:
  309. @pytest.mark.parametrize("axis", [0, None, "index"])
  310. def test_comparison_flex_basic(self, axis, comparison_op):
  311. left = Series(np.random.default_rng(2).standard_normal(10))
  312. right = Series(np.random.default_rng(2).standard_normal(10))
  313. result = getattr(left, comparison_op.__name__)(right, axis=axis)
  314. expected = comparison_op(left, right)
  315. tm.assert_series_equal(result, expected)
  316. def test_comparison_bad_axis(self, comparison_op):
  317. left = Series(np.random.default_rng(2).standard_normal(10))
  318. right = Series(np.random.default_rng(2).standard_normal(10))
  319. msg = "No axis named 1 for object type"
  320. with pytest.raises(ValueError, match=msg):
  321. getattr(left, comparison_op.__name__)(right, axis=1)
  322. @pytest.mark.parametrize(
  323. "values, op",
  324. [
  325. ([False, False, True, False], "eq"),
  326. ([True, True, False, True], "ne"),
  327. ([False, False, True, False], "le"),
  328. ([False, False, False, False], "lt"),
  329. ([False, True, True, False], "ge"),
  330. ([False, True, False, False], "gt"),
  331. ],
  332. )
  333. def test_comparison_flex_alignment(self, values, op):
  334. left = Series([1, 3, 2], index=list("abc"))
  335. right = Series([2, 2, 2], index=list("bcd"))
  336. result = getattr(left, op)(right)
  337. expected = Series(values, index=list("abcd"))
  338. tm.assert_series_equal(result, expected)
  339. @pytest.mark.parametrize(
  340. "values, op, fill_value",
  341. [
  342. ([False, False, True, True], "eq", 2),
  343. ([True, True, False, False], "ne", 2),
  344. ([False, False, True, True], "le", 0),
  345. ([False, False, False, True], "lt", 0),
  346. ([True, True, True, False], "ge", 0),
  347. ([True, True, False, False], "gt", 0),
  348. ],
  349. )
  350. def test_comparison_flex_alignment_fill(self, values, op, fill_value):
  351. left = Series([1, 3, 2], index=list("abc"))
  352. right = Series([2, 2, 2], index=list("bcd"))
  353. result = getattr(left, op)(right, fill_value=fill_value)
  354. expected = Series(values, index=list("abcd"))
  355. tm.assert_series_equal(result, expected)
  356. class TestSeriesComparison:
  357. def test_comparison_different_length(self):
  358. a = Series(["a", "b", "c"])
  359. b = Series(["b", "a"])
  360. msg = "only compare identically-labeled Series"
  361. with pytest.raises(ValueError, match=msg):
  362. a < b
  363. a = Series([1, 2])
  364. b = Series([2, 3, 4])
  365. with pytest.raises(ValueError, match=msg):
  366. a == b
  367. @pytest.mark.parametrize("opname", ["eq", "ne", "gt", "lt", "ge", "le"])
  368. def test_ser_flex_cmp_return_dtypes(self, opname):
  369. # GH#15115
  370. ser = Series([1, 3, 2], index=range(3))
  371. const = 2
  372. result = getattr(ser, opname)(const).dtypes
  373. expected = np.dtype("bool")
  374. assert result == expected
  375. @pytest.mark.parametrize("opname", ["eq", "ne", "gt", "lt", "ge", "le"])
  376. def test_ser_flex_cmp_return_dtypes_empty(self, opname):
  377. # GH#15115 empty Series case
  378. ser = Series([1, 3, 2], index=range(3))
  379. empty = ser.iloc[:0]
  380. const = 2
  381. result = getattr(empty, opname)(const).dtypes
  382. expected = np.dtype("bool")
  383. assert result == expected
  384. @pytest.mark.parametrize(
  385. "names", [(None, None, None), ("foo", "bar", None), ("baz", "baz", "baz")]
  386. )
  387. def test_ser_cmp_result_names(self, names, comparison_op):
  388. # datetime64 dtype
  389. op = comparison_op
  390. dti = date_range("1949-06-07 03:00:00", freq="h", periods=5, name=names[0])
  391. ser = Series(dti).rename(names[1])
  392. result = op(ser, dti)
  393. assert result.name == names[2]
  394. # datetime64tz dtype
  395. dti = dti.tz_localize("US/Central")
  396. dti = pd.DatetimeIndex(dti, freq="infer") # freq not preserved by tz_localize
  397. ser = Series(dti).rename(names[1])
  398. result = op(ser, dti)
  399. assert result.name == names[2]
  400. # timedelta64 dtype
  401. tdi = dti - dti.shift(1)
  402. ser = Series(tdi).rename(names[1])
  403. result = op(ser, tdi)
  404. assert result.name == names[2]
  405. # interval dtype
  406. if op in [operator.eq, operator.ne]:
  407. # interval dtype comparisons not yet implemented
  408. ii = pd.interval_range(start=0, periods=5, name=names[0])
  409. ser = Series(ii).rename(names[1])
  410. result = op(ser, ii)
  411. assert result.name == names[2]
  412. # categorical
  413. if op in [operator.eq, operator.ne]:
  414. # categorical dtype comparisons raise for inequalities
  415. cidx = tdi.astype("category")
  416. ser = Series(cidx).rename(names[1])
  417. result = op(ser, cidx)
  418. assert result.name == names[2]
  419. def test_comparisons(self):
  420. s = Series(["a", "b", "c"])
  421. s2 = Series([False, True, False])
  422. # it works!
  423. exp = Series([False, False, False])
  424. tm.assert_series_equal(s == s2, exp)
  425. tm.assert_series_equal(s2 == s, exp)
  426. # -----------------------------------------------------------------
  427. # Categorical Dtype Comparisons
  428. def test_categorical_comparisons(self):
  429. # GH#8938
  430. # allow equality comparisons
  431. a = Series(list("abc"), dtype="category")
  432. b = Series(list("abc"), dtype="object")
  433. c = Series(["a", "b", "cc"], dtype="object")
  434. d = Series(list("acb"), dtype="object")
  435. e = Categorical(list("abc"))
  436. f = Categorical(list("acb"))
  437. # vs scalar
  438. assert not (a == "a").all()
  439. assert ((a != "a") == ~(a == "a")).all()
  440. assert not ("a" == a).all()
  441. assert (a == "a")[0]
  442. assert ("a" == a)[0]
  443. assert not ("a" != a)[0]
  444. # vs list-like
  445. assert (a == a).all()
  446. assert not (a != a).all()
  447. assert (a == list(a)).all()
  448. assert (a == b).all()
  449. assert (b == a).all()
  450. assert ((~(a == b)) == (a != b)).all()
  451. assert ((~(b == a)) == (b != a)).all()
  452. assert not (a == c).all()
  453. assert not (c == a).all()
  454. assert not (a == d).all()
  455. assert not (d == a).all()
  456. # vs a cat-like
  457. assert (a == e).all()
  458. assert (e == a).all()
  459. assert not (a == f).all()
  460. assert not (f == a).all()
  461. assert (~(a == e) == (a != e)).all()
  462. assert (~(e == a) == (e != a)).all()
  463. assert (~(a == f) == (a != f)).all()
  464. assert (~(f == a) == (f != a)).all()
  465. # non-equality is not comparable
  466. msg = "can only compare equality or not"
  467. with pytest.raises(TypeError, match=msg):
  468. a < b
  469. with pytest.raises(TypeError, match=msg):
  470. b < a
  471. with pytest.raises(TypeError, match=msg):
  472. a > b
  473. with pytest.raises(TypeError, match=msg):
  474. b > a
  475. def test_unequal_categorical_comparison_raises_type_error(self):
  476. # unequal comparison should raise for unordered cats
  477. cat = Series(Categorical(list("abc")))
  478. msg = "can only compare equality or not"
  479. with pytest.raises(TypeError, match=msg):
  480. cat > "b"
  481. cat = Series(Categorical(list("abc"), ordered=False))
  482. with pytest.raises(TypeError, match=msg):
  483. cat > "b"
  484. # https://github.com/pandas-dev/pandas/issues/9836#issuecomment-92123057
  485. # and following comparisons with scalars not in categories should raise
  486. # for unequal comps, but not for equal/not equal
  487. cat = Series(Categorical(list("abc"), ordered=True))
  488. msg = "Invalid comparison between dtype=category and str"
  489. with pytest.raises(TypeError, match=msg):
  490. cat < "d"
  491. with pytest.raises(TypeError, match=msg):
  492. cat > "d"
  493. with pytest.raises(TypeError, match=msg):
  494. "d" < cat
  495. with pytest.raises(TypeError, match=msg):
  496. "d" > cat
  497. tm.assert_series_equal(cat == "d", Series([False, False, False]))
  498. tm.assert_series_equal(cat != "d", Series([True, True, True]))
  499. # -----------------------------------------------------------------
  500. def test_comparison_tuples(self):
  501. # GH#11339
  502. # comparisons vs tuple
  503. s = Series([(1, 1), (1, 2)])
  504. result = s == (1, 2)
  505. expected = Series([False, True])
  506. tm.assert_series_equal(result, expected)
  507. result = s != (1, 2)
  508. expected = Series([True, False])
  509. tm.assert_series_equal(result, expected)
  510. result = s == (0, 0)
  511. expected = Series([False, False])
  512. tm.assert_series_equal(result, expected)
  513. result = s != (0, 0)
  514. expected = Series([True, True])
  515. tm.assert_series_equal(result, expected)
  516. s = Series([(1, 1), (1, 1)])
  517. result = s == (1, 1)
  518. expected = Series([True, True])
  519. tm.assert_series_equal(result, expected)
  520. result = s != (1, 1)
  521. expected = Series([False, False])
  522. tm.assert_series_equal(result, expected)
  523. def test_comparison_frozenset(self):
  524. ser = Series([frozenset([1]), frozenset([1, 2])])
  525. result = ser == frozenset([1])
  526. expected = Series([True, False])
  527. tm.assert_series_equal(result, expected)
  528. def test_comparison_operators_with_nas(self, comparison_op):
  529. ser = Series(bdate_range("1/1/2000", periods=10), dtype=object)
  530. ser[::2] = np.nan
  531. # test that comparisons work
  532. val = ser[5]
  533. result = comparison_op(ser, val)
  534. expected = comparison_op(ser.dropna(), val).reindex(ser.index)
  535. msg = "Downcasting object dtype arrays"
  536. with tm.assert_produces_warning(FutureWarning, match=msg):
  537. if comparison_op is operator.ne:
  538. expected = expected.fillna(True).astype(bool)
  539. else:
  540. expected = expected.fillna(False).astype(bool)
  541. tm.assert_series_equal(result, expected)
  542. def test_ne(self):
  543. ts = Series([3, 4, 5, 6, 7], [3, 4, 5, 6, 7], dtype=float)
  544. expected = np.array([True, True, False, True, True])
  545. tm.assert_numpy_array_equal(ts.index != 5, expected)
  546. tm.assert_numpy_array_equal(~(ts.index == 5), expected)
  547. @pytest.mark.parametrize(
  548. "left, right",
  549. [
  550. (
  551. Series([1, 2, 3], index=list("ABC"), name="x"),
  552. Series([2, 2, 2], index=list("ABD"), name="x"),
  553. ),
  554. (
  555. Series([1, 2, 3], index=list("ABC"), name="x"),
  556. Series([2, 2, 2, 2], index=list("ABCD"), name="x"),
  557. ),
  558. ],
  559. )
  560. def test_comp_ops_df_compat(self, left, right, frame_or_series):
  561. # GH 1134
  562. # GH 50083 to clarify that index and columns must be identically labeled
  563. if frame_or_series is not Series:
  564. msg = (
  565. rf"Can only compare identically-labeled \(both index and columns\) "
  566. f"{frame_or_series.__name__} objects"
  567. )
  568. left = left.to_frame()
  569. right = right.to_frame()
  570. else:
  571. msg = (
  572. f"Can only compare identically-labeled {frame_or_series.__name__} "
  573. f"objects"
  574. )
  575. with pytest.raises(ValueError, match=msg):
  576. left == right
  577. with pytest.raises(ValueError, match=msg):
  578. right == left
  579. with pytest.raises(ValueError, match=msg):
  580. left != right
  581. with pytest.raises(ValueError, match=msg):
  582. right != left
  583. with pytest.raises(ValueError, match=msg):
  584. left < right
  585. with pytest.raises(ValueError, match=msg):
  586. right < left
  587. def test_compare_series_interval_keyword(self):
  588. # GH#25338
  589. ser = Series(["IntervalA", "IntervalB", "IntervalC"])
  590. result = ser == "IntervalA"
  591. expected = Series([True, False, False])
  592. tm.assert_series_equal(result, expected)
  593. # ------------------------------------------------------------------
  594. # Unsorted
  595. # These arithmetic tests were previously in other files, eventually
  596. # should be parametrized and put into tests.arithmetic
  597. class TestTimeSeriesArithmetic:
  598. def test_series_add_tz_mismatch_converts_to_utc(self):
  599. rng = date_range("1/1/2011", periods=100, freq="h", tz="utc")
  600. perm = np.random.default_rng(2).permutation(100)[:90]
  601. ser1 = Series(
  602. np.random.default_rng(2).standard_normal(90),
  603. index=rng.take(perm).tz_convert("US/Eastern"),
  604. )
  605. perm = np.random.default_rng(2).permutation(100)[:90]
  606. ser2 = Series(
  607. np.random.default_rng(2).standard_normal(90),
  608. index=rng.take(perm).tz_convert("Europe/Berlin"),
  609. )
  610. result = ser1 + ser2
  611. uts1 = ser1.tz_convert("utc")
  612. uts2 = ser2.tz_convert("utc")
  613. expected = uts1 + uts2
  614. # sort since input indexes are not equal
  615. expected = expected.sort_index()
  616. assert result.index.tz is timezone.utc
  617. tm.assert_series_equal(result, expected)
  618. def test_series_add_aware_naive_raises(self):
  619. rng = date_range("1/1/2011", periods=10, freq="h")
  620. ser = Series(np.random.default_rng(2).standard_normal(len(rng)), index=rng)
  621. ser_utc = ser.tz_localize("utc")
  622. msg = "Cannot join tz-naive with tz-aware DatetimeIndex"
  623. with pytest.raises(Exception, match=msg):
  624. ser + ser_utc
  625. with pytest.raises(Exception, match=msg):
  626. ser_utc + ser
  627. # TODO: belongs in tests/arithmetic?
  628. def test_datetime_understood(self, unit):
  629. # Ensures it doesn't fail to create the right series
  630. # reported in issue#16726
  631. series = Series(date_range("2012-01-01", periods=3, unit=unit))
  632. offset = pd.offsets.DateOffset(days=6)
  633. result = series - offset
  634. exp_dti = pd.to_datetime(["2011-12-26", "2011-12-27", "2011-12-28"]).as_unit(
  635. unit
  636. )
  637. expected = Series(exp_dti)
  638. tm.assert_series_equal(result, expected)
  639. def test_align_date_objects_with_datetimeindex(self):
  640. rng = date_range("1/1/2000", periods=20)
  641. ts = Series(np.random.default_rng(2).standard_normal(20), index=rng)
  642. ts_slice = ts[5:]
  643. ts2 = ts_slice.copy()
  644. ts2.index = [x.date() for x in ts2.index]
  645. result = ts + ts2
  646. result2 = ts2 + ts
  647. expected = ts + ts[5:]
  648. expected.index = expected.index._with_freq(None)
  649. tm.assert_series_equal(result, expected)
  650. tm.assert_series_equal(result2, expected)
  651. class TestNamePreservation:
  652. @pytest.mark.parametrize("box", [list, tuple, np.array, Index, Series, pd.array])
  653. @pytest.mark.parametrize("flex", [True, False])
  654. def test_series_ops_name_retention(self, flex, box, names, all_binary_operators):
  655. # GH#33930 consistent name-retention
  656. op = all_binary_operators
  657. left = Series(range(10), name=names[0])
  658. right = Series(range(10), name=names[1])
  659. name = op.__name__.strip("_")
  660. is_logical = name in ["and", "rand", "xor", "rxor", "or", "ror"]
  661. msg = (
  662. r"Logical ops \(and, or, xor\) between Pandas objects and "
  663. "dtype-less sequences"
  664. )
  665. warn = None
  666. if box in [list, tuple] and is_logical:
  667. warn = FutureWarning
  668. right = box(right)
  669. if flex:
  670. if is_logical:
  671. # Series doesn't have these as flex methods
  672. return
  673. result = getattr(left, name)(right)
  674. else:
  675. # GH#37374 logical ops behaving as set ops deprecated
  676. with tm.assert_produces_warning(warn, match=msg):
  677. result = op(left, right)
  678. assert isinstance(result, Series)
  679. if box in [Index, Series]:
  680. assert result.name is names[2] or result.name == names[2]
  681. else:
  682. assert result.name is names[0] or result.name == names[0]
  683. def test_binop_maybe_preserve_name(self, datetime_series):
  684. # names match, preserve
  685. result = datetime_series * datetime_series
  686. assert result.name == datetime_series.name
  687. result = datetime_series.mul(datetime_series)
  688. assert result.name == datetime_series.name
  689. result = datetime_series * datetime_series[:-2]
  690. assert result.name == datetime_series.name
  691. # names don't match, don't preserve
  692. cp = datetime_series.copy()
  693. cp.name = "something else"
  694. result = datetime_series + cp
  695. assert result.name is None
  696. result = datetime_series.add(cp)
  697. assert result.name is None
  698. ops = ["add", "sub", "mul", "div", "truediv", "floordiv", "mod", "pow"]
  699. ops = ops + ["r" + op for op in ops]
  700. for op in ops:
  701. # names match, preserve
  702. ser = datetime_series.copy()
  703. result = getattr(ser, op)(ser)
  704. assert result.name == datetime_series.name
  705. # names don't match, don't preserve
  706. cp = datetime_series.copy()
  707. cp.name = "changed"
  708. result = getattr(ser, op)(cp)
  709. assert result.name is None
  710. def test_scalarop_preserve_name(self, datetime_series):
  711. result = datetime_series * 2
  712. assert result.name == datetime_series.name
  713. class TestInplaceOperations:
  714. @pytest.mark.parametrize(
  715. "dtype1, dtype2, dtype_expected, dtype_mul",
  716. (
  717. ("Int64", "Int64", "Int64", "Int64"),
  718. ("float", "float", "float", "float"),
  719. ("Int64", "float", "Float64", "Float64"),
  720. ("Int64", "Float64", "Float64", "Float64"),
  721. ),
  722. )
  723. def test_series_inplace_ops(self, dtype1, dtype2, dtype_expected, dtype_mul):
  724. # GH 37910
  725. ser1 = Series([1], dtype=dtype1)
  726. ser2 = Series([2], dtype=dtype2)
  727. ser1 += ser2
  728. expected = Series([3], dtype=dtype_expected)
  729. tm.assert_series_equal(ser1, expected)
  730. ser1 -= ser2
  731. expected = Series([1], dtype=dtype_expected)
  732. tm.assert_series_equal(ser1, expected)
  733. ser1 *= ser2
  734. expected = Series([2], dtype=dtype_mul)
  735. tm.assert_series_equal(ser1, expected)
  736. def test_none_comparison(request, series_with_simple_index):
  737. series = series_with_simple_index
  738. if len(series) < 1:
  739. request.applymarker(
  740. pytest.mark.xfail(reason="Test doesn't make sense on empty data")
  741. )
  742. # bug brought up by #1079
  743. # changed from TypeError in 0.17.0
  744. series.iloc[0] = np.nan
  745. # noinspection PyComparisonWithNone
  746. result = series == None # noqa: E711
  747. assert not result.iat[0]
  748. assert not result.iat[1]
  749. # noinspection PyComparisonWithNone
  750. result = series != None # noqa: E711
  751. assert result.iat[0]
  752. assert result.iat[1]
  753. result = None == series # noqa: E711
  754. assert not result.iat[0]
  755. assert not result.iat[1]
  756. result = None != series # noqa: E711
  757. assert result.iat[0]
  758. assert result.iat[1]
  759. if lib.is_np_dtype(series.dtype, "M") or isinstance(series.dtype, DatetimeTZDtype):
  760. # Following DatetimeIndex (and Timestamp) convention,
  761. # inequality comparisons with Series[datetime64] raise
  762. msg = "Invalid comparison"
  763. with pytest.raises(TypeError, match=msg):
  764. None > series
  765. with pytest.raises(TypeError, match=msg):
  766. series > None
  767. else:
  768. result = None > series
  769. assert not result.iat[0]
  770. assert not result.iat[1]
  771. result = series < None
  772. assert not result.iat[0]
  773. assert not result.iat[1]
  774. def test_series_varied_multiindex_alignment():
  775. # GH 20414
  776. s1 = Series(
  777. range(8),
  778. index=pd.MultiIndex.from_product(
  779. [list("ab"), list("xy"), [1, 2]], names=["ab", "xy", "num"]
  780. ),
  781. )
  782. s2 = Series(
  783. [1000 * i for i in range(1, 5)],
  784. index=pd.MultiIndex.from_product([list("xy"), [1, 2]], names=["xy", "num"]),
  785. )
  786. result = s1.loc[pd.IndexSlice[["a"], :, :]] + s2
  787. expected = Series(
  788. [1000, 2001, 3002, 4003],
  789. index=pd.MultiIndex.from_tuples(
  790. [("a", "x", 1), ("a", "x", 2), ("a", "y", 1), ("a", "y", 2)],
  791. names=["ab", "xy", "num"],
  792. ),
  793. )
  794. tm.assert_series_equal(result, expected)
  795. def test_rmod_consistent_large_series():
  796. # GH 29602
  797. result = Series([2] * 10001).rmod(-1)
  798. expected = Series([1] * 10001)
  799. tm.assert_series_equal(result, expected)