test_decimal.py 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587
  1. from __future__ import annotations
  2. import decimal
  3. import operator
  4. import numpy as np
  5. import pytest
  6. from pandas.compat.numpy import np_version_gt2
  7. import pandas as pd
  8. import pandas._testing as tm
  9. from pandas.tests.extension import base
  10. from pandas.tests.extension.decimal.array import (
  11. DecimalArray,
  12. DecimalDtype,
  13. make_data,
  14. to_decimal,
  15. )
  16. @pytest.fixture
  17. def dtype():
  18. return DecimalDtype()
  19. @pytest.fixture
  20. def data():
  21. return DecimalArray(make_data())
  22. @pytest.fixture
  23. def data_for_twos():
  24. return DecimalArray([decimal.Decimal(2) for _ in range(100)])
  25. @pytest.fixture
  26. def data_missing():
  27. return DecimalArray([decimal.Decimal("NaN"), decimal.Decimal(1)])
  28. @pytest.fixture
  29. def data_for_sorting():
  30. return DecimalArray(
  31. [decimal.Decimal("1"), decimal.Decimal("2"), decimal.Decimal("0")]
  32. )
  33. @pytest.fixture
  34. def data_missing_for_sorting():
  35. return DecimalArray(
  36. [decimal.Decimal("1"), decimal.Decimal("NaN"), decimal.Decimal("0")]
  37. )
  38. @pytest.fixture
  39. def na_cmp():
  40. return lambda x, y: x.is_nan() and y.is_nan()
  41. @pytest.fixture
  42. def data_for_grouping():
  43. b = decimal.Decimal("1.0")
  44. a = decimal.Decimal("0.0")
  45. c = decimal.Decimal("2.0")
  46. na = decimal.Decimal("NaN")
  47. return DecimalArray([b, b, na, na, a, a, b, c])
  48. class TestDecimalArray(base.ExtensionTests):
  49. def _get_expected_exception(
  50. self, op_name: str, obj, other
  51. ) -> type[Exception] | tuple[type[Exception], ...] | None:
  52. return None
  53. def _supports_reduction(self, ser: pd.Series, op_name: str) -> bool:
  54. return True
  55. def check_reduce(self, ser: pd.Series, op_name: str, skipna: bool):
  56. if op_name == "count":
  57. return super().check_reduce(ser, op_name, skipna)
  58. else:
  59. result = getattr(ser, op_name)(skipna=skipna)
  60. expected = getattr(np.asarray(ser), op_name)()
  61. tm.assert_almost_equal(result, expected)
  62. def test_reduce_series_numeric(self, data, all_numeric_reductions, skipna, request):
  63. if all_numeric_reductions in ["kurt", "skew", "sem", "median"]:
  64. mark = pytest.mark.xfail(raises=NotImplementedError)
  65. request.applymarker(mark)
  66. super().test_reduce_series_numeric(data, all_numeric_reductions, skipna)
  67. def test_reduce_frame(self, data, all_numeric_reductions, skipna, request):
  68. op_name = all_numeric_reductions
  69. if op_name in ["skew", "median"]:
  70. mark = pytest.mark.xfail(raises=NotImplementedError)
  71. request.applymarker(mark)
  72. return super().test_reduce_frame(data, all_numeric_reductions, skipna)
  73. def test_compare_scalar(self, data, comparison_op):
  74. ser = pd.Series(data)
  75. self._compare_other(ser, data, comparison_op, 0.5)
  76. def test_compare_array(self, data, comparison_op):
  77. ser = pd.Series(data)
  78. alter = np.random.default_rng(2).choice([-1, 0, 1], len(data))
  79. # Randomly double, halve or keep same value
  80. other = pd.Series(data) * [decimal.Decimal(pow(2.0, i)) for i in alter]
  81. self._compare_other(ser, data, comparison_op, other)
  82. def test_arith_series_with_array(self, data, all_arithmetic_operators):
  83. op_name = all_arithmetic_operators
  84. ser = pd.Series(data)
  85. context = decimal.getcontext()
  86. divbyzerotrap = context.traps[decimal.DivisionByZero]
  87. invalidoptrap = context.traps[decimal.InvalidOperation]
  88. context.traps[decimal.DivisionByZero] = 0
  89. context.traps[decimal.InvalidOperation] = 0
  90. # Decimal supports ops with int, but not float
  91. other = pd.Series([int(d * 100) for d in data])
  92. self.check_opname(ser, op_name, other)
  93. if "mod" not in op_name:
  94. self.check_opname(ser, op_name, ser * 2)
  95. self.check_opname(ser, op_name, 0)
  96. self.check_opname(ser, op_name, 5)
  97. context.traps[decimal.DivisionByZero] = divbyzerotrap
  98. context.traps[decimal.InvalidOperation] = invalidoptrap
  99. def test_fillna_frame(self, data_missing):
  100. msg = "ExtensionArray.fillna added a 'copy' keyword"
  101. with tm.assert_produces_warning(
  102. DeprecationWarning, match=msg, check_stacklevel=False
  103. ):
  104. super().test_fillna_frame(data_missing)
  105. def test_fillna_limit_pad(self, data_missing):
  106. msg = "ExtensionArray.fillna 'method' keyword is deprecated"
  107. with tm.assert_produces_warning(
  108. DeprecationWarning,
  109. match=msg,
  110. check_stacklevel=False,
  111. raise_on_extra_warnings=False,
  112. ):
  113. super().test_fillna_limit_pad(data_missing)
  114. msg = "The 'method' keyword in DecimalArray.fillna is deprecated"
  115. with tm.assert_produces_warning(
  116. FutureWarning,
  117. match=msg,
  118. check_stacklevel=False,
  119. raise_on_extra_warnings=False,
  120. ):
  121. super().test_fillna_limit_pad(data_missing)
  122. @pytest.mark.parametrize(
  123. "limit_area, input_ilocs, expected_ilocs",
  124. [
  125. ("outside", [1, 0, 0, 0, 1], [1, 0, 0, 0, 1]),
  126. ("outside", [1, 0, 1, 0, 1], [1, 0, 1, 0, 1]),
  127. ("outside", [0, 1, 1, 1, 0], [0, 1, 1, 1, 1]),
  128. ("outside", [0, 1, 0, 1, 0], [0, 1, 0, 1, 1]),
  129. ("inside", [1, 0, 0, 0, 1], [1, 1, 1, 1, 1]),
  130. ("inside", [1, 0, 1, 0, 1], [1, 1, 1, 1, 1]),
  131. ("inside", [0, 1, 1, 1, 0], [0, 1, 1, 1, 0]),
  132. ("inside", [0, 1, 0, 1, 0], [0, 1, 1, 1, 0]),
  133. ],
  134. )
  135. def test_ffill_limit_area(
  136. self, data_missing, limit_area, input_ilocs, expected_ilocs
  137. ):
  138. # GH#56616
  139. msg = "ExtensionArray.fillna 'method' keyword is deprecated"
  140. with tm.assert_produces_warning(
  141. DeprecationWarning,
  142. match=msg,
  143. check_stacklevel=False,
  144. raise_on_extra_warnings=False,
  145. ):
  146. msg = "DecimalArray does not implement limit_area"
  147. with pytest.raises(NotImplementedError, match=msg):
  148. super().test_ffill_limit_area(
  149. data_missing, limit_area, input_ilocs, expected_ilocs
  150. )
  151. def test_fillna_limit_backfill(self, data_missing):
  152. msg = "Series.fillna with 'method' is deprecated"
  153. with tm.assert_produces_warning(
  154. FutureWarning,
  155. match=msg,
  156. check_stacklevel=False,
  157. raise_on_extra_warnings=False,
  158. ):
  159. super().test_fillna_limit_backfill(data_missing)
  160. msg = "ExtensionArray.fillna 'method' keyword is deprecated"
  161. with tm.assert_produces_warning(
  162. DeprecationWarning,
  163. match=msg,
  164. check_stacklevel=False,
  165. raise_on_extra_warnings=False,
  166. ):
  167. super().test_fillna_limit_backfill(data_missing)
  168. msg = "The 'method' keyword in DecimalArray.fillna is deprecated"
  169. with tm.assert_produces_warning(
  170. FutureWarning,
  171. match=msg,
  172. check_stacklevel=False,
  173. raise_on_extra_warnings=False,
  174. ):
  175. super().test_fillna_limit_backfill(data_missing)
  176. def test_fillna_no_op_returns_copy(self, data):
  177. msg = "|".join(
  178. [
  179. "ExtensionArray.fillna 'method' keyword is deprecated",
  180. "The 'method' keyword in DecimalArray.fillna is deprecated",
  181. ]
  182. )
  183. with tm.assert_produces_warning(
  184. (FutureWarning, DeprecationWarning), match=msg, check_stacklevel=False
  185. ):
  186. super().test_fillna_no_op_returns_copy(data)
  187. def test_fillna_series(self, data_missing):
  188. msg = "ExtensionArray.fillna added a 'copy' keyword"
  189. with tm.assert_produces_warning(
  190. DeprecationWarning, match=msg, check_stacklevel=False
  191. ):
  192. super().test_fillna_series(data_missing)
  193. def test_fillna_series_method(self, data_missing, fillna_method):
  194. msg = "|".join(
  195. [
  196. "ExtensionArray.fillna 'method' keyword is deprecated",
  197. "The 'method' keyword in DecimalArray.fillna is deprecated",
  198. ]
  199. )
  200. with tm.assert_produces_warning(
  201. (FutureWarning, DeprecationWarning), match=msg, check_stacklevel=False
  202. ):
  203. super().test_fillna_series_method(data_missing, fillna_method)
  204. def test_fillna_copy_frame(self, data_missing, using_copy_on_write):
  205. warn = DeprecationWarning if not using_copy_on_write else None
  206. msg = "ExtensionArray.fillna added a 'copy' keyword"
  207. with tm.assert_produces_warning(warn, match=msg, check_stacklevel=False):
  208. super().test_fillna_copy_frame(data_missing)
  209. def test_fillna_copy_series(self, data_missing, using_copy_on_write):
  210. warn = DeprecationWarning if not using_copy_on_write else None
  211. msg = "ExtensionArray.fillna added a 'copy' keyword"
  212. with tm.assert_produces_warning(warn, match=msg, check_stacklevel=False):
  213. super().test_fillna_copy_series(data_missing)
  214. @pytest.mark.parametrize("dropna", [True, False])
  215. def test_value_counts(self, all_data, dropna, request):
  216. all_data = all_data[:10]
  217. if dropna:
  218. other = np.array(all_data[~all_data.isna()])
  219. else:
  220. other = all_data
  221. vcs = pd.Series(all_data).value_counts(dropna=dropna)
  222. vcs_ex = pd.Series(other).value_counts(dropna=dropna)
  223. with decimal.localcontext() as ctx:
  224. # avoid raising when comparing Decimal("NAN") < Decimal(2)
  225. ctx.traps[decimal.InvalidOperation] = False
  226. result = vcs.sort_index()
  227. expected = vcs_ex.sort_index()
  228. tm.assert_series_equal(result, expected)
  229. def test_series_repr(self, data):
  230. # Overriding this base test to explicitly test that
  231. # the custom _formatter is used
  232. ser = pd.Series(data)
  233. assert data.dtype.name in repr(ser)
  234. assert "Decimal: " in repr(ser)
  235. @pytest.mark.xfail(reason="Inconsistent array-vs-scalar behavior")
  236. @pytest.mark.parametrize("ufunc", [np.positive, np.negative, np.abs])
  237. def test_unary_ufunc_dunder_equivalence(self, data, ufunc):
  238. super().test_unary_ufunc_dunder_equivalence(data, ufunc)
  239. def test_array_interface_copy(self, data):
  240. result_copy1 = np.array(data, copy=True)
  241. result_copy2 = np.array(data, copy=True)
  242. assert not np.may_share_memory(result_copy1, result_copy2)
  243. if not np_version_gt2:
  244. # copy=False semantics are only supported in NumPy>=2.
  245. return
  246. try:
  247. result_nocopy1 = np.array(data, copy=False)
  248. except ValueError:
  249. # An error is always acceptable for `copy=False`
  250. return
  251. result_nocopy2 = np.array(data, copy=False)
  252. # If copy=False was given and did not raise, these must share the same data
  253. assert np.may_share_memory(result_nocopy1, result_nocopy2)
  254. def test_take_na_value_other_decimal():
  255. arr = DecimalArray([decimal.Decimal("1.0"), decimal.Decimal("2.0")])
  256. result = arr.take([0, -1], allow_fill=True, fill_value=decimal.Decimal("-1.0"))
  257. expected = DecimalArray([decimal.Decimal("1.0"), decimal.Decimal("-1.0")])
  258. tm.assert_extension_array_equal(result, expected)
  259. def test_series_constructor_coerce_data_to_extension_dtype():
  260. dtype = DecimalDtype()
  261. ser = pd.Series([0, 1, 2], dtype=dtype)
  262. arr = DecimalArray(
  263. [decimal.Decimal(0), decimal.Decimal(1), decimal.Decimal(2)],
  264. dtype=dtype,
  265. )
  266. exp = pd.Series(arr)
  267. tm.assert_series_equal(ser, exp)
  268. def test_series_constructor_with_dtype():
  269. arr = DecimalArray([decimal.Decimal("10.0")])
  270. result = pd.Series(arr, dtype=DecimalDtype())
  271. expected = pd.Series(arr)
  272. tm.assert_series_equal(result, expected)
  273. result = pd.Series(arr, dtype="int64")
  274. expected = pd.Series([10])
  275. tm.assert_series_equal(result, expected)
  276. def test_dataframe_constructor_with_dtype():
  277. arr = DecimalArray([decimal.Decimal("10.0")])
  278. result = pd.DataFrame({"A": arr}, dtype=DecimalDtype())
  279. expected = pd.DataFrame({"A": arr})
  280. tm.assert_frame_equal(result, expected)
  281. arr = DecimalArray([decimal.Decimal("10.0")])
  282. result = pd.DataFrame({"A": arr}, dtype="int64")
  283. expected = pd.DataFrame({"A": [10]})
  284. tm.assert_frame_equal(result, expected)
  285. @pytest.mark.parametrize("frame", [True, False])
  286. def test_astype_dispatches(frame):
  287. # This is a dtype-specific test that ensures Series[decimal].astype
  288. # gets all the way through to ExtensionArray.astype
  289. # Designing a reliable smoke test that works for arbitrary data types
  290. # is difficult.
  291. data = pd.Series(DecimalArray([decimal.Decimal(2)]), name="a")
  292. ctx = decimal.Context()
  293. ctx.prec = 5
  294. if frame:
  295. data = data.to_frame()
  296. result = data.astype(DecimalDtype(ctx))
  297. if frame:
  298. result = result["a"]
  299. assert result.dtype.context.prec == ctx.prec
  300. class DecimalArrayWithoutFromSequence(DecimalArray):
  301. """Helper class for testing error handling in _from_sequence."""
  302. @classmethod
  303. def _from_sequence(cls, scalars, *, dtype=None, copy=False):
  304. raise KeyError("For the test")
  305. class DecimalArrayWithoutCoercion(DecimalArrayWithoutFromSequence):
  306. @classmethod
  307. def _create_arithmetic_method(cls, op):
  308. return cls._create_method(op, coerce_to_dtype=False)
  309. DecimalArrayWithoutCoercion._add_arithmetic_ops()
  310. def test_combine_from_sequence_raises(monkeypatch):
  311. # https://github.com/pandas-dev/pandas/issues/22850
  312. cls = DecimalArrayWithoutFromSequence
  313. @classmethod
  314. def construct_array_type(cls):
  315. return DecimalArrayWithoutFromSequence
  316. monkeypatch.setattr(DecimalDtype, "construct_array_type", construct_array_type)
  317. arr = cls([decimal.Decimal("1.0"), decimal.Decimal("2.0")])
  318. ser = pd.Series(arr)
  319. result = ser.combine(ser, operator.add)
  320. # note: object dtype
  321. expected = pd.Series(
  322. [decimal.Decimal("2.0"), decimal.Decimal("4.0")], dtype="object"
  323. )
  324. tm.assert_series_equal(result, expected)
  325. @pytest.mark.parametrize(
  326. "class_", [DecimalArrayWithoutFromSequence, DecimalArrayWithoutCoercion]
  327. )
  328. def test_scalar_ops_from_sequence_raises(class_):
  329. # op(EA, EA) should return an EA, or an ndarray if it's not possible
  330. # to return an EA with the return values.
  331. arr = class_([decimal.Decimal("1.0"), decimal.Decimal("2.0")])
  332. result = arr + arr
  333. expected = np.array(
  334. [decimal.Decimal("2.0"), decimal.Decimal("4.0")], dtype="object"
  335. )
  336. tm.assert_numpy_array_equal(result, expected)
  337. @pytest.mark.parametrize(
  338. "reverse, expected_div, expected_mod",
  339. [(False, [0, 1, 1, 2], [1, 0, 1, 0]), (True, [2, 1, 0, 0], [0, 0, 2, 2])],
  340. )
  341. def test_divmod_array(reverse, expected_div, expected_mod):
  342. # https://github.com/pandas-dev/pandas/issues/22930
  343. arr = to_decimal([1, 2, 3, 4])
  344. if reverse:
  345. div, mod = divmod(2, arr)
  346. else:
  347. div, mod = divmod(arr, 2)
  348. expected_div = to_decimal(expected_div)
  349. expected_mod = to_decimal(expected_mod)
  350. tm.assert_extension_array_equal(div, expected_div)
  351. tm.assert_extension_array_equal(mod, expected_mod)
  352. def test_ufunc_fallback(data):
  353. a = data[:5]
  354. s = pd.Series(a, index=range(3, 8))
  355. result = np.abs(s)
  356. expected = pd.Series(np.abs(a), index=range(3, 8))
  357. tm.assert_series_equal(result, expected)
  358. def test_array_ufunc():
  359. a = to_decimal([1, 2, 3])
  360. result = np.exp(a)
  361. expected = to_decimal(np.exp(a._data))
  362. tm.assert_extension_array_equal(result, expected)
  363. def test_array_ufunc_series():
  364. a = to_decimal([1, 2, 3])
  365. s = pd.Series(a)
  366. result = np.exp(s)
  367. expected = pd.Series(to_decimal(np.exp(a._data)))
  368. tm.assert_series_equal(result, expected)
  369. def test_array_ufunc_series_scalar_other():
  370. # check _HANDLED_TYPES
  371. a = to_decimal([1, 2, 3])
  372. s = pd.Series(a)
  373. result = np.add(s, decimal.Decimal(1))
  374. expected = pd.Series(np.add(a, decimal.Decimal(1)))
  375. tm.assert_series_equal(result, expected)
  376. def test_array_ufunc_series_defer():
  377. a = to_decimal([1, 2, 3])
  378. s = pd.Series(a)
  379. expected = pd.Series(to_decimal([2, 4, 6]))
  380. r1 = np.add(s, a)
  381. r2 = np.add(a, s)
  382. tm.assert_series_equal(r1, expected)
  383. tm.assert_series_equal(r2, expected)
  384. def test_groupby_agg():
  385. # Ensure that the result of agg is inferred to be decimal dtype
  386. # https://github.com/pandas-dev/pandas/issues/29141
  387. data = make_data()[:5]
  388. df = pd.DataFrame(
  389. {"id1": [0, 0, 0, 1, 1], "id2": [0, 1, 0, 1, 1], "decimals": DecimalArray(data)}
  390. )
  391. # single key, selected column
  392. expected = pd.Series(to_decimal([data[0], data[3]]))
  393. result = df.groupby("id1")["decimals"].agg(lambda x: x.iloc[0])
  394. tm.assert_series_equal(result, expected, check_names=False)
  395. result = df["decimals"].groupby(df["id1"]).agg(lambda x: x.iloc[0])
  396. tm.assert_series_equal(result, expected, check_names=False)
  397. # multiple keys, selected column
  398. expected = pd.Series(
  399. to_decimal([data[0], data[1], data[3]]),
  400. index=pd.MultiIndex.from_tuples([(0, 0), (0, 1), (1, 1)]),
  401. )
  402. result = df.groupby(["id1", "id2"])["decimals"].agg(lambda x: x.iloc[0])
  403. tm.assert_series_equal(result, expected, check_names=False)
  404. result = df["decimals"].groupby([df["id1"], df["id2"]]).agg(lambda x: x.iloc[0])
  405. tm.assert_series_equal(result, expected, check_names=False)
  406. # multiple columns
  407. expected = pd.DataFrame({"id2": [0, 1], "decimals": to_decimal([data[0], data[3]])})
  408. result = df.groupby("id1").agg(lambda x: x.iloc[0])
  409. tm.assert_frame_equal(result, expected, check_names=False)
  410. def test_groupby_agg_ea_method(monkeypatch):
  411. # Ensure that the result of agg is inferred to be decimal dtype
  412. # https://github.com/pandas-dev/pandas/issues/29141
  413. def DecimalArray__my_sum(self):
  414. return np.sum(np.array(self))
  415. monkeypatch.setattr(DecimalArray, "my_sum", DecimalArray__my_sum, raising=False)
  416. data = make_data()[:5]
  417. df = pd.DataFrame({"id": [0, 0, 0, 1, 1], "decimals": DecimalArray(data)})
  418. expected = pd.Series(to_decimal([data[0] + data[1] + data[2], data[3] + data[4]]))
  419. result = df.groupby("id")["decimals"].agg(lambda x: x.values.my_sum())
  420. tm.assert_series_equal(result, expected, check_names=False)
  421. s = pd.Series(DecimalArray(data))
  422. grouper = np.array([0, 0, 0, 1, 1], dtype=np.int64)
  423. result = s.groupby(grouper).agg(lambda x: x.values.my_sum())
  424. tm.assert_series_equal(result, expected, check_names=False)
  425. def test_indexing_no_materialize(monkeypatch):
  426. # See https://github.com/pandas-dev/pandas/issues/29708
  427. # Ensure that indexing operations do not materialize (convert to a numpy
  428. # array) the ExtensionArray unnecessary
  429. def DecimalArray__array__(self, dtype=None):
  430. raise Exception("tried to convert a DecimalArray to a numpy array")
  431. monkeypatch.setattr(DecimalArray, "__array__", DecimalArray__array__, raising=False)
  432. data = make_data()
  433. s = pd.Series(DecimalArray(data))
  434. df = pd.DataFrame({"a": s, "b": range(len(s))})
  435. # ensure the following operations do not raise an error
  436. s[s > 0.5]
  437. df[s > 0.5]
  438. s.at[0]
  439. df.at[0, "a"]
  440. def test_to_numpy_keyword():
  441. # test the extra keyword
  442. values = [decimal.Decimal("1.1111"), decimal.Decimal("2.2222")]
  443. expected = np.array(
  444. [decimal.Decimal("1.11"), decimal.Decimal("2.22")], dtype="object"
  445. )
  446. a = pd.array(values, dtype="decimal")
  447. result = a.to_numpy(decimals=2)
  448. tm.assert_numpy_array_equal(result, expected)
  449. result = pd.Series(a).to_numpy(decimals=2)
  450. tm.assert_numpy_array_equal(result, expected)
  451. def test_array_copy_on_write(using_copy_on_write):
  452. df = pd.DataFrame({"a": [decimal.Decimal(2), decimal.Decimal(3)]}, dtype="object")
  453. df2 = df.astype(DecimalDtype())
  454. df.iloc[0, 0] = 0
  455. if using_copy_on_write:
  456. expected = pd.DataFrame(
  457. {"a": [decimal.Decimal(2), decimal.Decimal(3)]}, dtype=DecimalDtype()
  458. )
  459. tm.assert_equal(df2.values, expected.values)