test_datetimelike.py 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393
  1. from __future__ import annotations
  2. import array
  3. import re
  4. import numpy as np
  5. import pytest
  6. from pandas._libs import (
  7. NaT,
  8. OutOfBoundsDatetime,
  9. Timestamp,
  10. )
  11. import pandas.util._test_decorators as td
  12. import pandas as pd
  13. from pandas import (
  14. DatetimeIndex,
  15. Period,
  16. PeriodIndex,
  17. TimedeltaIndex,
  18. )
  19. import pandas._testing as tm
  20. from pandas.core.arrays import (
  21. DatetimeArray,
  22. PandasArray,
  23. PeriodArray,
  24. TimedeltaArray,
  25. )
  26. from pandas.core.arrays.datetimes import _sequence_to_dt64ns
  27. from pandas.core.arrays.timedeltas import sequence_to_td64ns
  28. # TODO: more freq variants
  29. @pytest.fixture(params=["D", "B", "W", "M", "Q", "Y"])
  30. def freqstr(request):
  31. """Fixture returning parametrized frequency in string format."""
  32. return request.param
  33. @pytest.fixture
  34. def period_index(freqstr):
  35. """
  36. A fixture to provide PeriodIndex objects with different frequencies.
  37. Most PeriodArray behavior is already tested in PeriodIndex tests,
  38. so here we just test that the PeriodArray behavior matches
  39. the PeriodIndex behavior.
  40. """
  41. # TODO: non-monotone indexes; NaTs, different start dates
  42. pi = pd.period_range(start=Timestamp("2000-01-01"), periods=100, freq=freqstr)
  43. return pi
  44. @pytest.fixture
  45. def datetime_index(freqstr):
  46. """
  47. A fixture to provide DatetimeIndex objects with different frequencies.
  48. Most DatetimeArray behavior is already tested in DatetimeIndex tests,
  49. so here we just test that the DatetimeArray behavior matches
  50. the DatetimeIndex behavior.
  51. """
  52. # TODO: non-monotone indexes; NaTs, different start dates, timezones
  53. dti = pd.date_range(start=Timestamp("2000-01-01"), periods=100, freq=freqstr)
  54. return dti
  55. @pytest.fixture
  56. def timedelta_index():
  57. """
  58. A fixture to provide TimedeltaIndex objects with different frequencies.
  59. Most TimedeltaArray behavior is already tested in TimedeltaIndex tests,
  60. so here we just test that the TimedeltaArray behavior matches
  61. the TimedeltaIndex behavior.
  62. """
  63. # TODO: flesh this out
  64. return TimedeltaIndex(["1 Day", "3 Hours", "NaT"])
  65. class SharedTests:
  66. index_cls: type[DatetimeIndex | PeriodIndex | TimedeltaIndex]
  67. @pytest.fixture
  68. def arr1d(self):
  69. """Fixture returning DatetimeArray with daily frequency."""
  70. data = np.arange(10, dtype="i8") * 24 * 3600 * 10**9
  71. arr = self.array_cls(data, freq="D")
  72. return arr
  73. def test_compare_len1_raises(self, arr1d):
  74. # make sure we raise when comparing with different lengths, specific
  75. # to the case where one has length-1, which numpy would broadcast
  76. arr = arr1d
  77. idx = self.index_cls(arr)
  78. with pytest.raises(ValueError, match="Lengths must match"):
  79. arr == arr[:1]
  80. # test the index classes while we're at it, GH#23078
  81. with pytest.raises(ValueError, match="Lengths must match"):
  82. idx <= idx[[0]]
  83. @pytest.mark.parametrize(
  84. "result",
  85. [
  86. pd.date_range("2020", periods=3),
  87. pd.date_range("2020", periods=3, tz="UTC"),
  88. pd.timedelta_range("0 days", periods=3),
  89. pd.period_range("2020Q1", periods=3, freq="Q"),
  90. ],
  91. )
  92. def test_compare_with_Categorical(self, result):
  93. expected = pd.Categorical(result)
  94. assert all(result == expected)
  95. assert not any(result != expected)
  96. @pytest.mark.parametrize("reverse", [True, False])
  97. @pytest.mark.parametrize("as_index", [True, False])
  98. def test_compare_categorical_dtype(self, arr1d, as_index, reverse, ordered):
  99. other = pd.Categorical(arr1d, ordered=ordered)
  100. if as_index:
  101. other = pd.CategoricalIndex(other)
  102. left, right = arr1d, other
  103. if reverse:
  104. left, right = right, left
  105. ones = np.ones(arr1d.shape, dtype=bool)
  106. zeros = ~ones
  107. result = left == right
  108. tm.assert_numpy_array_equal(result, ones)
  109. result = left != right
  110. tm.assert_numpy_array_equal(result, zeros)
  111. if not reverse and not as_index:
  112. # Otherwise Categorical raises TypeError bc it is not ordered
  113. # TODO: we should probably get the same behavior regardless?
  114. result = left < right
  115. tm.assert_numpy_array_equal(result, zeros)
  116. result = left <= right
  117. tm.assert_numpy_array_equal(result, ones)
  118. result = left > right
  119. tm.assert_numpy_array_equal(result, zeros)
  120. result = left >= right
  121. tm.assert_numpy_array_equal(result, ones)
  122. def test_take(self):
  123. data = np.arange(100, dtype="i8") * 24 * 3600 * 10**9
  124. np.random.shuffle(data)
  125. freq = None if self.array_cls is not PeriodArray else "D"
  126. arr = self.array_cls(data, freq=freq)
  127. idx = self.index_cls._simple_new(arr)
  128. takers = [1, 4, 94]
  129. result = arr.take(takers)
  130. expected = idx.take(takers)
  131. tm.assert_index_equal(self.index_cls(result), expected)
  132. takers = np.array([1, 4, 94])
  133. result = arr.take(takers)
  134. expected = idx.take(takers)
  135. tm.assert_index_equal(self.index_cls(result), expected)
  136. @pytest.mark.parametrize("fill_value", [2, 2.0, Timestamp(2021, 1, 1, 12).time])
  137. def test_take_fill_raises(self, fill_value):
  138. data = np.arange(10, dtype="i8") * 24 * 3600 * 10**9
  139. arr = self.array_cls(data, freq="D")
  140. msg = f"value should be a '{arr._scalar_type.__name__}' or 'NaT'. Got"
  141. with pytest.raises(TypeError, match=msg):
  142. arr.take([0, 1], allow_fill=True, fill_value=fill_value)
  143. def test_take_fill(self):
  144. data = np.arange(10, dtype="i8") * 24 * 3600 * 10**9
  145. arr = self.array_cls(data, freq="D")
  146. result = arr.take([-1, 1], allow_fill=True, fill_value=None)
  147. assert result[0] is NaT
  148. result = arr.take([-1, 1], allow_fill=True, fill_value=np.nan)
  149. assert result[0] is NaT
  150. result = arr.take([-1, 1], allow_fill=True, fill_value=NaT)
  151. assert result[0] is NaT
  152. def test_take_fill_str(self, arr1d):
  153. # Cast str fill_value matching other fill_value-taking methods
  154. result = arr1d.take([-1, 1], allow_fill=True, fill_value=str(arr1d[-1]))
  155. expected = arr1d[[-1, 1]]
  156. tm.assert_equal(result, expected)
  157. msg = f"value should be a '{arr1d._scalar_type.__name__}' or 'NaT'. Got"
  158. with pytest.raises(TypeError, match=msg):
  159. arr1d.take([-1, 1], allow_fill=True, fill_value="foo")
  160. def test_concat_same_type(self, arr1d):
  161. arr = arr1d
  162. idx = self.index_cls(arr)
  163. idx = idx.insert(0, NaT)
  164. arr = self.array_cls(idx)
  165. result = arr._concat_same_type([arr[:-1], arr[1:], arr])
  166. arr2 = arr.astype(object)
  167. expected = self.index_cls(np.concatenate([arr2[:-1], arr2[1:], arr2]), None)
  168. tm.assert_index_equal(self.index_cls(result), expected)
  169. def test_unbox_scalar(self):
  170. data = np.arange(10, dtype="i8") * 24 * 3600 * 10**9
  171. arr = self.array_cls(data, freq="D")
  172. result = arr._unbox_scalar(arr[0])
  173. expected = arr._ndarray.dtype.type
  174. assert isinstance(result, expected)
  175. result = arr._unbox_scalar(NaT)
  176. assert isinstance(result, expected)
  177. msg = f"'value' should be a {self.scalar_type.__name__}."
  178. with pytest.raises(ValueError, match=msg):
  179. arr._unbox_scalar("foo")
  180. def test_check_compatible_with(self):
  181. data = np.arange(10, dtype="i8") * 24 * 3600 * 10**9
  182. arr = self.array_cls(data, freq="D")
  183. arr._check_compatible_with(arr[0])
  184. arr._check_compatible_with(arr[:1])
  185. arr._check_compatible_with(NaT)
  186. def test_scalar_from_string(self):
  187. data = np.arange(10, dtype="i8") * 24 * 3600 * 10**9
  188. arr = self.array_cls(data, freq="D")
  189. result = arr._scalar_from_string(str(arr[0]))
  190. assert result == arr[0]
  191. def test_reduce_invalid(self):
  192. data = np.arange(10, dtype="i8") * 24 * 3600 * 10**9
  193. arr = self.array_cls(data, freq="D")
  194. msg = "does not support reduction 'not a method'"
  195. with pytest.raises(TypeError, match=msg):
  196. arr._reduce("not a method")
  197. @pytest.mark.parametrize("method", ["pad", "backfill"])
  198. def test_fillna_method_doesnt_change_orig(self, method):
  199. data = np.arange(10, dtype="i8") * 24 * 3600 * 10**9
  200. arr = self.array_cls(data, freq="D")
  201. arr[4] = NaT
  202. fill_value = arr[3] if method == "pad" else arr[5]
  203. result = arr.fillna(method=method)
  204. assert result[4] == fill_value
  205. # check that the original was not changed
  206. assert arr[4] is NaT
  207. def test_searchsorted(self):
  208. data = np.arange(10, dtype="i8") * 24 * 3600 * 10**9
  209. arr = self.array_cls(data, freq="D")
  210. # scalar
  211. result = arr.searchsorted(arr[1])
  212. assert result == 1
  213. result = arr.searchsorted(arr[2], side="right")
  214. assert result == 3
  215. # own-type
  216. result = arr.searchsorted(arr[1:3])
  217. expected = np.array([1, 2], dtype=np.intp)
  218. tm.assert_numpy_array_equal(result, expected)
  219. result = arr.searchsorted(arr[1:3], side="right")
  220. expected = np.array([2, 3], dtype=np.intp)
  221. tm.assert_numpy_array_equal(result, expected)
  222. # GH#29884 match numpy convention on whether NaT goes
  223. # at the end or the beginning
  224. result = arr.searchsorted(NaT)
  225. assert result == 10
  226. @pytest.mark.parametrize("box", [None, "index", "series"])
  227. def test_searchsorted_castable_strings(self, arr1d, box, string_storage):
  228. arr = arr1d
  229. if box is None:
  230. pass
  231. elif box == "index":
  232. # Test the equivalent Index.searchsorted method while we're here
  233. arr = self.index_cls(arr)
  234. else:
  235. # Test the equivalent Series.searchsorted method while we're here
  236. arr = pd.Series(arr)
  237. # scalar
  238. result = arr.searchsorted(str(arr[1]))
  239. assert result == 1
  240. result = arr.searchsorted(str(arr[2]), side="right")
  241. assert result == 3
  242. result = arr.searchsorted([str(x) for x in arr[1:3]])
  243. expected = np.array([1, 2], dtype=np.intp)
  244. tm.assert_numpy_array_equal(result, expected)
  245. with pytest.raises(
  246. TypeError,
  247. match=re.escape(
  248. f"value should be a '{arr1d._scalar_type.__name__}', 'NaT', "
  249. "or array of those. Got 'str' instead."
  250. ),
  251. ):
  252. arr.searchsorted("foo")
  253. arr_type = "StringArray" if string_storage == "python" else "ArrowStringArray"
  254. with pd.option_context("string_storage", string_storage):
  255. with pytest.raises(
  256. TypeError,
  257. match=re.escape(
  258. f"value should be a '{arr1d._scalar_type.__name__}', 'NaT', "
  259. f"or array of those. Got '{arr_type}' instead."
  260. ),
  261. ):
  262. arr.searchsorted([str(arr[1]), "baz"])
  263. def test_getitem_near_implementation_bounds(self):
  264. # We only check tz-naive for DTA bc the bounds are slightly different
  265. # for other tzs
  266. i8vals = np.asarray([NaT._value + n for n in range(1, 5)], dtype="i8")
  267. arr = self.array_cls(i8vals, freq="ns")
  268. arr[0] # should not raise OutOfBoundsDatetime
  269. index = pd.Index(arr)
  270. index[0] # should not raise OutOfBoundsDatetime
  271. ser = pd.Series(arr)
  272. ser[0] # should not raise OutOfBoundsDatetime
  273. def test_getitem_2d(self, arr1d):
  274. # 2d slicing on a 1D array
  275. expected = type(arr1d)(arr1d._ndarray[:, np.newaxis], dtype=arr1d.dtype)
  276. result = arr1d[:, np.newaxis]
  277. tm.assert_equal(result, expected)
  278. # Lookup on a 2D array
  279. arr2d = expected
  280. expected = type(arr2d)(arr2d._ndarray[:3, 0], dtype=arr2d.dtype)
  281. result = arr2d[:3, 0]
  282. tm.assert_equal(result, expected)
  283. # Scalar lookup
  284. result = arr2d[-1, 0]
  285. expected = arr1d[-1]
  286. assert result == expected
  287. def test_iter_2d(self, arr1d):
  288. data2d = arr1d._ndarray[:3, np.newaxis]
  289. arr2d = type(arr1d)._simple_new(data2d, dtype=arr1d.dtype)
  290. result = list(arr2d)
  291. assert len(result) == 3
  292. for x in result:
  293. assert isinstance(x, type(arr1d))
  294. assert x.ndim == 1
  295. assert x.dtype == arr1d.dtype
  296. def test_repr_2d(self, arr1d):
  297. data2d = arr1d._ndarray[:3, np.newaxis]
  298. arr2d = type(arr1d)._simple_new(data2d, dtype=arr1d.dtype)
  299. result = repr(arr2d)
  300. if isinstance(arr2d, TimedeltaArray):
  301. expected = (
  302. f"<{type(arr2d).__name__}>\n"
  303. "[\n"
  304. f"['{arr1d[0]._repr_base()}'],\n"
  305. f"['{arr1d[1]._repr_base()}'],\n"
  306. f"['{arr1d[2]._repr_base()}']\n"
  307. "]\n"
  308. f"Shape: (3, 1), dtype: {arr1d.dtype}"
  309. )
  310. else:
  311. expected = (
  312. f"<{type(arr2d).__name__}>\n"
  313. "[\n"
  314. f"['{arr1d[0]}'],\n"
  315. f"['{arr1d[1]}'],\n"
  316. f"['{arr1d[2]}']\n"
  317. "]\n"
  318. f"Shape: (3, 1), dtype: {arr1d.dtype}"
  319. )
  320. assert result == expected
  321. def test_setitem(self):
  322. data = np.arange(10, dtype="i8") * 24 * 3600 * 10**9
  323. arr = self.array_cls(data, freq="D")
  324. arr[0] = arr[1]
  325. expected = np.arange(10, dtype="i8") * 24 * 3600 * 10**9
  326. expected[0] = expected[1]
  327. tm.assert_numpy_array_equal(arr.asi8, expected)
  328. arr[:2] = arr[-2:]
  329. expected[:2] = expected[-2:]
  330. tm.assert_numpy_array_equal(arr.asi8, expected)
  331. @pytest.mark.parametrize(
  332. "box",
  333. [
  334. pd.Index,
  335. pd.Series,
  336. np.array,
  337. list,
  338. PandasArray,
  339. ],
  340. )
  341. def test_setitem_object_dtype(self, box, arr1d):
  342. expected = arr1d.copy()[::-1]
  343. if expected.dtype.kind in ["m", "M"]:
  344. expected = expected._with_freq(None)
  345. vals = expected
  346. if box is list:
  347. vals = list(vals)
  348. elif box is np.array:
  349. # if we do np.array(x).astype(object) then dt64 and td64 cast to ints
  350. vals = np.array(vals.astype(object))
  351. elif box is PandasArray:
  352. vals = box(np.asarray(vals, dtype=object))
  353. else:
  354. vals = box(vals).astype(object)
  355. arr1d[:] = vals
  356. tm.assert_equal(arr1d, expected)
  357. def test_setitem_strs(self, arr1d):
  358. # Check that we parse strs in both scalar and listlike
  359. # Setting list-like of strs
  360. expected = arr1d.copy()
  361. expected[[0, 1]] = arr1d[-2:]
  362. result = arr1d.copy()
  363. result[:2] = [str(x) for x in arr1d[-2:]]
  364. tm.assert_equal(result, expected)
  365. # Same thing but now for just a scalar str
  366. expected = arr1d.copy()
  367. expected[0] = arr1d[-1]
  368. result = arr1d.copy()
  369. result[0] = str(arr1d[-1])
  370. tm.assert_equal(result, expected)
  371. @pytest.mark.parametrize("as_index", [True, False])
  372. def test_setitem_categorical(self, arr1d, as_index):
  373. expected = arr1d.copy()[::-1]
  374. if not isinstance(expected, PeriodArray):
  375. expected = expected._with_freq(None)
  376. cat = pd.Categorical(arr1d)
  377. if as_index:
  378. cat = pd.CategoricalIndex(cat)
  379. arr1d[:] = cat[::-1]
  380. tm.assert_equal(arr1d, expected)
  381. def test_setitem_raises(self):
  382. data = np.arange(10, dtype="i8") * 24 * 3600 * 10**9
  383. arr = self.array_cls(data, freq="D")
  384. val = arr[0]
  385. with pytest.raises(IndexError, match="index 12 is out of bounds"):
  386. arr[12] = val
  387. with pytest.raises(TypeError, match="value should be a.* 'object'"):
  388. arr[0] = object()
  389. msg = "cannot set using a list-like indexer with a different length"
  390. with pytest.raises(ValueError, match=msg):
  391. # GH#36339
  392. arr[[]] = [arr[1]]
  393. msg = "cannot set using a slice indexer with a different length than"
  394. with pytest.raises(ValueError, match=msg):
  395. # GH#36339
  396. arr[1:1] = arr[:3]
  397. @pytest.mark.parametrize("box", [list, np.array, pd.Index, pd.Series])
  398. def test_setitem_numeric_raises(self, arr1d, box):
  399. # We dont case e.g. int64 to our own dtype for setitem
  400. msg = (
  401. f"value should be a '{arr1d._scalar_type.__name__}', "
  402. "'NaT', or array of those. Got"
  403. )
  404. with pytest.raises(TypeError, match=msg):
  405. arr1d[:2] = box([0, 1])
  406. with pytest.raises(TypeError, match=msg):
  407. arr1d[:2] = box([0.0, 1.0])
  408. def test_inplace_arithmetic(self):
  409. # GH#24115 check that iadd and isub are actually in-place
  410. data = np.arange(10, dtype="i8") * 24 * 3600 * 10**9
  411. arr = self.array_cls(data, freq="D")
  412. expected = arr + pd.Timedelta(days=1)
  413. arr += pd.Timedelta(days=1)
  414. tm.assert_equal(arr, expected)
  415. expected = arr - pd.Timedelta(days=1)
  416. arr -= pd.Timedelta(days=1)
  417. tm.assert_equal(arr, expected)
  418. def test_shift_fill_int_deprecated(self):
  419. # GH#31971, enforced in 2.0
  420. data = np.arange(10, dtype="i8") * 24 * 3600 * 10**9
  421. arr = self.array_cls(data, freq="D")
  422. with pytest.raises(TypeError, match="value should be a"):
  423. arr.shift(1, fill_value=1)
  424. def test_median(self, arr1d):
  425. arr = arr1d
  426. if len(arr) % 2 == 0:
  427. # make it easier to define `expected`
  428. arr = arr[:-1]
  429. expected = arr[len(arr) // 2]
  430. result = arr.median()
  431. assert type(result) is type(expected)
  432. assert result == expected
  433. arr[len(arr) // 2] = NaT
  434. if not isinstance(expected, Period):
  435. expected = arr[len(arr) // 2 - 1 : len(arr) // 2 + 2].mean()
  436. assert arr.median(skipna=False) is NaT
  437. result = arr.median()
  438. assert type(result) is type(expected)
  439. assert result == expected
  440. assert arr[:0].median() is NaT
  441. assert arr[:0].median(skipna=False) is NaT
  442. # 2d Case
  443. arr2 = arr.reshape(-1, 1)
  444. result = arr2.median(axis=None)
  445. assert type(result) is type(expected)
  446. assert result == expected
  447. assert arr2.median(axis=None, skipna=False) is NaT
  448. result = arr2.median(axis=0)
  449. expected2 = type(arr)._from_sequence([expected], dtype=arr.dtype)
  450. tm.assert_equal(result, expected2)
  451. result = arr2.median(axis=0, skipna=False)
  452. expected2 = type(arr)._from_sequence([NaT], dtype=arr.dtype)
  453. tm.assert_equal(result, expected2)
  454. result = arr2.median(axis=1)
  455. tm.assert_equal(result, arr)
  456. result = arr2.median(axis=1, skipna=False)
  457. tm.assert_equal(result, arr)
  458. def test_from_integer_array(self):
  459. arr = np.array([1, 2, 3], dtype=np.int64)
  460. expected = self.array_cls(arr, dtype=self.example_dtype)
  461. data = pd.array(arr, dtype="Int64")
  462. result = self.array_cls(data, dtype=self.example_dtype)
  463. tm.assert_extension_array_equal(result, expected)
  464. class TestDatetimeArray(SharedTests):
  465. index_cls = DatetimeIndex
  466. array_cls = DatetimeArray
  467. scalar_type = Timestamp
  468. example_dtype = "M8[ns]"
  469. @pytest.fixture
  470. def arr1d(self, tz_naive_fixture, freqstr):
  471. """
  472. Fixture returning DatetimeArray with parametrized frequency and
  473. timezones
  474. """
  475. tz = tz_naive_fixture
  476. dti = pd.date_range("2016-01-01 01:01:00", periods=5, freq=freqstr, tz=tz)
  477. dta = dti._data
  478. return dta
  479. def test_round(self, arr1d):
  480. # GH#24064
  481. dti = self.index_cls(arr1d)
  482. result = dti.round(freq="2T")
  483. expected = dti - pd.Timedelta(minutes=1)
  484. expected = expected._with_freq(None)
  485. tm.assert_index_equal(result, expected)
  486. dta = dti._data
  487. result = dta.round(freq="2T")
  488. expected = expected._data._with_freq(None)
  489. tm.assert_datetime_array_equal(result, expected)
  490. def test_array_interface(self, datetime_index):
  491. arr = DatetimeArray(datetime_index)
  492. # default asarray gives the same underlying data (for tz naive)
  493. result = np.asarray(arr)
  494. expected = arr._ndarray
  495. assert result is expected
  496. tm.assert_numpy_array_equal(result, expected)
  497. result = np.array(arr, copy=False)
  498. assert result is expected
  499. tm.assert_numpy_array_equal(result, expected)
  500. # specifying M8[ns] gives the same result as default
  501. result = np.asarray(arr, dtype="datetime64[ns]")
  502. expected = arr._ndarray
  503. assert result is expected
  504. tm.assert_numpy_array_equal(result, expected)
  505. result = np.array(arr, dtype="datetime64[ns]", copy=False)
  506. assert result is expected
  507. tm.assert_numpy_array_equal(result, expected)
  508. result = np.array(arr, dtype="datetime64[ns]")
  509. assert result is not expected
  510. tm.assert_numpy_array_equal(result, expected)
  511. # to object dtype
  512. result = np.asarray(arr, dtype=object)
  513. expected = np.array(list(arr), dtype=object)
  514. tm.assert_numpy_array_equal(result, expected)
  515. # to other dtype always copies
  516. result = np.asarray(arr, dtype="int64")
  517. assert result is not arr.asi8
  518. assert not np.may_share_memory(arr, result)
  519. expected = arr.asi8.copy()
  520. tm.assert_numpy_array_equal(result, expected)
  521. # other dtypes handled by numpy
  522. for dtype in ["float64", str]:
  523. result = np.asarray(arr, dtype=dtype)
  524. expected = np.asarray(arr).astype(dtype)
  525. tm.assert_numpy_array_equal(result, expected)
  526. def test_array_object_dtype(self, arr1d):
  527. # GH#23524
  528. arr = arr1d
  529. dti = self.index_cls(arr1d)
  530. expected = np.array(list(dti))
  531. result = np.array(arr, dtype=object)
  532. tm.assert_numpy_array_equal(result, expected)
  533. # also test the DatetimeIndex method while we're at it
  534. result = np.array(dti, dtype=object)
  535. tm.assert_numpy_array_equal(result, expected)
  536. def test_array_tz(self, arr1d):
  537. # GH#23524
  538. arr = arr1d
  539. dti = self.index_cls(arr1d)
  540. expected = dti.asi8.view("M8[ns]")
  541. result = np.array(arr, dtype="M8[ns]")
  542. tm.assert_numpy_array_equal(result, expected)
  543. result = np.array(arr, dtype="datetime64[ns]")
  544. tm.assert_numpy_array_equal(result, expected)
  545. # check that we are not making copies when setting copy=False
  546. result = np.array(arr, dtype="M8[ns]", copy=False)
  547. assert result.base is expected.base
  548. assert result.base is not None
  549. result = np.array(arr, dtype="datetime64[ns]", copy=False)
  550. assert result.base is expected.base
  551. assert result.base is not None
  552. def test_array_i8_dtype(self, arr1d):
  553. arr = arr1d
  554. dti = self.index_cls(arr1d)
  555. expected = dti.asi8
  556. result = np.array(arr, dtype="i8")
  557. tm.assert_numpy_array_equal(result, expected)
  558. result = np.array(arr, dtype=np.int64)
  559. tm.assert_numpy_array_equal(result, expected)
  560. # check that we are still making copies when setting copy=False
  561. result = np.array(arr, dtype="i8", copy=False)
  562. assert result.base is not expected.base
  563. assert result.base is None
  564. def test_from_array_keeps_base(self):
  565. # Ensure that DatetimeArray._ndarray.base isn't lost.
  566. arr = np.array(["2000-01-01", "2000-01-02"], dtype="M8[ns]")
  567. dta = DatetimeArray(arr)
  568. assert dta._ndarray is arr
  569. dta = DatetimeArray(arr[:0])
  570. assert dta._ndarray.base is arr
  571. def test_from_dti(self, arr1d):
  572. arr = arr1d
  573. dti = self.index_cls(arr1d)
  574. assert list(dti) == list(arr)
  575. # Check that Index.__new__ knows what to do with DatetimeArray
  576. dti2 = pd.Index(arr)
  577. assert isinstance(dti2, DatetimeIndex)
  578. assert list(dti2) == list(arr)
  579. def test_astype_object(self, arr1d):
  580. arr = arr1d
  581. dti = self.index_cls(arr1d)
  582. asobj = arr.astype("O")
  583. assert isinstance(asobj, np.ndarray)
  584. assert asobj.dtype == "O"
  585. assert list(asobj) == list(dti)
  586. def test_to_period(self, datetime_index, freqstr):
  587. dti = datetime_index
  588. arr = DatetimeArray(dti)
  589. expected = dti.to_period(freq=freqstr)
  590. result = arr.to_period(freq=freqstr)
  591. assert isinstance(result, PeriodArray)
  592. # placeholder until these become actual EA subclasses and we can use
  593. # an EA-specific tm.assert_ function
  594. tm.assert_index_equal(pd.Index(result), pd.Index(expected))
  595. def test_to_period_2d(self, arr1d):
  596. arr2d = arr1d.reshape(1, -1)
  597. warn = None if arr1d.tz is None else UserWarning
  598. with tm.assert_produces_warning(warn):
  599. result = arr2d.to_period("D")
  600. expected = arr1d.to_period("D").reshape(1, -1)
  601. tm.assert_period_array_equal(result, expected)
  602. @pytest.mark.parametrize("propname", DatetimeArray._bool_ops)
  603. def test_bool_properties(self, arr1d, propname):
  604. # in this case _bool_ops is just `is_leap_year`
  605. dti = self.index_cls(arr1d)
  606. arr = arr1d
  607. assert dti.freq == arr.freq
  608. result = getattr(arr, propname)
  609. expected = np.array(getattr(dti, propname), dtype=result.dtype)
  610. tm.assert_numpy_array_equal(result, expected)
  611. @pytest.mark.parametrize("propname", DatetimeArray._field_ops)
  612. def test_int_properties(self, arr1d, propname):
  613. dti = self.index_cls(arr1d)
  614. arr = arr1d
  615. result = getattr(arr, propname)
  616. expected = np.array(getattr(dti, propname), dtype=result.dtype)
  617. tm.assert_numpy_array_equal(result, expected)
  618. def test_take_fill_valid(self, arr1d, fixed_now_ts):
  619. arr = arr1d
  620. dti = self.index_cls(arr1d)
  621. now = fixed_now_ts.tz_localize(dti.tz)
  622. result = arr.take([-1, 1], allow_fill=True, fill_value=now)
  623. assert result[0] == now
  624. msg = f"value should be a '{arr1d._scalar_type.__name__}' or 'NaT'. Got"
  625. with pytest.raises(TypeError, match=msg):
  626. # fill_value Timedelta invalid
  627. arr.take([-1, 1], allow_fill=True, fill_value=now - now)
  628. with pytest.raises(TypeError, match=msg):
  629. # fill_value Period invalid
  630. arr.take([-1, 1], allow_fill=True, fill_value=Period("2014Q1"))
  631. tz = None if dti.tz is not None else "US/Eastern"
  632. now = fixed_now_ts.tz_localize(tz)
  633. msg = "Cannot compare tz-naive and tz-aware datetime-like objects"
  634. with pytest.raises(TypeError, match=msg):
  635. # Timestamp with mismatched tz-awareness
  636. arr.take([-1, 1], allow_fill=True, fill_value=now)
  637. value = NaT._value
  638. msg = f"value should be a '{arr1d._scalar_type.__name__}' or 'NaT'. Got"
  639. with pytest.raises(TypeError, match=msg):
  640. # require NaT, not iNaT, as it could be confused with an integer
  641. arr.take([-1, 1], allow_fill=True, fill_value=value)
  642. value = np.timedelta64("NaT", "ns")
  643. with pytest.raises(TypeError, match=msg):
  644. # require appropriate-dtype if we have a NA value
  645. arr.take([-1, 1], allow_fill=True, fill_value=value)
  646. if arr.tz is not None:
  647. # GH#37356
  648. # Assuming here that arr1d fixture does not include Australia/Melbourne
  649. value = fixed_now_ts.tz_localize("Australia/Melbourne")
  650. result = arr.take([-1, 1], allow_fill=True, fill_value=value)
  651. expected = arr.take(
  652. [-1, 1],
  653. allow_fill=True,
  654. fill_value=value.tz_convert(arr.dtype.tz),
  655. )
  656. tm.assert_equal(result, expected)
  657. def test_concat_same_type_invalid(self, arr1d):
  658. # different timezones
  659. arr = arr1d
  660. if arr.tz is None:
  661. other = arr.tz_localize("UTC")
  662. else:
  663. other = arr.tz_localize(None)
  664. with pytest.raises(ValueError, match="to_concat must have the same"):
  665. arr._concat_same_type([arr, other])
  666. def test_concat_same_type_different_freq(self):
  667. # we *can* concatenate DTI with different freqs.
  668. a = DatetimeArray(pd.date_range("2000", periods=2, freq="D", tz="US/Central"))
  669. b = DatetimeArray(pd.date_range("2000", periods=2, freq="H", tz="US/Central"))
  670. result = DatetimeArray._concat_same_type([a, b])
  671. expected = DatetimeArray(
  672. pd.to_datetime(
  673. [
  674. "2000-01-01 00:00:00",
  675. "2000-01-02 00:00:00",
  676. "2000-01-01 00:00:00",
  677. "2000-01-01 01:00:00",
  678. ]
  679. ).tz_localize("US/Central")
  680. )
  681. tm.assert_datetime_array_equal(result, expected)
  682. def test_strftime(self, arr1d):
  683. arr = arr1d
  684. result = arr.strftime("%Y %b")
  685. expected = np.array([ts.strftime("%Y %b") for ts in arr], dtype=object)
  686. tm.assert_numpy_array_equal(result, expected)
  687. def test_strftime_nat(self):
  688. # GH 29578
  689. arr = DatetimeArray(DatetimeIndex(["2019-01-01", NaT]))
  690. result = arr.strftime("%Y-%m-%d")
  691. expected = np.array(["2019-01-01", np.nan], dtype=object)
  692. tm.assert_numpy_array_equal(result, expected)
  693. class TestTimedeltaArray(SharedTests):
  694. index_cls = TimedeltaIndex
  695. array_cls = TimedeltaArray
  696. scalar_type = pd.Timedelta
  697. example_dtype = "m8[ns]"
  698. def test_from_tdi(self):
  699. tdi = TimedeltaIndex(["1 Day", "3 Hours"])
  700. arr = TimedeltaArray(tdi)
  701. assert list(arr) == list(tdi)
  702. # Check that Index.__new__ knows what to do with TimedeltaArray
  703. tdi2 = pd.Index(arr)
  704. assert isinstance(tdi2, TimedeltaIndex)
  705. assert list(tdi2) == list(arr)
  706. def test_astype_object(self):
  707. tdi = TimedeltaIndex(["1 Day", "3 Hours"])
  708. arr = TimedeltaArray(tdi)
  709. asobj = arr.astype("O")
  710. assert isinstance(asobj, np.ndarray)
  711. assert asobj.dtype == "O"
  712. assert list(asobj) == list(tdi)
  713. def test_to_pytimedelta(self, timedelta_index):
  714. tdi = timedelta_index
  715. arr = TimedeltaArray(tdi)
  716. expected = tdi.to_pytimedelta()
  717. result = arr.to_pytimedelta()
  718. tm.assert_numpy_array_equal(result, expected)
  719. def test_total_seconds(self, timedelta_index):
  720. tdi = timedelta_index
  721. arr = TimedeltaArray(tdi)
  722. expected = tdi.total_seconds()
  723. result = arr.total_seconds()
  724. tm.assert_numpy_array_equal(result, expected.values)
  725. @pytest.mark.parametrize("propname", TimedeltaArray._field_ops)
  726. def test_int_properties(self, timedelta_index, propname):
  727. tdi = timedelta_index
  728. arr = TimedeltaArray(tdi)
  729. result = getattr(arr, propname)
  730. expected = np.array(getattr(tdi, propname), dtype=result.dtype)
  731. tm.assert_numpy_array_equal(result, expected)
  732. def test_array_interface(self, timedelta_index):
  733. arr = TimedeltaArray(timedelta_index)
  734. # default asarray gives the same underlying data
  735. result = np.asarray(arr)
  736. expected = arr._ndarray
  737. assert result is expected
  738. tm.assert_numpy_array_equal(result, expected)
  739. result = np.array(arr, copy=False)
  740. assert result is expected
  741. tm.assert_numpy_array_equal(result, expected)
  742. # specifying m8[ns] gives the same result as default
  743. result = np.asarray(arr, dtype="timedelta64[ns]")
  744. expected = arr._ndarray
  745. assert result is expected
  746. tm.assert_numpy_array_equal(result, expected)
  747. result = np.array(arr, dtype="timedelta64[ns]", copy=False)
  748. assert result is expected
  749. tm.assert_numpy_array_equal(result, expected)
  750. result = np.array(arr, dtype="timedelta64[ns]")
  751. assert result is not expected
  752. tm.assert_numpy_array_equal(result, expected)
  753. # to object dtype
  754. result = np.asarray(arr, dtype=object)
  755. expected = np.array(list(arr), dtype=object)
  756. tm.assert_numpy_array_equal(result, expected)
  757. # to other dtype always copies
  758. result = np.asarray(arr, dtype="int64")
  759. assert result is not arr.asi8
  760. assert not np.may_share_memory(arr, result)
  761. expected = arr.asi8.copy()
  762. tm.assert_numpy_array_equal(result, expected)
  763. # other dtypes handled by numpy
  764. for dtype in ["float64", str]:
  765. result = np.asarray(arr, dtype=dtype)
  766. expected = np.asarray(arr).astype(dtype)
  767. tm.assert_numpy_array_equal(result, expected)
  768. def test_take_fill_valid(self, timedelta_index, fixed_now_ts):
  769. tdi = timedelta_index
  770. arr = TimedeltaArray(tdi)
  771. td1 = pd.Timedelta(days=1)
  772. result = arr.take([-1, 1], allow_fill=True, fill_value=td1)
  773. assert result[0] == td1
  774. value = fixed_now_ts
  775. msg = f"value should be a '{arr._scalar_type.__name__}' or 'NaT'. Got"
  776. with pytest.raises(TypeError, match=msg):
  777. # fill_value Timestamp invalid
  778. arr.take([0, 1], allow_fill=True, fill_value=value)
  779. value = fixed_now_ts.to_period("D")
  780. with pytest.raises(TypeError, match=msg):
  781. # fill_value Period invalid
  782. arr.take([0, 1], allow_fill=True, fill_value=value)
  783. value = np.datetime64("NaT", "ns")
  784. with pytest.raises(TypeError, match=msg):
  785. # require appropriate-dtype if we have a NA value
  786. arr.take([-1, 1], allow_fill=True, fill_value=value)
  787. class TestPeriodArray(SharedTests):
  788. index_cls = PeriodIndex
  789. array_cls = PeriodArray
  790. scalar_type = Period
  791. example_dtype = PeriodIndex([], freq="W").dtype
  792. @pytest.fixture
  793. def arr1d(self, period_index):
  794. """
  795. Fixture returning DatetimeArray from parametrized PeriodIndex objects
  796. """
  797. return period_index._data
  798. def test_from_pi(self, arr1d):
  799. pi = self.index_cls(arr1d)
  800. arr = arr1d
  801. assert list(arr) == list(pi)
  802. # Check that Index.__new__ knows what to do with PeriodArray
  803. pi2 = pd.Index(arr)
  804. assert isinstance(pi2, PeriodIndex)
  805. assert list(pi2) == list(arr)
  806. def test_astype_object(self, arr1d):
  807. pi = self.index_cls(arr1d)
  808. arr = arr1d
  809. asobj = arr.astype("O")
  810. assert isinstance(asobj, np.ndarray)
  811. assert asobj.dtype == "O"
  812. assert list(asobj) == list(pi)
  813. def test_take_fill_valid(self, arr1d):
  814. arr = arr1d
  815. value = NaT._value
  816. msg = f"value should be a '{arr1d._scalar_type.__name__}' or 'NaT'. Got"
  817. with pytest.raises(TypeError, match=msg):
  818. # require NaT, not iNaT, as it could be confused with an integer
  819. arr.take([-1, 1], allow_fill=True, fill_value=value)
  820. value = np.timedelta64("NaT", "ns")
  821. with pytest.raises(TypeError, match=msg):
  822. # require appropriate-dtype if we have a NA value
  823. arr.take([-1, 1], allow_fill=True, fill_value=value)
  824. @pytest.mark.parametrize("how", ["S", "E"])
  825. def test_to_timestamp(self, how, arr1d):
  826. pi = self.index_cls(arr1d)
  827. arr = arr1d
  828. expected = DatetimeArray(pi.to_timestamp(how=how))
  829. result = arr.to_timestamp(how=how)
  830. assert isinstance(result, DatetimeArray)
  831. # placeholder until these become actual EA subclasses and we can use
  832. # an EA-specific tm.assert_ function
  833. tm.assert_index_equal(pd.Index(result), pd.Index(expected))
  834. def test_to_timestamp_roundtrip_bday(self):
  835. # Case where infer_freq inside would choose "D" instead of "B"
  836. dta = pd.date_range("2021-10-18", periods=3, freq="B")._data
  837. parr = dta.to_period()
  838. result = parr.to_timestamp()
  839. assert result.freq == "B"
  840. tm.assert_extension_array_equal(result, dta)
  841. dta2 = dta[::2]
  842. parr2 = dta2.to_period()
  843. result2 = parr2.to_timestamp()
  844. assert result2.freq == "2B"
  845. tm.assert_extension_array_equal(result2, dta2)
  846. parr3 = dta.to_period("2B")
  847. result3 = parr3.to_timestamp()
  848. assert result3.freq == "B"
  849. tm.assert_extension_array_equal(result3, dta)
  850. def test_to_timestamp_out_of_bounds(self):
  851. # GH#19643 previously overflowed silently
  852. pi = pd.period_range("1500", freq="Y", periods=3)
  853. msg = "Out of bounds nanosecond timestamp: 1500-01-01 00:00:00"
  854. with pytest.raises(OutOfBoundsDatetime, match=msg):
  855. pi.to_timestamp()
  856. with pytest.raises(OutOfBoundsDatetime, match=msg):
  857. pi._data.to_timestamp()
  858. @pytest.mark.parametrize("propname", PeriodArray._bool_ops)
  859. def test_bool_properties(self, arr1d, propname):
  860. # in this case _bool_ops is just `is_leap_year`
  861. pi = self.index_cls(arr1d)
  862. arr = arr1d
  863. result = getattr(arr, propname)
  864. expected = np.array(getattr(pi, propname))
  865. tm.assert_numpy_array_equal(result, expected)
  866. @pytest.mark.parametrize("propname", PeriodArray._field_ops)
  867. def test_int_properties(self, arr1d, propname):
  868. pi = self.index_cls(arr1d)
  869. arr = arr1d
  870. result = getattr(arr, propname)
  871. expected = np.array(getattr(pi, propname))
  872. tm.assert_numpy_array_equal(result, expected)
  873. def test_array_interface(self, arr1d):
  874. arr = arr1d
  875. # default asarray gives objects
  876. result = np.asarray(arr)
  877. expected = np.array(list(arr), dtype=object)
  878. tm.assert_numpy_array_equal(result, expected)
  879. # to object dtype (same as default)
  880. result = np.asarray(arr, dtype=object)
  881. tm.assert_numpy_array_equal(result, expected)
  882. result = np.asarray(arr, dtype="int64")
  883. tm.assert_numpy_array_equal(result, arr.asi8)
  884. # to other dtypes
  885. msg = r"float\(\) argument must be a string or a( real)? number, not 'Period'"
  886. with pytest.raises(TypeError, match=msg):
  887. np.asarray(arr, dtype="float64")
  888. result = np.asarray(arr, dtype="S20")
  889. expected = np.asarray(arr).astype("S20")
  890. tm.assert_numpy_array_equal(result, expected)
  891. def test_strftime(self, arr1d):
  892. arr = arr1d
  893. result = arr.strftime("%Y")
  894. expected = np.array([per.strftime("%Y") for per in arr], dtype=object)
  895. tm.assert_numpy_array_equal(result, expected)
  896. def test_strftime_nat(self):
  897. # GH 29578
  898. arr = PeriodArray(PeriodIndex(["2019-01-01", NaT], dtype="period[D]"))
  899. result = arr.strftime("%Y-%m-%d")
  900. expected = np.array(["2019-01-01", np.nan], dtype=object)
  901. tm.assert_numpy_array_equal(result, expected)
  902. @pytest.mark.parametrize(
  903. "arr,casting_nats",
  904. [
  905. (
  906. TimedeltaIndex(["1 Day", "3 Hours", "NaT"])._data,
  907. (NaT, np.timedelta64("NaT", "ns")),
  908. ),
  909. (
  910. pd.date_range("2000-01-01", periods=3, freq="D")._data,
  911. (NaT, np.datetime64("NaT", "ns")),
  912. ),
  913. (pd.period_range("2000-01-01", periods=3, freq="D")._data, (NaT,)),
  914. ],
  915. ids=lambda x: type(x).__name__,
  916. )
  917. def test_casting_nat_setitem_array(arr, casting_nats):
  918. expected = type(arr)._from_sequence([NaT, arr[1], arr[2]])
  919. for nat in casting_nats:
  920. arr = arr.copy()
  921. arr[0] = nat
  922. tm.assert_equal(arr, expected)
  923. @pytest.mark.parametrize(
  924. "arr,non_casting_nats",
  925. [
  926. (
  927. TimedeltaIndex(["1 Day", "3 Hours", "NaT"])._data,
  928. (np.datetime64("NaT", "ns"), NaT._value),
  929. ),
  930. (
  931. pd.date_range("2000-01-01", periods=3, freq="D")._data,
  932. (np.timedelta64("NaT", "ns"), NaT._value),
  933. ),
  934. (
  935. pd.period_range("2000-01-01", periods=3, freq="D")._data,
  936. (np.datetime64("NaT", "ns"), np.timedelta64("NaT", "ns"), NaT._value),
  937. ),
  938. ],
  939. ids=lambda x: type(x).__name__,
  940. )
  941. def test_invalid_nat_setitem_array(arr, non_casting_nats):
  942. msg = (
  943. "value should be a '(Timestamp|Timedelta|Period)', 'NaT', or array of those. "
  944. "Got '(timedelta64|datetime64|int)' instead."
  945. )
  946. for nat in non_casting_nats:
  947. with pytest.raises(TypeError, match=msg):
  948. arr[0] = nat
  949. @pytest.mark.parametrize(
  950. "arr",
  951. [
  952. pd.date_range("2000", periods=4).array,
  953. pd.timedelta_range("2000", periods=4).array,
  954. ],
  955. )
  956. def test_to_numpy_extra(arr):
  957. arr[0] = NaT
  958. original = arr.copy()
  959. result = arr.to_numpy()
  960. assert np.isnan(result[0])
  961. result = arr.to_numpy(dtype="int64")
  962. assert result[0] == -9223372036854775808
  963. result = arr.to_numpy(dtype="int64", na_value=0)
  964. assert result[0] == 0
  965. result = arr.to_numpy(na_value=arr[1].to_numpy())
  966. assert result[0] == result[1]
  967. result = arr.to_numpy(na_value=arr[1].to_numpy(copy=False))
  968. assert result[0] == result[1]
  969. tm.assert_equal(arr, original)
  970. @pytest.mark.parametrize("as_index", [True, False])
  971. @pytest.mark.parametrize(
  972. "values",
  973. [
  974. pd.to_datetime(["2020-01-01", "2020-02-01"]),
  975. TimedeltaIndex([1, 2], unit="D"),
  976. PeriodIndex(["2020-01-01", "2020-02-01"], freq="D"),
  977. ],
  978. )
  979. @pytest.mark.parametrize(
  980. "klass",
  981. [
  982. list,
  983. np.array,
  984. pd.array,
  985. pd.Series,
  986. pd.Index,
  987. pd.Categorical,
  988. pd.CategoricalIndex,
  989. ],
  990. )
  991. def test_searchsorted_datetimelike_with_listlike(values, klass, as_index):
  992. # https://github.com/pandas-dev/pandas/issues/32762
  993. if not as_index:
  994. values = values._data
  995. result = values.searchsorted(klass(values))
  996. expected = np.array([0, 1], dtype=result.dtype)
  997. tm.assert_numpy_array_equal(result, expected)
  998. @pytest.mark.parametrize(
  999. "values",
  1000. [
  1001. pd.to_datetime(["2020-01-01", "2020-02-01"]),
  1002. TimedeltaIndex([1, 2], unit="D"),
  1003. PeriodIndex(["2020-01-01", "2020-02-01"], freq="D"),
  1004. ],
  1005. )
  1006. @pytest.mark.parametrize(
  1007. "arg", [[1, 2], ["a", "b"], [Timestamp("2020-01-01", tz="Europe/London")] * 2]
  1008. )
  1009. def test_searchsorted_datetimelike_with_listlike_invalid_dtype(values, arg):
  1010. # https://github.com/pandas-dev/pandas/issues/32762
  1011. msg = "[Unexpected type|Cannot compare]"
  1012. with pytest.raises(TypeError, match=msg):
  1013. values.searchsorted(arg)
  1014. @pytest.mark.parametrize("klass", [list, tuple, np.array, pd.Series])
  1015. def test_period_index_construction_from_strings(klass):
  1016. # https://github.com/pandas-dev/pandas/issues/26109
  1017. strings = ["2020Q1", "2020Q2"] * 2
  1018. data = klass(strings)
  1019. result = PeriodIndex(data, freq="Q")
  1020. expected = PeriodIndex([Period(s) for s in strings])
  1021. tm.assert_index_equal(result, expected)
  1022. @pytest.mark.parametrize("dtype", ["M8[ns]", "m8[ns]"])
  1023. def test_from_pandas_array(dtype):
  1024. # GH#24615
  1025. data = np.array([1, 2, 3], dtype=dtype)
  1026. arr = PandasArray(data)
  1027. cls = {"M8[ns]": DatetimeArray, "m8[ns]": TimedeltaArray}[dtype]
  1028. result = cls(arr)
  1029. expected = cls(data)
  1030. tm.assert_extension_array_equal(result, expected)
  1031. result = cls._from_sequence(arr)
  1032. expected = cls._from_sequence(data)
  1033. tm.assert_extension_array_equal(result, expected)
  1034. func = {"M8[ns]": _sequence_to_dt64ns, "m8[ns]": sequence_to_td64ns}[dtype]
  1035. result = func(arr)[0]
  1036. expected = func(data)[0]
  1037. tm.assert_equal(result, expected)
  1038. func = {"M8[ns]": pd.to_datetime, "m8[ns]": pd.to_timedelta}[dtype]
  1039. result = func(arr).array
  1040. expected = func(data).array
  1041. tm.assert_equal(result, expected)
  1042. # Let's check the Indexes while we're here
  1043. idx_cls = {"M8[ns]": DatetimeIndex, "m8[ns]": TimedeltaIndex}[dtype]
  1044. result = idx_cls(arr)
  1045. expected = idx_cls(data)
  1046. tm.assert_index_equal(result, expected)
  1047. @pytest.fixture(
  1048. params=[
  1049. "memoryview",
  1050. "array",
  1051. pytest.param("dask", marks=td.skip_if_no("dask.array")),
  1052. pytest.param("xarray", marks=td.skip_if_no("xarray")),
  1053. ]
  1054. )
  1055. def array_likes(request):
  1056. """
  1057. Fixture giving a numpy array and a parametrized 'data' object, which can
  1058. be a memoryview, array, dask or xarray object created from the numpy array.
  1059. """
  1060. # GH#24539 recognize e.g xarray, dask, ...
  1061. arr = np.array([1, 2, 3], dtype=np.int64)
  1062. name = request.param
  1063. if name == "memoryview":
  1064. data = memoryview(arr)
  1065. elif name == "array":
  1066. data = array.array("i", arr)
  1067. elif name == "dask":
  1068. import dask.array
  1069. data = dask.array.array(arr)
  1070. elif name == "xarray":
  1071. import xarray as xr
  1072. data = xr.DataArray(arr)
  1073. return arr, data
  1074. @pytest.mark.parametrize("dtype", ["M8[ns]", "m8[ns]"])
  1075. def test_from_obscure_array(dtype, array_likes):
  1076. # GH#24539 recognize e.g xarray, dask, ...
  1077. # Note: we dont do this for PeriodArray bc _from_sequence won't accept
  1078. # an array of integers
  1079. # TODO: could check with arraylike of Period objects
  1080. arr, data = array_likes
  1081. cls = {"M8[ns]": DatetimeArray, "m8[ns]": TimedeltaArray}[dtype]
  1082. expected = cls(arr)
  1083. result = cls._from_sequence(data)
  1084. tm.assert_extension_array_equal(result, expected)
  1085. func = {"M8[ns]": _sequence_to_dt64ns, "m8[ns]": sequence_to_td64ns}[dtype]
  1086. result = func(arr)[0]
  1087. expected = func(data)[0]
  1088. tm.assert_equal(result, expected)
  1089. if not isinstance(data, memoryview):
  1090. # FIXME(GH#44431) these raise on memoryview and attempted fix
  1091. # fails on py3.10
  1092. func = {"M8[ns]": pd.to_datetime, "m8[ns]": pd.to_timedelta}[dtype]
  1093. result = func(arr).array
  1094. expected = func(data).array
  1095. tm.assert_equal(result, expected)
  1096. # Let's check the Indexes while we're here
  1097. idx_cls = {"M8[ns]": DatetimeIndex, "m8[ns]": TimedeltaIndex}[dtype]
  1098. result = idx_cls(arr)
  1099. expected = idx_cls(data)
  1100. tm.assert_index_equal(result, expected)