test_datetimelike.py 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813
  1. from typing import Type, Union
  2. import numpy as np
  3. import pytest
  4. from pandas._libs import OutOfBoundsDatetime
  5. from pandas.compat.numpy import _np_version_under1p18
  6. import pandas as pd
  7. import pandas._testing as tm
  8. from pandas.core.arrays import DatetimeArray, PeriodArray, TimedeltaArray
  9. from pandas.core.indexes.datetimes import DatetimeIndex
  10. from pandas.core.indexes.period import PeriodIndex
  11. from pandas.core.indexes.timedeltas import TimedeltaIndex
  12. # TODO: more freq variants
  13. @pytest.fixture(params=["D", "B", "W", "M", "Q", "Y"])
  14. def period_index(request):
  15. """
  16. A fixture to provide PeriodIndex objects with different frequencies.
  17. Most PeriodArray behavior is already tested in PeriodIndex tests,
  18. so here we just test that the PeriodArray behavior matches
  19. the PeriodIndex behavior.
  20. """
  21. freqstr = request.param
  22. # TODO: non-monotone indexes; NaTs, different start dates
  23. pi = pd.period_range(start=pd.Timestamp("2000-01-01"), periods=100, freq=freqstr)
  24. return pi
  25. @pytest.fixture(params=["D", "B", "W", "M", "Q", "Y"])
  26. def datetime_index(request):
  27. """
  28. A fixture to provide DatetimeIndex objects with different frequencies.
  29. Most DatetimeArray behavior is already tested in DatetimeIndex tests,
  30. so here we just test that the DatetimeArray behavior matches
  31. the DatetimeIndex behavior.
  32. """
  33. freqstr = request.param
  34. # TODO: non-monotone indexes; NaTs, different start dates, timezones
  35. dti = pd.date_range(start=pd.Timestamp("2000-01-01"), periods=100, freq=freqstr)
  36. return dti
  37. @pytest.fixture
  38. def timedelta_index(request):
  39. """
  40. A fixture to provide TimedeltaIndex objects with different frequencies.
  41. Most TimedeltaArray behavior is already tested in TimedeltaIndex tests,
  42. so here we just test that the TimedeltaArray behavior matches
  43. the TimedeltaIndex behavior.
  44. """
  45. # TODO: flesh this out
  46. return pd.TimedeltaIndex(["1 Day", "3 Hours", "NaT"])
  47. class SharedTests:
  48. index_cls: Type[Union[DatetimeIndex, PeriodIndex, TimedeltaIndex]]
  49. def test_compare_len1_raises(self):
  50. # make sure we raise when comparing with different lengths, specific
  51. # to the case where one has length-1, which numpy would broadcast
  52. data = np.arange(10, dtype="i8") * 24 * 3600 * 10 ** 9
  53. idx = self.index_cls._simple_new(data, freq="D")
  54. arr = self.array_cls(idx)
  55. with pytest.raises(ValueError, match="Lengths must match"):
  56. arr == arr[:1]
  57. # test the index classes while we're at it, GH#23078
  58. with pytest.raises(ValueError, match="Lengths must match"):
  59. idx <= idx[[0]]
  60. def test_take(self):
  61. data = np.arange(100, dtype="i8") * 24 * 3600 * 10 ** 9
  62. np.random.shuffle(data)
  63. idx = self.index_cls._simple_new(data, freq="D")
  64. arr = self.array_cls(idx)
  65. takers = [1, 4, 94]
  66. result = arr.take(takers)
  67. expected = idx.take(takers)
  68. tm.assert_index_equal(self.index_cls(result), expected)
  69. takers = np.array([1, 4, 94])
  70. result = arr.take(takers)
  71. expected = idx.take(takers)
  72. tm.assert_index_equal(self.index_cls(result), expected)
  73. def test_take_fill(self):
  74. data = np.arange(10, dtype="i8") * 24 * 3600 * 10 ** 9
  75. idx = self.index_cls._simple_new(data, freq="D")
  76. arr = self.array_cls(idx)
  77. result = arr.take([-1, 1], allow_fill=True, fill_value=None)
  78. assert result[0] is pd.NaT
  79. result = arr.take([-1, 1], allow_fill=True, fill_value=np.nan)
  80. assert result[0] is pd.NaT
  81. result = arr.take([-1, 1], allow_fill=True, fill_value=pd.NaT)
  82. assert result[0] is pd.NaT
  83. with pytest.raises(ValueError):
  84. arr.take([0, 1], allow_fill=True, fill_value=2)
  85. with pytest.raises(ValueError):
  86. arr.take([0, 1], allow_fill=True, fill_value=2.0)
  87. with pytest.raises(ValueError):
  88. arr.take([0, 1], allow_fill=True, fill_value=pd.Timestamp.now().time)
  89. def test_concat_same_type(self):
  90. data = np.arange(10, dtype="i8") * 24 * 3600 * 10 ** 9
  91. idx = self.index_cls._simple_new(data, freq="D").insert(0, pd.NaT)
  92. arr = self.array_cls(idx)
  93. result = arr._concat_same_type([arr[:-1], arr[1:], arr])
  94. expected = idx._concat_same_dtype([idx[:-1], idx[1:], idx], None)
  95. tm.assert_index_equal(self.index_cls(result), expected)
  96. def test_unbox_scalar(self):
  97. data = np.arange(10, dtype="i8") * 24 * 3600 * 10 ** 9
  98. arr = self.array_cls(data, freq="D")
  99. result = arr._unbox_scalar(arr[0])
  100. assert isinstance(result, int)
  101. result = arr._unbox_scalar(pd.NaT)
  102. assert isinstance(result, int)
  103. with pytest.raises(ValueError):
  104. arr._unbox_scalar("foo")
  105. def test_check_compatible_with(self):
  106. data = np.arange(10, dtype="i8") * 24 * 3600 * 10 ** 9
  107. arr = self.array_cls(data, freq="D")
  108. arr._check_compatible_with(arr[0])
  109. arr._check_compatible_with(arr[:1])
  110. arr._check_compatible_with(pd.NaT)
  111. def test_scalar_from_string(self):
  112. data = np.arange(10, dtype="i8") * 24 * 3600 * 10 ** 9
  113. arr = self.array_cls(data, freq="D")
  114. result = arr._scalar_from_string(str(arr[0]))
  115. assert result == arr[0]
  116. def test_reduce_invalid(self):
  117. data = np.arange(10, dtype="i8") * 24 * 3600 * 10 ** 9
  118. arr = self.array_cls(data, freq="D")
  119. with pytest.raises(TypeError, match="cannot perform"):
  120. arr._reduce("not a method")
  121. @pytest.mark.parametrize("method", ["pad", "backfill"])
  122. def test_fillna_method_doesnt_change_orig(self, method):
  123. data = np.arange(10, dtype="i8") * 24 * 3600 * 10 ** 9
  124. arr = self.array_cls(data, freq="D")
  125. arr[4] = pd.NaT
  126. fill_value = arr[3] if method == "pad" else arr[5]
  127. result = arr.fillna(method=method)
  128. assert result[4] == fill_value
  129. # check that the original was not changed
  130. assert arr[4] is pd.NaT
  131. def test_searchsorted(self):
  132. data = np.arange(10, dtype="i8") * 24 * 3600 * 10 ** 9
  133. arr = self.array_cls(data, freq="D")
  134. # scalar
  135. result = arr.searchsorted(arr[1])
  136. assert result == 1
  137. result = arr.searchsorted(arr[2], side="right")
  138. assert result == 3
  139. # own-type
  140. result = arr.searchsorted(arr[1:3])
  141. expected = np.array([1, 2], dtype=np.intp)
  142. tm.assert_numpy_array_equal(result, expected)
  143. result = arr.searchsorted(arr[1:3], side="right")
  144. expected = np.array([2, 3], dtype=np.intp)
  145. tm.assert_numpy_array_equal(result, expected)
  146. # Following numpy convention, NaT goes at the beginning
  147. # (unlike NaN which goes at the end)
  148. result = arr.searchsorted(pd.NaT)
  149. assert result == 0
  150. def test_setitem(self):
  151. data = np.arange(10, dtype="i8") * 24 * 3600 * 10 ** 9
  152. arr = self.array_cls(data, freq="D")
  153. arr[0] = arr[1]
  154. expected = np.arange(10, dtype="i8") * 24 * 3600 * 10 ** 9
  155. expected[0] = expected[1]
  156. tm.assert_numpy_array_equal(arr.asi8, expected)
  157. arr[:2] = arr[-2:]
  158. expected[:2] = expected[-2:]
  159. tm.assert_numpy_array_equal(arr.asi8, expected)
  160. def test_setitem_raises(self):
  161. data = np.arange(10, dtype="i8") * 24 * 3600 * 10 ** 9
  162. arr = self.array_cls(data, freq="D")
  163. val = arr[0]
  164. with pytest.raises(IndexError, match="index 12 is out of bounds"):
  165. arr[12] = val
  166. with pytest.raises(TypeError, match="'value' should be a.* 'object'"):
  167. arr[0] = object()
  168. def test_inplace_arithmetic(self):
  169. # GH#24115 check that iadd and isub are actually in-place
  170. data = np.arange(10, dtype="i8") * 24 * 3600 * 10 ** 9
  171. arr = self.array_cls(data, freq="D")
  172. expected = arr + pd.Timedelta(days=1)
  173. arr += pd.Timedelta(days=1)
  174. tm.assert_equal(arr, expected)
  175. expected = arr - pd.Timedelta(days=1)
  176. arr -= pd.Timedelta(days=1)
  177. tm.assert_equal(arr, expected)
  178. def test_shift_fill_int_deprecated(self):
  179. # GH#31971
  180. data = np.arange(10, dtype="i8") * 24 * 3600 * 10 ** 9
  181. arr = self.array_cls(data, freq="D")
  182. with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
  183. result = arr.shift(1, fill_value=1)
  184. expected = arr.copy()
  185. if self.array_cls is PeriodArray:
  186. fill_val = PeriodArray._scalar_type._from_ordinal(1, freq=arr.freq)
  187. else:
  188. fill_val = arr._scalar_type(1)
  189. expected[0] = fill_val
  190. expected[1:] = arr[:-1]
  191. tm.assert_equal(result, expected)
  192. class TestDatetimeArray(SharedTests):
  193. index_cls = pd.DatetimeIndex
  194. array_cls = DatetimeArray
  195. def test_round(self, tz_naive_fixture):
  196. # GH#24064
  197. tz = tz_naive_fixture
  198. dti = pd.date_range("2016-01-01 01:01:00", periods=3, freq="H", tz=tz)
  199. result = dti.round(freq="2T")
  200. expected = dti - pd.Timedelta(minutes=1)
  201. tm.assert_index_equal(result, expected)
  202. def test_array_interface(self, datetime_index):
  203. arr = DatetimeArray(datetime_index)
  204. # default asarray gives the same underlying data (for tz naive)
  205. result = np.asarray(arr)
  206. expected = arr._data
  207. assert result is expected
  208. tm.assert_numpy_array_equal(result, expected)
  209. result = np.array(arr, copy=False)
  210. assert result is expected
  211. tm.assert_numpy_array_equal(result, expected)
  212. # specifying M8[ns] gives the same result as default
  213. result = np.asarray(arr, dtype="datetime64[ns]")
  214. expected = arr._data
  215. assert result is expected
  216. tm.assert_numpy_array_equal(result, expected)
  217. result = np.array(arr, dtype="datetime64[ns]", copy=False)
  218. assert result is expected
  219. tm.assert_numpy_array_equal(result, expected)
  220. result = np.array(arr, dtype="datetime64[ns]")
  221. assert result is not expected
  222. tm.assert_numpy_array_equal(result, expected)
  223. # to object dtype
  224. result = np.asarray(arr, dtype=object)
  225. expected = np.array(list(arr), dtype=object)
  226. tm.assert_numpy_array_equal(result, expected)
  227. # to other dtype always copies
  228. result = np.asarray(arr, dtype="int64")
  229. assert result is not arr.asi8
  230. assert not np.may_share_memory(arr, result)
  231. expected = arr.asi8.copy()
  232. tm.assert_numpy_array_equal(result, expected)
  233. # other dtypes handled by numpy
  234. for dtype in ["float64", str]:
  235. result = np.asarray(arr, dtype=dtype)
  236. expected = np.asarray(arr).astype(dtype)
  237. tm.assert_numpy_array_equal(result, expected)
  238. def test_array_object_dtype(self, tz_naive_fixture):
  239. # GH#23524
  240. tz = tz_naive_fixture
  241. dti = pd.date_range("2016-01-01", periods=3, tz=tz)
  242. arr = DatetimeArray(dti)
  243. expected = np.array(list(dti))
  244. result = np.array(arr, dtype=object)
  245. tm.assert_numpy_array_equal(result, expected)
  246. # also test the DatetimeIndex method while we're at it
  247. result = np.array(dti, dtype=object)
  248. tm.assert_numpy_array_equal(result, expected)
  249. def test_array_tz(self, tz_naive_fixture):
  250. # GH#23524
  251. tz = tz_naive_fixture
  252. dti = pd.date_range("2016-01-01", periods=3, tz=tz)
  253. arr = DatetimeArray(dti)
  254. expected = dti.asi8.view("M8[ns]")
  255. result = np.array(arr, dtype="M8[ns]")
  256. tm.assert_numpy_array_equal(result, expected)
  257. result = np.array(arr, dtype="datetime64[ns]")
  258. tm.assert_numpy_array_equal(result, expected)
  259. # check that we are not making copies when setting copy=False
  260. result = np.array(arr, dtype="M8[ns]", copy=False)
  261. assert result.base is expected.base
  262. assert result.base is not None
  263. result = np.array(arr, dtype="datetime64[ns]", copy=False)
  264. assert result.base is expected.base
  265. assert result.base is not None
  266. def test_array_i8_dtype(self, tz_naive_fixture):
  267. tz = tz_naive_fixture
  268. dti = pd.date_range("2016-01-01", periods=3, tz=tz)
  269. arr = DatetimeArray(dti)
  270. expected = dti.asi8
  271. result = np.array(arr, dtype="i8")
  272. tm.assert_numpy_array_equal(result, expected)
  273. result = np.array(arr, dtype=np.int64)
  274. tm.assert_numpy_array_equal(result, expected)
  275. # check that we are still making copies when setting copy=False
  276. result = np.array(arr, dtype="i8", copy=False)
  277. assert result.base is not expected.base
  278. assert result.base is None
  279. def test_from_array_keeps_base(self):
  280. # Ensure that DatetimeArray._data.base isn't lost.
  281. arr = np.array(["2000-01-01", "2000-01-02"], dtype="M8[ns]")
  282. dta = DatetimeArray(arr)
  283. assert dta._data is arr
  284. dta = DatetimeArray(arr[:0])
  285. assert dta._data.base is arr
  286. def test_from_dti(self, tz_naive_fixture):
  287. tz = tz_naive_fixture
  288. dti = pd.date_range("2016-01-01", periods=3, tz=tz)
  289. arr = DatetimeArray(dti)
  290. assert list(dti) == list(arr)
  291. # Check that Index.__new__ knows what to do with DatetimeArray
  292. dti2 = pd.Index(arr)
  293. assert isinstance(dti2, pd.DatetimeIndex)
  294. assert list(dti2) == list(arr)
  295. def test_astype_object(self, tz_naive_fixture):
  296. tz = tz_naive_fixture
  297. dti = pd.date_range("2016-01-01", periods=3, tz=tz)
  298. arr = DatetimeArray(dti)
  299. asobj = arr.astype("O")
  300. assert isinstance(asobj, np.ndarray)
  301. assert asobj.dtype == "O"
  302. assert list(asobj) == list(dti)
  303. @pytest.mark.parametrize("freqstr", ["D", "B", "W", "M", "Q", "Y"])
  304. def test_to_perioddelta(self, datetime_index, freqstr):
  305. # GH#23113
  306. dti = datetime_index
  307. arr = DatetimeArray(dti)
  308. expected = dti.to_perioddelta(freq=freqstr)
  309. result = arr.to_perioddelta(freq=freqstr)
  310. assert isinstance(result, TimedeltaArray)
  311. # placeholder until these become actual EA subclasses and we can use
  312. # an EA-specific tm.assert_ function
  313. tm.assert_index_equal(pd.Index(result), pd.Index(expected))
  314. @pytest.mark.parametrize("freqstr", ["D", "B", "W", "M", "Q", "Y"])
  315. def test_to_period(self, datetime_index, freqstr):
  316. dti = datetime_index
  317. arr = DatetimeArray(dti)
  318. expected = dti.to_period(freq=freqstr)
  319. result = arr.to_period(freq=freqstr)
  320. assert isinstance(result, PeriodArray)
  321. # placeholder until these become actual EA subclasses and we can use
  322. # an EA-specific tm.assert_ function
  323. tm.assert_index_equal(pd.Index(result), pd.Index(expected))
  324. @pytest.mark.parametrize("propname", pd.DatetimeIndex._bool_ops)
  325. def test_bool_properties(self, datetime_index, propname):
  326. # in this case _bool_ops is just `is_leap_year`
  327. dti = datetime_index
  328. arr = DatetimeArray(dti)
  329. assert dti.freq == arr.freq
  330. result = getattr(arr, propname)
  331. expected = np.array(getattr(dti, propname), dtype=result.dtype)
  332. tm.assert_numpy_array_equal(result, expected)
  333. @pytest.mark.parametrize("propname", pd.DatetimeIndex._field_ops)
  334. def test_int_properties(self, datetime_index, propname):
  335. dti = datetime_index
  336. arr = DatetimeArray(dti)
  337. result = getattr(arr, propname)
  338. expected = np.array(getattr(dti, propname), dtype=result.dtype)
  339. tm.assert_numpy_array_equal(result, expected)
  340. def test_take_fill_valid(self, datetime_index, tz_naive_fixture):
  341. dti = datetime_index.tz_localize(tz_naive_fixture)
  342. arr = DatetimeArray(dti)
  343. now = pd.Timestamp.now().tz_localize(dti.tz)
  344. result = arr.take([-1, 1], allow_fill=True, fill_value=now)
  345. assert result[0] == now
  346. with pytest.raises(ValueError):
  347. # fill_value Timedelta invalid
  348. arr.take([-1, 1], allow_fill=True, fill_value=now - now)
  349. with pytest.raises(ValueError):
  350. # fill_value Period invalid
  351. arr.take([-1, 1], allow_fill=True, fill_value=pd.Period("2014Q1"))
  352. tz = None if dti.tz is not None else "US/Eastern"
  353. now = pd.Timestamp.now().tz_localize(tz)
  354. with pytest.raises(TypeError):
  355. # Timestamp with mismatched tz-awareness
  356. arr.take([-1, 1], allow_fill=True, fill_value=now)
  357. with pytest.raises(ValueError):
  358. # require NaT, not iNaT, as it could be confused with an integer
  359. arr.take([-1, 1], allow_fill=True, fill_value=pd.NaT.value)
  360. def test_concat_same_type_invalid(self, datetime_index):
  361. # different timezones
  362. dti = datetime_index
  363. arr = DatetimeArray(dti)
  364. if arr.tz is None:
  365. other = arr.tz_localize("UTC")
  366. else:
  367. other = arr.tz_localize(None)
  368. with pytest.raises(AssertionError):
  369. arr._concat_same_type([arr, other])
  370. def test_concat_same_type_different_freq(self):
  371. # we *can* concatenate DTI with different freqs.
  372. a = DatetimeArray(pd.date_range("2000", periods=2, freq="D", tz="US/Central"))
  373. b = DatetimeArray(pd.date_range("2000", periods=2, freq="H", tz="US/Central"))
  374. result = DatetimeArray._concat_same_type([a, b])
  375. expected = DatetimeArray(
  376. pd.to_datetime(
  377. [
  378. "2000-01-01 00:00:00",
  379. "2000-01-02 00:00:00",
  380. "2000-01-01 00:00:00",
  381. "2000-01-01 01:00:00",
  382. ]
  383. ).tz_localize("US/Central")
  384. )
  385. tm.assert_datetime_array_equal(result, expected)
  386. def test_strftime(self, datetime_index):
  387. arr = DatetimeArray(datetime_index)
  388. result = arr.strftime("%Y %b")
  389. expected = np.array([ts.strftime("%Y %b") for ts in arr], dtype=object)
  390. tm.assert_numpy_array_equal(result, expected)
  391. def test_strftime_nat(self):
  392. # GH 29578
  393. arr = DatetimeArray(DatetimeIndex(["2019-01-01", pd.NaT]))
  394. result = arr.strftime("%Y-%m-%d")
  395. expected = np.array(["2019-01-01", np.nan], dtype=object)
  396. tm.assert_numpy_array_equal(result, expected)
  397. class TestTimedeltaArray(SharedTests):
  398. index_cls = pd.TimedeltaIndex
  399. array_cls = TimedeltaArray
  400. def test_from_tdi(self):
  401. tdi = pd.TimedeltaIndex(["1 Day", "3 Hours"])
  402. arr = TimedeltaArray(tdi)
  403. assert list(arr) == list(tdi)
  404. # Check that Index.__new__ knows what to do with TimedeltaArray
  405. tdi2 = pd.Index(arr)
  406. assert isinstance(tdi2, pd.TimedeltaIndex)
  407. assert list(tdi2) == list(arr)
  408. def test_astype_object(self):
  409. tdi = pd.TimedeltaIndex(["1 Day", "3 Hours"])
  410. arr = TimedeltaArray(tdi)
  411. asobj = arr.astype("O")
  412. assert isinstance(asobj, np.ndarray)
  413. assert asobj.dtype == "O"
  414. assert list(asobj) == list(tdi)
  415. def test_to_pytimedelta(self, timedelta_index):
  416. tdi = timedelta_index
  417. arr = TimedeltaArray(tdi)
  418. expected = tdi.to_pytimedelta()
  419. result = arr.to_pytimedelta()
  420. tm.assert_numpy_array_equal(result, expected)
  421. def test_total_seconds(self, timedelta_index):
  422. tdi = timedelta_index
  423. arr = TimedeltaArray(tdi)
  424. expected = tdi.total_seconds()
  425. result = arr.total_seconds()
  426. tm.assert_numpy_array_equal(result, expected.values)
  427. @pytest.mark.parametrize("propname", pd.TimedeltaIndex._field_ops)
  428. def test_int_properties(self, timedelta_index, propname):
  429. tdi = timedelta_index
  430. arr = TimedeltaArray(tdi)
  431. result = getattr(arr, propname)
  432. expected = np.array(getattr(tdi, propname), dtype=result.dtype)
  433. tm.assert_numpy_array_equal(result, expected)
  434. def test_array_interface(self, timedelta_index):
  435. arr = TimedeltaArray(timedelta_index)
  436. # default asarray gives the same underlying data
  437. result = np.asarray(arr)
  438. expected = arr._data
  439. assert result is expected
  440. tm.assert_numpy_array_equal(result, expected)
  441. result = np.array(arr, copy=False)
  442. assert result is expected
  443. tm.assert_numpy_array_equal(result, expected)
  444. # specifying m8[ns] gives the same result as default
  445. result = np.asarray(arr, dtype="timedelta64[ns]")
  446. expected = arr._data
  447. assert result is expected
  448. tm.assert_numpy_array_equal(result, expected)
  449. result = np.array(arr, dtype="timedelta64[ns]", copy=False)
  450. assert result is expected
  451. tm.assert_numpy_array_equal(result, expected)
  452. result = np.array(arr, dtype="timedelta64[ns]")
  453. assert result is not expected
  454. tm.assert_numpy_array_equal(result, expected)
  455. # to object dtype
  456. result = np.asarray(arr, dtype=object)
  457. expected = np.array(list(arr), dtype=object)
  458. tm.assert_numpy_array_equal(result, expected)
  459. # to other dtype always copies
  460. result = np.asarray(arr, dtype="int64")
  461. assert result is not arr.asi8
  462. assert not np.may_share_memory(arr, result)
  463. expected = arr.asi8.copy()
  464. tm.assert_numpy_array_equal(result, expected)
  465. # other dtypes handled by numpy
  466. for dtype in ["float64", str]:
  467. result = np.asarray(arr, dtype=dtype)
  468. expected = np.asarray(arr).astype(dtype)
  469. tm.assert_numpy_array_equal(result, expected)
  470. def test_take_fill_valid(self, timedelta_index):
  471. tdi = timedelta_index
  472. arr = TimedeltaArray(tdi)
  473. td1 = pd.Timedelta(days=1)
  474. result = arr.take([-1, 1], allow_fill=True, fill_value=td1)
  475. assert result[0] == td1
  476. now = pd.Timestamp.now()
  477. with pytest.raises(ValueError):
  478. # fill_value Timestamp invalid
  479. arr.take([0, 1], allow_fill=True, fill_value=now)
  480. with pytest.raises(ValueError):
  481. # fill_value Period invalid
  482. arr.take([0, 1], allow_fill=True, fill_value=now.to_period("D"))
  483. class TestPeriodArray(SharedTests):
  484. index_cls = pd.PeriodIndex
  485. array_cls = PeriodArray
  486. def test_from_pi(self, period_index):
  487. pi = period_index
  488. arr = PeriodArray(pi)
  489. assert list(arr) == list(pi)
  490. # Check that Index.__new__ knows what to do with PeriodArray
  491. pi2 = pd.Index(arr)
  492. assert isinstance(pi2, pd.PeriodIndex)
  493. assert list(pi2) == list(arr)
  494. def test_astype_object(self, period_index):
  495. pi = period_index
  496. arr = PeriodArray(pi)
  497. asobj = arr.astype("O")
  498. assert isinstance(asobj, np.ndarray)
  499. assert asobj.dtype == "O"
  500. assert list(asobj) == list(pi)
  501. @pytest.mark.parametrize("how", ["S", "E"])
  502. def test_to_timestamp(self, how, period_index):
  503. pi = period_index
  504. arr = PeriodArray(pi)
  505. expected = DatetimeArray(pi.to_timestamp(how=how))
  506. result = arr.to_timestamp(how=how)
  507. assert isinstance(result, DatetimeArray)
  508. # placeholder until these become actual EA subclasses and we can use
  509. # an EA-specific tm.assert_ function
  510. tm.assert_index_equal(pd.Index(result), pd.Index(expected))
  511. def test_to_timestamp_out_of_bounds(self):
  512. # GH#19643 previously overflowed silently
  513. pi = pd.period_range("1500", freq="Y", periods=3)
  514. with pytest.raises(OutOfBoundsDatetime):
  515. pi.to_timestamp()
  516. with pytest.raises(OutOfBoundsDatetime):
  517. pi._data.to_timestamp()
  518. @pytest.mark.parametrize("propname", PeriodArray._bool_ops)
  519. def test_bool_properties(self, period_index, propname):
  520. # in this case _bool_ops is just `is_leap_year`
  521. pi = period_index
  522. arr = PeriodArray(pi)
  523. result = getattr(arr, propname)
  524. expected = np.array(getattr(pi, propname))
  525. tm.assert_numpy_array_equal(result, expected)
  526. @pytest.mark.parametrize("propname", PeriodArray._field_ops)
  527. def test_int_properties(self, period_index, propname):
  528. pi = period_index
  529. arr = PeriodArray(pi)
  530. result = getattr(arr, propname)
  531. expected = np.array(getattr(pi, propname))
  532. tm.assert_numpy_array_equal(result, expected)
  533. def test_array_interface(self, period_index):
  534. arr = PeriodArray(period_index)
  535. # default asarray gives objects
  536. result = np.asarray(arr)
  537. expected = np.array(list(arr), dtype=object)
  538. tm.assert_numpy_array_equal(result, expected)
  539. # to object dtype (same as default)
  540. result = np.asarray(arr, dtype=object)
  541. tm.assert_numpy_array_equal(result, expected)
  542. # to other dtypes
  543. with pytest.raises(TypeError):
  544. np.asarray(arr, dtype="int64")
  545. with pytest.raises(TypeError):
  546. np.asarray(arr, dtype="float64")
  547. result = np.asarray(arr, dtype="S20")
  548. expected = np.asarray(arr).astype("S20")
  549. tm.assert_numpy_array_equal(result, expected)
  550. def test_strftime(self, period_index):
  551. arr = PeriodArray(period_index)
  552. result = arr.strftime("%Y")
  553. expected = np.array([per.strftime("%Y") for per in arr], dtype=object)
  554. tm.assert_numpy_array_equal(result, expected)
  555. def test_strftime_nat(self):
  556. # GH 29578
  557. arr = PeriodArray(PeriodIndex(["2019-01-01", pd.NaT], dtype="period[D]"))
  558. result = arr.strftime("%Y-%m-%d")
  559. expected = np.array(["2019-01-01", np.nan], dtype=object)
  560. tm.assert_numpy_array_equal(result, expected)
  561. @pytest.mark.parametrize(
  562. "array,casting_nats",
  563. [
  564. (
  565. pd.TimedeltaIndex(["1 Day", "3 Hours", "NaT"])._data,
  566. (pd.NaT, np.timedelta64("NaT", "ns")),
  567. ),
  568. (
  569. pd.date_range("2000-01-01", periods=3, freq="D")._data,
  570. (pd.NaT, np.datetime64("NaT", "ns")),
  571. ),
  572. (pd.period_range("2000-01-01", periods=3, freq="D")._data, (pd.NaT,)),
  573. ],
  574. ids=lambda x: type(x).__name__,
  575. )
  576. def test_casting_nat_setitem_array(array, casting_nats):
  577. expected = type(array)._from_sequence([pd.NaT, array[1], array[2]])
  578. for nat in casting_nats:
  579. arr = array.copy()
  580. arr[0] = nat
  581. tm.assert_equal(arr, expected)
  582. @pytest.mark.parametrize(
  583. "array,non_casting_nats",
  584. [
  585. (
  586. pd.TimedeltaIndex(["1 Day", "3 Hours", "NaT"])._data,
  587. (np.datetime64("NaT", "ns"), pd.NaT.value),
  588. ),
  589. (
  590. pd.date_range("2000-01-01", periods=3, freq="D")._data,
  591. (np.timedelta64("NaT", "ns"), pd.NaT.value),
  592. ),
  593. (
  594. pd.period_range("2000-01-01", periods=3, freq="D")._data,
  595. (np.datetime64("NaT", "ns"), np.timedelta64("NaT", "ns"), pd.NaT.value),
  596. ),
  597. ],
  598. ids=lambda x: type(x).__name__,
  599. )
  600. def test_invalid_nat_setitem_array(array, non_casting_nats):
  601. for nat in non_casting_nats:
  602. with pytest.raises(TypeError):
  603. array[0] = nat
  604. @pytest.mark.parametrize(
  605. "array",
  606. [
  607. pd.date_range("2000", periods=4).array,
  608. pd.timedelta_range("2000", periods=4).array,
  609. ],
  610. )
  611. def test_to_numpy_extra(array):
  612. if _np_version_under1p18:
  613. # np.isnan(NaT) raises, so use pandas'
  614. isnan = pd.isna
  615. else:
  616. isnan = np.isnan
  617. array[0] = pd.NaT
  618. original = array.copy()
  619. result = array.to_numpy()
  620. assert isnan(result[0])
  621. result = array.to_numpy(dtype="int64")
  622. assert result[0] == -9223372036854775808
  623. result = array.to_numpy(dtype="int64", na_value=0)
  624. assert result[0] == 0
  625. result = array.to_numpy(na_value=array[1].to_numpy())
  626. assert result[0] == result[1]
  627. result = array.to_numpy(na_value=array[1].to_numpy(copy=False))
  628. assert result[0] == result[1]
  629. tm.assert_equal(array, original)