test_conversion.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551
  1. import numpy as np
  2. import pytest
  3. from pandas.core.dtypes.common import (
  4. is_datetime64_dtype,
  5. is_timedelta64_dtype,
  6. )
  7. from pandas.core.dtypes.dtypes import DatetimeTZDtype
  8. import pandas as pd
  9. from pandas import (
  10. CategoricalIndex,
  11. Series,
  12. Timedelta,
  13. Timestamp,
  14. date_range,
  15. )
  16. import pandas._testing as tm
  17. from pandas.core.arrays import (
  18. DatetimeArray,
  19. IntervalArray,
  20. PandasArray,
  21. PeriodArray,
  22. SparseArray,
  23. TimedeltaArray,
  24. )
  25. class TestToIterable:
  26. # test that we convert an iterable to python types
  27. dtypes = [
  28. ("int8", int),
  29. ("int16", int),
  30. ("int32", int),
  31. ("int64", int),
  32. ("uint8", int),
  33. ("uint16", int),
  34. ("uint32", int),
  35. ("uint64", int),
  36. ("float16", float),
  37. ("float32", float),
  38. ("float64", float),
  39. ("datetime64[ns]", Timestamp),
  40. ("datetime64[ns, US/Eastern]", Timestamp),
  41. ("timedelta64[ns]", Timedelta),
  42. ]
  43. @pytest.mark.parametrize("dtype, rdtype", dtypes)
  44. @pytest.mark.parametrize(
  45. "method",
  46. [
  47. lambda x: x.tolist(),
  48. lambda x: x.to_list(),
  49. lambda x: list(x),
  50. lambda x: list(x.__iter__()),
  51. ],
  52. ids=["tolist", "to_list", "list", "iter"],
  53. )
  54. def test_iterable(self, index_or_series, method, dtype, rdtype):
  55. # gh-10904
  56. # gh-13258
  57. # coerce iteration to underlying python / pandas types
  58. typ = index_or_series
  59. if dtype == "float16" and issubclass(typ, pd.Index):
  60. with pytest.raises(NotImplementedError, match="float16 indexes are not "):
  61. typ([1], dtype=dtype)
  62. return
  63. s = typ([1], dtype=dtype)
  64. result = method(s)[0]
  65. assert isinstance(result, rdtype)
  66. @pytest.mark.parametrize(
  67. "dtype, rdtype, obj",
  68. [
  69. ("object", object, "a"),
  70. ("object", int, 1),
  71. ("category", object, "a"),
  72. ("category", int, 1),
  73. ],
  74. )
  75. @pytest.mark.parametrize(
  76. "method",
  77. [
  78. lambda x: x.tolist(),
  79. lambda x: x.to_list(),
  80. lambda x: list(x),
  81. lambda x: list(x.__iter__()),
  82. ],
  83. ids=["tolist", "to_list", "list", "iter"],
  84. )
  85. def test_iterable_object_and_category(
  86. self, index_or_series, method, dtype, rdtype, obj
  87. ):
  88. # gh-10904
  89. # gh-13258
  90. # coerce iteration to underlying python / pandas types
  91. typ = index_or_series
  92. s = typ([obj], dtype=dtype)
  93. result = method(s)[0]
  94. assert isinstance(result, rdtype)
  95. @pytest.mark.parametrize("dtype, rdtype", dtypes)
  96. def test_iterable_items(self, dtype, rdtype):
  97. # gh-13258
  98. # test if items yields the correct boxed scalars
  99. # this only applies to series
  100. s = Series([1], dtype=dtype)
  101. _, result = list(s.items())[0]
  102. assert isinstance(result, rdtype)
  103. _, result = list(s.items())[0]
  104. assert isinstance(result, rdtype)
  105. @pytest.mark.parametrize(
  106. "dtype, rdtype", dtypes + [("object", int), ("category", int)]
  107. )
  108. def test_iterable_map(self, index_or_series, dtype, rdtype):
  109. # gh-13236
  110. # coerce iteration to underlying python / pandas types
  111. typ = index_or_series
  112. if dtype == "float16" and issubclass(typ, pd.Index):
  113. with pytest.raises(NotImplementedError, match="float16 indexes are not "):
  114. typ([1], dtype=dtype)
  115. return
  116. s = typ([1], dtype=dtype)
  117. result = s.map(type)[0]
  118. if not isinstance(rdtype, tuple):
  119. rdtype = (rdtype,)
  120. assert result in rdtype
  121. @pytest.mark.parametrize(
  122. "method",
  123. [
  124. lambda x: x.tolist(),
  125. lambda x: x.to_list(),
  126. lambda x: list(x),
  127. lambda x: list(x.__iter__()),
  128. ],
  129. ids=["tolist", "to_list", "list", "iter"],
  130. )
  131. def test_categorial_datetimelike(self, method):
  132. i = CategoricalIndex([Timestamp("1999-12-31"), Timestamp("2000-12-31")])
  133. result = method(i)[0]
  134. assert isinstance(result, Timestamp)
  135. def test_iter_box(self):
  136. vals = [Timestamp("2011-01-01"), Timestamp("2011-01-02")]
  137. s = Series(vals)
  138. assert s.dtype == "datetime64[ns]"
  139. for res, exp in zip(s, vals):
  140. assert isinstance(res, Timestamp)
  141. assert res.tz is None
  142. assert res == exp
  143. vals = [
  144. Timestamp("2011-01-01", tz="US/Eastern"),
  145. Timestamp("2011-01-02", tz="US/Eastern"),
  146. ]
  147. s = Series(vals)
  148. assert s.dtype == "datetime64[ns, US/Eastern]"
  149. for res, exp in zip(s, vals):
  150. assert isinstance(res, Timestamp)
  151. assert res.tz == exp.tz
  152. assert res == exp
  153. # timedelta
  154. vals = [Timedelta("1 days"), Timedelta("2 days")]
  155. s = Series(vals)
  156. assert s.dtype == "timedelta64[ns]"
  157. for res, exp in zip(s, vals):
  158. assert isinstance(res, Timedelta)
  159. assert res == exp
  160. # period
  161. vals = [pd.Period("2011-01-01", freq="M"), pd.Period("2011-01-02", freq="M")]
  162. s = Series(vals)
  163. assert s.dtype == "Period[M]"
  164. for res, exp in zip(s, vals):
  165. assert isinstance(res, pd.Period)
  166. assert res.freq == "M"
  167. assert res == exp
  168. @pytest.mark.parametrize(
  169. "arr, expected_type, dtype",
  170. [
  171. (np.array([0, 1], dtype=np.int64), np.ndarray, "int64"),
  172. (np.array(["a", "b"]), np.ndarray, "object"),
  173. (pd.Categorical(["a", "b"]), pd.Categorical, "category"),
  174. (
  175. pd.DatetimeIndex(["2017", "2018"], tz="US/Central"),
  176. DatetimeArray,
  177. "datetime64[ns, US/Central]",
  178. ),
  179. (
  180. pd.PeriodIndex([2018, 2019], freq="A"),
  181. PeriodArray,
  182. pd.core.dtypes.dtypes.PeriodDtype("A-DEC"),
  183. ),
  184. (pd.IntervalIndex.from_breaks([0, 1, 2]), IntervalArray, "interval"),
  185. (
  186. pd.DatetimeIndex(["2017", "2018"]),
  187. DatetimeArray,
  188. "datetime64[ns]",
  189. ),
  190. (
  191. pd.TimedeltaIndex([10**10]),
  192. TimedeltaArray,
  193. "m8[ns]",
  194. ),
  195. ],
  196. )
  197. def test_values_consistent(arr, expected_type, dtype):
  198. l_values = Series(arr)._values
  199. r_values = pd.Index(arr)._values
  200. assert type(l_values) is expected_type
  201. assert type(l_values) is type(r_values)
  202. tm.assert_equal(l_values, r_values)
  203. @pytest.mark.parametrize("arr", [np.array([1, 2, 3])])
  204. def test_numpy_array(arr):
  205. ser = Series(arr)
  206. result = ser.array
  207. expected = PandasArray(arr)
  208. tm.assert_extension_array_equal(result, expected)
  209. def test_numpy_array_all_dtypes(any_numpy_dtype):
  210. ser = Series(dtype=any_numpy_dtype)
  211. result = ser.array
  212. if is_datetime64_dtype(any_numpy_dtype):
  213. assert isinstance(result, DatetimeArray)
  214. elif is_timedelta64_dtype(any_numpy_dtype):
  215. assert isinstance(result, TimedeltaArray)
  216. else:
  217. assert isinstance(result, PandasArray)
  218. @pytest.mark.parametrize(
  219. "arr, attr",
  220. [
  221. (pd.Categorical(["a", "b"]), "_codes"),
  222. (pd.core.arrays.period_array(["2000", "2001"], freq="D"), "_ndarray"),
  223. (pd.array([0, np.nan], dtype="Int64"), "_data"),
  224. (IntervalArray.from_breaks([0, 1]), "_left"),
  225. (SparseArray([0, 1]), "_sparse_values"),
  226. (DatetimeArray(np.array([1, 2], dtype="datetime64[ns]")), "_ndarray"),
  227. # tz-aware Datetime
  228. (
  229. DatetimeArray(
  230. np.array(
  231. ["2000-01-01T12:00:00", "2000-01-02T12:00:00"], dtype="M8[ns]"
  232. ),
  233. dtype=DatetimeTZDtype(tz="US/Central"),
  234. ),
  235. "_ndarray",
  236. ),
  237. ],
  238. )
  239. def test_array(arr, attr, index_or_series, request):
  240. box = index_or_series
  241. result = box(arr, copy=False).array
  242. if attr:
  243. arr = getattr(arr, attr)
  244. result = getattr(result, attr)
  245. assert result is arr
  246. def test_array_multiindex_raises():
  247. idx = pd.MultiIndex.from_product([["A"], ["a", "b"]])
  248. msg = "MultiIndex has no single backing array"
  249. with pytest.raises(ValueError, match=msg):
  250. idx.array
  251. @pytest.mark.parametrize(
  252. "arr, expected",
  253. [
  254. (np.array([1, 2], dtype=np.int64), np.array([1, 2], dtype=np.int64)),
  255. (pd.Categorical(["a", "b"]), np.array(["a", "b"], dtype=object)),
  256. (
  257. pd.core.arrays.period_array(["2000", "2001"], freq="D"),
  258. np.array([pd.Period("2000", freq="D"), pd.Period("2001", freq="D")]),
  259. ),
  260. (pd.array([0, np.nan], dtype="Int64"), np.array([0, pd.NA], dtype=object)),
  261. (
  262. IntervalArray.from_breaks([0, 1, 2]),
  263. np.array([pd.Interval(0, 1), pd.Interval(1, 2)], dtype=object),
  264. ),
  265. (SparseArray([0, 1]), np.array([0, 1], dtype=np.int64)),
  266. # tz-naive datetime
  267. (
  268. DatetimeArray(np.array(["2000", "2001"], dtype="M8[ns]")),
  269. np.array(["2000", "2001"], dtype="M8[ns]"),
  270. ),
  271. # tz-aware stays tz`-aware
  272. (
  273. DatetimeArray(
  274. np.array(
  275. ["2000-01-01T06:00:00", "2000-01-02T06:00:00"], dtype="M8[ns]"
  276. ),
  277. dtype=DatetimeTZDtype(tz="US/Central"),
  278. ),
  279. np.array(
  280. [
  281. Timestamp("2000-01-01", tz="US/Central"),
  282. Timestamp("2000-01-02", tz="US/Central"),
  283. ]
  284. ),
  285. ),
  286. # Timedelta
  287. (
  288. TimedeltaArray(np.array([0, 3600000000000], dtype="i8"), freq="H"),
  289. np.array([0, 3600000000000], dtype="m8[ns]"),
  290. ),
  291. # GH#26406 tz is preserved in Categorical[dt64tz]
  292. (
  293. pd.Categorical(date_range("2016-01-01", periods=2, tz="US/Pacific")),
  294. np.array(
  295. [
  296. Timestamp("2016-01-01", tz="US/Pacific"),
  297. Timestamp("2016-01-02", tz="US/Pacific"),
  298. ]
  299. ),
  300. ),
  301. ],
  302. )
  303. def test_to_numpy(arr, expected, index_or_series_or_array, request):
  304. box = index_or_series_or_array
  305. with tm.assert_produces_warning(None):
  306. thing = box(arr)
  307. if arr.dtype.name == "int64" and box is pd.array:
  308. mark = pytest.mark.xfail(reason="thing is Int64 and to_numpy() returns object")
  309. request.node.add_marker(mark)
  310. result = thing.to_numpy()
  311. tm.assert_numpy_array_equal(result, expected)
  312. result = np.asarray(thing)
  313. tm.assert_numpy_array_equal(result, expected)
  314. @pytest.mark.parametrize("as_series", [True, False])
  315. @pytest.mark.parametrize(
  316. "arr", [np.array([1, 2, 3], dtype="int64"), np.array(["a", "b", "c"], dtype=object)]
  317. )
  318. def test_to_numpy_copy(arr, as_series):
  319. obj = pd.Index(arr, copy=False)
  320. if as_series:
  321. obj = Series(obj.values, copy=False)
  322. # no copy by default
  323. result = obj.to_numpy()
  324. assert np.shares_memory(arr, result) is True
  325. result = obj.to_numpy(copy=False)
  326. assert np.shares_memory(arr, result) is True
  327. # copy=True
  328. result = obj.to_numpy(copy=True)
  329. assert np.shares_memory(arr, result) is False
  330. @pytest.mark.parametrize("as_series", [True, False])
  331. def test_to_numpy_dtype(as_series):
  332. tz = "US/Eastern"
  333. obj = pd.DatetimeIndex(["2000", "2001"], tz=tz)
  334. if as_series:
  335. obj = Series(obj)
  336. # preserve tz by default
  337. result = obj.to_numpy()
  338. expected = np.array(
  339. [Timestamp("2000", tz=tz), Timestamp("2001", tz=tz)], dtype=object
  340. )
  341. tm.assert_numpy_array_equal(result, expected)
  342. result = obj.to_numpy(dtype="object")
  343. tm.assert_numpy_array_equal(result, expected)
  344. result = obj.to_numpy(dtype="M8[ns]")
  345. expected = np.array(["2000-01-01T05", "2001-01-01T05"], dtype="M8[ns]")
  346. tm.assert_numpy_array_equal(result, expected)
  347. @pytest.mark.parametrize(
  348. "values, dtype, na_value, expected",
  349. [
  350. ([1, 2, None], "float64", 0, [1.0, 2.0, 0.0]),
  351. (
  352. [Timestamp("2000"), Timestamp("2000"), pd.NaT],
  353. None,
  354. Timestamp("2000"),
  355. [np.datetime64("2000-01-01T00:00:00.000000000")] * 3,
  356. ),
  357. ],
  358. )
  359. def test_to_numpy_na_value_numpy_dtype(
  360. index_or_series, values, dtype, na_value, expected
  361. ):
  362. obj = index_or_series(values)
  363. result = obj.to_numpy(dtype=dtype, na_value=na_value)
  364. expected = np.array(expected)
  365. tm.assert_numpy_array_equal(result, expected)
  366. @pytest.mark.parametrize(
  367. "data, multiindex, dtype, na_value, expected",
  368. [
  369. (
  370. [1, 2, None, 4],
  371. [(0, "a"), (0, "b"), (1, "b"), (1, "c")],
  372. float,
  373. None,
  374. [1.0, 2.0, np.nan, 4.0],
  375. ),
  376. (
  377. [1, 2, None, 4],
  378. [(0, "a"), (0, "b"), (1, "b"), (1, "c")],
  379. float,
  380. np.nan,
  381. [1.0, 2.0, np.nan, 4.0],
  382. ),
  383. (
  384. [1.0, 2.0, np.nan, 4.0],
  385. [("a", 0), ("a", 1), ("a", 2), ("b", 0)],
  386. int,
  387. 0,
  388. [1, 2, 0, 4],
  389. ),
  390. (
  391. [Timestamp("2000"), Timestamp("2000"), pd.NaT],
  392. [(0, Timestamp("2021")), (0, Timestamp("2022")), (1, Timestamp("2000"))],
  393. None,
  394. Timestamp("2000"),
  395. [np.datetime64("2000-01-01T00:00:00.000000000")] * 3,
  396. ),
  397. ],
  398. )
  399. def test_to_numpy_multiindex_series_na_value(
  400. data, multiindex, dtype, na_value, expected
  401. ):
  402. index = pd.MultiIndex.from_tuples(multiindex)
  403. series = Series(data, index=index)
  404. result = series.to_numpy(dtype=dtype, na_value=na_value)
  405. expected = np.array(expected)
  406. tm.assert_numpy_array_equal(result, expected)
  407. def test_to_numpy_kwargs_raises():
  408. # numpy
  409. s = Series([1, 2, 3])
  410. msg = r"to_numpy\(\) got an unexpected keyword argument 'foo'"
  411. with pytest.raises(TypeError, match=msg):
  412. s.to_numpy(foo=True)
  413. # extension
  414. s = Series([1, 2, 3], dtype="Int64")
  415. with pytest.raises(TypeError, match=msg):
  416. s.to_numpy(foo=True)
  417. @pytest.mark.parametrize(
  418. "data",
  419. [
  420. {"a": [1, 2, 3], "b": [1, 2, None]},
  421. {"a": np.array([1, 2, 3]), "b": np.array([1, 2, np.nan])},
  422. {"a": pd.array([1, 2, 3]), "b": pd.array([1, 2, None])},
  423. ],
  424. )
  425. @pytest.mark.parametrize("dtype, na_value", [(float, np.nan), (object, None)])
  426. def test_to_numpy_dataframe_na_value(data, dtype, na_value):
  427. # https://github.com/pandas-dev/pandas/issues/33820
  428. df = pd.DataFrame(data)
  429. result = df.to_numpy(dtype=dtype, na_value=na_value)
  430. expected = np.array([[1, 1], [2, 2], [3, na_value]], dtype=dtype)
  431. tm.assert_numpy_array_equal(result, expected)
  432. @pytest.mark.parametrize(
  433. "data, expected",
  434. [
  435. (
  436. {"a": pd.array([1, 2, None])},
  437. np.array([[1.0], [2.0], [np.nan]], dtype=float),
  438. ),
  439. (
  440. {"a": [1, 2, 3], "b": [1, 2, 3]},
  441. np.array([[1, 1], [2, 2], [3, 3]], dtype=float),
  442. ),
  443. ],
  444. )
  445. def test_to_numpy_dataframe_single_block(data, expected):
  446. # https://github.com/pandas-dev/pandas/issues/33820
  447. df = pd.DataFrame(data)
  448. result = df.to_numpy(dtype=float, na_value=np.nan)
  449. tm.assert_numpy_array_equal(result, expected)
  450. def test_to_numpy_dataframe_single_block_no_mutate():
  451. # https://github.com/pandas-dev/pandas/issues/33820
  452. result = pd.DataFrame(np.array([1.0, 2.0, np.nan]))
  453. expected = pd.DataFrame(np.array([1.0, 2.0, np.nan]))
  454. result.to_numpy(na_value=0.0)
  455. tm.assert_frame_equal(result, expected)
  456. class TestAsArray:
  457. @pytest.mark.parametrize("tz", [None, "US/Central"])
  458. def test_asarray_object_dt64(self, tz):
  459. ser = Series(date_range("2000", periods=2, tz=tz))
  460. with tm.assert_produces_warning(None):
  461. # Future behavior (for tzaware case) with no warning
  462. result = np.asarray(ser, dtype=object)
  463. expected = np.array(
  464. [Timestamp("2000-01-01", tz=tz), Timestamp("2000-01-02", tz=tz)]
  465. )
  466. tm.assert_numpy_array_equal(result, expected)
  467. def test_asarray_tz_naive(self):
  468. # This shouldn't produce a warning.
  469. ser = Series(date_range("2000", periods=2))
  470. expected = np.array(["2000-01-01", "2000-01-02"], dtype="M8[ns]")
  471. result = np.asarray(ser)
  472. tm.assert_numpy_array_equal(result, expected)
  473. def test_asarray_tz_aware(self):
  474. tz = "US/Central"
  475. ser = Series(date_range("2000", periods=2, tz=tz))
  476. expected = np.array(["2000-01-01T06", "2000-01-02T06"], dtype="M8[ns]")
  477. result = np.asarray(ser, dtype="datetime64[ns]")
  478. tm.assert_numpy_array_equal(result, expected)
  479. # Old behavior with no warning
  480. result = np.asarray(ser, dtype="M8[ns]")
  481. tm.assert_numpy_array_equal(result, expected)