test_interval.py 50 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246
  1. from __future__ import division
  2. from itertools import permutations
  3. import re
  4. import numpy as np
  5. import pytest
  6. from pandas.compat import lzip
  7. import pandas as pd
  8. from pandas import (
  9. Index, Interval, IntervalIndex, Timedelta, Timestamp, date_range,
  10. interval_range, isna, notna, timedelta_range)
  11. import pandas.core.common as com
  12. from pandas.tests.indexes.common import Base
  13. import pandas.util.testing as tm
  14. @pytest.fixture(scope='class', params=[None, 'foo'])
  15. def name(request):
  16. return request.param
  17. class TestIntervalIndex(Base):
  18. _holder = IntervalIndex
  19. def setup_method(self, method):
  20. self.index = IntervalIndex.from_arrays([0, 1], [1, 2])
  21. self.index_with_nan = IntervalIndex.from_tuples(
  22. [(0, 1), np.nan, (1, 2)])
  23. self.indices = dict(intervalIndex=tm.makeIntervalIndex(10))
  24. def create_index(self, closed='right'):
  25. return IntervalIndex.from_breaks(range(11), closed=closed)
  26. def create_index_with_nan(self, closed='right'):
  27. mask = [True, False] + [True] * 8
  28. return IntervalIndex.from_arrays(
  29. np.where(mask, np.arange(10), np.nan),
  30. np.where(mask, np.arange(1, 11), np.nan), closed=closed)
  31. def test_properties(self, closed):
  32. index = self.create_index(closed=closed)
  33. assert len(index) == 10
  34. assert index.size == 10
  35. assert index.shape == (10, )
  36. tm.assert_index_equal(index.left, Index(np.arange(10)))
  37. tm.assert_index_equal(index.right, Index(np.arange(1, 11)))
  38. tm.assert_index_equal(index.mid, Index(np.arange(0.5, 10.5)))
  39. assert index.closed == closed
  40. ivs = [Interval(l, r, closed) for l, r in zip(range(10), range(1, 11))]
  41. expected = np.array(ivs, dtype=object)
  42. tm.assert_numpy_array_equal(np.asarray(index), expected)
  43. # with nans
  44. index = self.create_index_with_nan(closed=closed)
  45. assert len(index) == 10
  46. assert index.size == 10
  47. assert index.shape == (10, )
  48. expected_left = Index([0, np.nan, 2, 3, 4, 5, 6, 7, 8, 9])
  49. expected_right = expected_left + 1
  50. expected_mid = expected_left + 0.5
  51. tm.assert_index_equal(index.left, expected_left)
  52. tm.assert_index_equal(index.right, expected_right)
  53. tm.assert_index_equal(index.mid, expected_mid)
  54. assert index.closed == closed
  55. ivs = [Interval(l, r, closed) if notna(l) else np.nan
  56. for l, r in zip(expected_left, expected_right)]
  57. expected = np.array(ivs, dtype=object)
  58. tm.assert_numpy_array_equal(np.asarray(index), expected)
  59. @pytest.mark.parametrize('breaks', [
  60. [1, 1, 2, 5, 15, 53, 217, 1014, 5335, 31240, 201608],
  61. [-np.inf, -100, -10, 0.5, 1, 1.5, 3.8, 101, 202, np.inf],
  62. pd.to_datetime(['20170101', '20170202', '20170303', '20170404']),
  63. pd.to_timedelta(['1ns', '2ms', '3s', '4M', '5H', '6D'])])
  64. def test_length(self, closed, breaks):
  65. # GH 18789
  66. index = IntervalIndex.from_breaks(breaks, closed=closed)
  67. result = index.length
  68. expected = Index(iv.length for iv in index)
  69. tm.assert_index_equal(result, expected)
  70. # with NA
  71. index = index.insert(1, np.nan)
  72. result = index.length
  73. expected = Index(iv.length if notna(iv) else iv for iv in index)
  74. tm.assert_index_equal(result, expected)
  75. def test_with_nans(self, closed):
  76. index = self.create_index(closed=closed)
  77. assert index.hasnans is False
  78. result = index.isna()
  79. expected = np.repeat(False, len(index))
  80. tm.assert_numpy_array_equal(result, expected)
  81. result = index.notna()
  82. expected = np.repeat(True, len(index))
  83. tm.assert_numpy_array_equal(result, expected)
  84. index = self.create_index_with_nan(closed=closed)
  85. assert index.hasnans is True
  86. result = index.isna()
  87. expected = np.array([False, True] + [False] * (len(index) - 2))
  88. tm.assert_numpy_array_equal(result, expected)
  89. result = index.notna()
  90. expected = np.array([True, False] + [True] * (len(index) - 2))
  91. tm.assert_numpy_array_equal(result, expected)
  92. def test_copy(self, closed):
  93. expected = self.create_index(closed=closed)
  94. result = expected.copy()
  95. assert result.equals(expected)
  96. result = expected.copy(deep=True)
  97. assert result.equals(expected)
  98. assert result.left is not expected.left
  99. def test_ensure_copied_data(self, closed):
  100. # exercise the copy flag in the constructor
  101. # not copying
  102. index = self.create_index(closed=closed)
  103. result = IntervalIndex(index, copy=False)
  104. tm.assert_numpy_array_equal(index.left.values, result.left.values,
  105. check_same='same')
  106. tm.assert_numpy_array_equal(index.right.values, result.right.values,
  107. check_same='same')
  108. # by-definition make a copy
  109. result = IntervalIndex(index._ndarray_values, copy=False)
  110. tm.assert_numpy_array_equal(index.left.values, result.left.values,
  111. check_same='copy')
  112. tm.assert_numpy_array_equal(index.right.values, result.right.values,
  113. check_same='copy')
  114. def test_equals(self, closed):
  115. expected = IntervalIndex.from_breaks(np.arange(5), closed=closed)
  116. assert expected.equals(expected)
  117. assert expected.equals(expected.copy())
  118. assert not expected.equals(expected.astype(object))
  119. assert not expected.equals(np.array(expected))
  120. assert not expected.equals(list(expected))
  121. assert not expected.equals([1, 2])
  122. assert not expected.equals(np.array([1, 2]))
  123. assert not expected.equals(pd.date_range('20130101', periods=2))
  124. expected_name1 = IntervalIndex.from_breaks(
  125. np.arange(5), closed=closed, name='foo')
  126. expected_name2 = IntervalIndex.from_breaks(
  127. np.arange(5), closed=closed, name='bar')
  128. assert expected.equals(expected_name1)
  129. assert expected_name1.equals(expected_name2)
  130. for other_closed in {'left', 'right', 'both', 'neither'} - {closed}:
  131. expected_other_closed = IntervalIndex.from_breaks(
  132. np.arange(5), closed=other_closed)
  133. assert not expected.equals(expected_other_closed)
  134. @pytest.mark.parametrize('klass', [list, tuple, np.array, pd.Series])
  135. def test_where(self, closed, klass):
  136. idx = self.create_index(closed=closed)
  137. cond = [True] * len(idx)
  138. expected = idx
  139. result = expected.where(klass(cond))
  140. tm.assert_index_equal(result, expected)
  141. cond = [False] + [True] * len(idx[1:])
  142. expected = IntervalIndex([np.nan] + idx[1:].tolist())
  143. result = idx.where(klass(cond))
  144. tm.assert_index_equal(result, expected)
  145. def test_delete(self, closed):
  146. expected = IntervalIndex.from_breaks(np.arange(1, 11), closed=closed)
  147. result = self.create_index(closed=closed).delete(0)
  148. tm.assert_index_equal(result, expected)
  149. @pytest.mark.parametrize('data', [
  150. interval_range(0, periods=10, closed='neither'),
  151. interval_range(1.7, periods=8, freq=2.5, closed='both'),
  152. interval_range(Timestamp('20170101'), periods=12, closed='left'),
  153. interval_range(Timedelta('1 day'), periods=6, closed='right')])
  154. def test_insert(self, data):
  155. item = data[0]
  156. idx_item = IntervalIndex([item])
  157. # start
  158. expected = idx_item.append(data)
  159. result = data.insert(0, item)
  160. tm.assert_index_equal(result, expected)
  161. # end
  162. expected = data.append(idx_item)
  163. result = data.insert(len(data), item)
  164. tm.assert_index_equal(result, expected)
  165. # mid
  166. expected = data[:3].append(idx_item).append(data[3:])
  167. result = data.insert(3, item)
  168. tm.assert_index_equal(result, expected)
  169. # invalid type
  170. msg = 'can only insert Interval objects and NA into an IntervalIndex'
  171. with pytest.raises(ValueError, match=msg):
  172. data.insert(1, 'foo')
  173. # invalid closed
  174. msg = 'inserted item must be closed on the same side as the index'
  175. for closed in {'left', 'right', 'both', 'neither'} - {item.closed}:
  176. with pytest.raises(ValueError, match=msg):
  177. bad_item = Interval(item.left, item.right, closed=closed)
  178. data.insert(1, bad_item)
  179. # GH 18295 (test missing)
  180. na_idx = IntervalIndex([np.nan], closed=data.closed)
  181. for na in (np.nan, pd.NaT, None):
  182. expected = data[:1].append(na_idx).append(data[1:])
  183. result = data.insert(1, na)
  184. tm.assert_index_equal(result, expected)
  185. def test_take(self, closed):
  186. index = self.create_index(closed=closed)
  187. result = index.take(range(10))
  188. tm.assert_index_equal(result, index)
  189. result = index.take([0, 0, 1])
  190. expected = IntervalIndex.from_arrays(
  191. [0, 0, 1], [1, 1, 2], closed=closed)
  192. tm.assert_index_equal(result, expected)
  193. def test_is_unique_interval(self, closed):
  194. """
  195. Interval specific tests for is_unique in addition to base class tests
  196. """
  197. # unique overlapping - distinct endpoints
  198. idx = IntervalIndex.from_tuples([(0, 1), (0.5, 1.5)], closed=closed)
  199. assert idx.is_unique is True
  200. # unique overlapping - shared endpoints
  201. idx = pd.IntervalIndex.from_tuples(
  202. [(1, 2), (1, 3), (2, 3)], closed=closed)
  203. assert idx.is_unique is True
  204. # unique nested
  205. idx = IntervalIndex.from_tuples([(-1, 1), (-2, 2)], closed=closed)
  206. assert idx.is_unique is True
  207. def test_monotonic(self, closed):
  208. # increasing non-overlapping
  209. idx = IntervalIndex.from_tuples(
  210. [(0, 1), (2, 3), (4, 5)], closed=closed)
  211. assert idx.is_monotonic is True
  212. assert idx._is_strictly_monotonic_increasing is True
  213. assert idx.is_monotonic_decreasing is False
  214. assert idx._is_strictly_monotonic_decreasing is False
  215. # decreasing non-overlapping
  216. idx = IntervalIndex.from_tuples(
  217. [(4, 5), (2, 3), (1, 2)], closed=closed)
  218. assert idx.is_monotonic is False
  219. assert idx._is_strictly_monotonic_increasing is False
  220. assert idx.is_monotonic_decreasing is True
  221. assert idx._is_strictly_monotonic_decreasing is True
  222. # unordered non-overlapping
  223. idx = IntervalIndex.from_tuples(
  224. [(0, 1), (4, 5), (2, 3)], closed=closed)
  225. assert idx.is_monotonic is False
  226. assert idx._is_strictly_monotonic_increasing is False
  227. assert idx.is_monotonic_decreasing is False
  228. assert idx._is_strictly_monotonic_decreasing is False
  229. # increasing overlapping
  230. idx = IntervalIndex.from_tuples(
  231. [(0, 2), (0.5, 2.5), (1, 3)], closed=closed)
  232. assert idx.is_monotonic is True
  233. assert idx._is_strictly_monotonic_increasing is True
  234. assert idx.is_monotonic_decreasing is False
  235. assert idx._is_strictly_monotonic_decreasing is False
  236. # decreasing overlapping
  237. idx = IntervalIndex.from_tuples(
  238. [(1, 3), (0.5, 2.5), (0, 2)], closed=closed)
  239. assert idx.is_monotonic is False
  240. assert idx._is_strictly_monotonic_increasing is False
  241. assert idx.is_monotonic_decreasing is True
  242. assert idx._is_strictly_monotonic_decreasing is True
  243. # unordered overlapping
  244. idx = IntervalIndex.from_tuples(
  245. [(0.5, 2.5), (0, 2), (1, 3)], closed=closed)
  246. assert idx.is_monotonic is False
  247. assert idx._is_strictly_monotonic_increasing is False
  248. assert idx.is_monotonic_decreasing is False
  249. assert idx._is_strictly_monotonic_decreasing is False
  250. # increasing overlapping shared endpoints
  251. idx = pd.IntervalIndex.from_tuples(
  252. [(1, 2), (1, 3), (2, 3)], closed=closed)
  253. assert idx.is_monotonic is True
  254. assert idx._is_strictly_monotonic_increasing is True
  255. assert idx.is_monotonic_decreasing is False
  256. assert idx._is_strictly_monotonic_decreasing is False
  257. # decreasing overlapping shared endpoints
  258. idx = pd.IntervalIndex.from_tuples(
  259. [(2, 3), (1, 3), (1, 2)], closed=closed)
  260. assert idx.is_monotonic is False
  261. assert idx._is_strictly_monotonic_increasing is False
  262. assert idx.is_monotonic_decreasing is True
  263. assert idx._is_strictly_monotonic_decreasing is True
  264. # stationary
  265. idx = IntervalIndex.from_tuples([(0, 1), (0, 1)], closed=closed)
  266. assert idx.is_monotonic is True
  267. assert idx._is_strictly_monotonic_increasing is False
  268. assert idx.is_monotonic_decreasing is True
  269. assert idx._is_strictly_monotonic_decreasing is False
  270. # empty
  271. idx = IntervalIndex([], closed=closed)
  272. assert idx.is_monotonic is True
  273. assert idx._is_strictly_monotonic_increasing is True
  274. assert idx.is_monotonic_decreasing is True
  275. assert idx._is_strictly_monotonic_decreasing is True
  276. @pytest.mark.skip(reason='not a valid repr as we use interval notation')
  277. def test_repr(self):
  278. i = IntervalIndex.from_tuples([(0, 1), (1, 2)], closed='right')
  279. expected = ("IntervalIndex(left=[0, 1],"
  280. "\n right=[1, 2],"
  281. "\n closed='right',"
  282. "\n dtype='interval[int64]')")
  283. assert repr(i) == expected
  284. i = IntervalIndex.from_tuples((Timestamp('20130101'),
  285. Timestamp('20130102')),
  286. (Timestamp('20130102'),
  287. Timestamp('20130103')),
  288. closed='right')
  289. expected = ("IntervalIndex(left=['2013-01-01', '2013-01-02'],"
  290. "\n right=['2013-01-02', '2013-01-03'],"
  291. "\n closed='right',"
  292. "\n dtype='interval[datetime64[ns]]')")
  293. assert repr(i) == expected
  294. @pytest.mark.skip(reason='not a valid repr as we use interval notation')
  295. def test_repr_max_seq_item_setting(self):
  296. super(TestIntervalIndex, self).test_repr_max_seq_item_setting()
  297. @pytest.mark.skip(reason='not a valid repr as we use interval notation')
  298. def test_repr_roundtrip(self):
  299. super(TestIntervalIndex, self).test_repr_roundtrip()
  300. def test_frame_repr(self):
  301. # https://github.com/pandas-dev/pandas/pull/24134/files
  302. df = pd.DataFrame({'A': [1, 2, 3, 4]},
  303. index=pd.IntervalIndex.from_breaks([0, 1, 2, 3, 4]))
  304. result = repr(df)
  305. expected = (
  306. ' A\n'
  307. '(0, 1] 1\n'
  308. '(1, 2] 2\n'
  309. '(2, 3] 3\n'
  310. '(3, 4] 4'
  311. )
  312. assert result == expected
  313. # TODO: check this behavior is consistent with test_interval_new.py
  314. def test_get_item(self, closed):
  315. i = IntervalIndex.from_arrays((0, 1, np.nan), (1, 2, np.nan),
  316. closed=closed)
  317. assert i[0] == Interval(0.0, 1.0, closed=closed)
  318. assert i[1] == Interval(1.0, 2.0, closed=closed)
  319. assert isna(i[2])
  320. result = i[0:1]
  321. expected = IntervalIndex.from_arrays((0.,), (1.,), closed=closed)
  322. tm.assert_index_equal(result, expected)
  323. result = i[0:2]
  324. expected = IntervalIndex.from_arrays((0., 1), (1., 2.), closed=closed)
  325. tm.assert_index_equal(result, expected)
  326. result = i[1:3]
  327. expected = IntervalIndex.from_arrays((1., np.nan), (2., np.nan),
  328. closed=closed)
  329. tm.assert_index_equal(result, expected)
  330. # To be removed, replaced by test_interval_new.py (see #16316, #16386)
  331. def test_get_loc_value(self):
  332. pytest.raises(KeyError, self.index.get_loc, 0)
  333. assert self.index.get_loc(0.5) == 0
  334. assert self.index.get_loc(1) == 0
  335. assert self.index.get_loc(1.5) == 1
  336. assert self.index.get_loc(2) == 1
  337. pytest.raises(KeyError, self.index.get_loc, -1)
  338. pytest.raises(KeyError, self.index.get_loc, 3)
  339. idx = IntervalIndex.from_tuples([(0, 2), (1, 3)])
  340. assert idx.get_loc(0.5) == 0
  341. assert idx.get_loc(1) == 0
  342. tm.assert_numpy_array_equal(idx.get_loc(1.5),
  343. np.array([0, 1], dtype='intp'))
  344. tm.assert_numpy_array_equal(np.sort(idx.get_loc(2)),
  345. np.array([0, 1], dtype='intp'))
  346. assert idx.get_loc(3) == 1
  347. pytest.raises(KeyError, idx.get_loc, 3.5)
  348. idx = IntervalIndex.from_arrays([0, 2], [1, 3])
  349. pytest.raises(KeyError, idx.get_loc, 1.5)
  350. # To be removed, replaced by test_interval_new.py (see #16316, #16386)
  351. def slice_locs_cases(self, breaks):
  352. # TODO: same tests for more index types
  353. index = IntervalIndex.from_breaks([0, 1, 2], closed='right')
  354. assert index.slice_locs() == (0, 2)
  355. assert index.slice_locs(0, 1) == (0, 1)
  356. assert index.slice_locs(1, 1) == (0, 1)
  357. assert index.slice_locs(0, 2) == (0, 2)
  358. assert index.slice_locs(0.5, 1.5) == (0, 2)
  359. assert index.slice_locs(0, 0.5) == (0, 1)
  360. assert index.slice_locs(start=1) == (0, 2)
  361. assert index.slice_locs(start=1.2) == (1, 2)
  362. assert index.slice_locs(end=1) == (0, 1)
  363. assert index.slice_locs(end=1.1) == (0, 2)
  364. assert index.slice_locs(end=1.0) == (0, 1)
  365. assert index.slice_locs(-1, -1) == (0, 0)
  366. index = IntervalIndex.from_breaks([0, 1, 2], closed='neither')
  367. assert index.slice_locs(0, 1) == (0, 1)
  368. assert index.slice_locs(0, 2) == (0, 2)
  369. assert index.slice_locs(0.5, 1.5) == (0, 2)
  370. assert index.slice_locs(1, 1) == (1, 1)
  371. assert index.slice_locs(1, 2) == (1, 2)
  372. index = IntervalIndex.from_tuples([(0, 1), (2, 3), (4, 5)],
  373. closed='both')
  374. assert index.slice_locs(1, 1) == (0, 1)
  375. assert index.slice_locs(1, 2) == (0, 2)
  376. # To be removed, replaced by test_interval_new.py (see #16316, #16386)
  377. def test_slice_locs_int64(self):
  378. self.slice_locs_cases([0, 1, 2])
  379. # To be removed, replaced by test_interval_new.py (see #16316, #16386)
  380. def test_slice_locs_float64(self):
  381. self.slice_locs_cases([0.0, 1.0, 2.0])
  382. # To be removed, replaced by test_interval_new.py (see #16316, #16386)
  383. def slice_locs_decreasing_cases(self, tuples):
  384. index = IntervalIndex.from_tuples(tuples)
  385. assert index.slice_locs(1.5, 0.5) == (1, 3)
  386. assert index.slice_locs(2, 0) == (1, 3)
  387. assert index.slice_locs(2, 1) == (1, 3)
  388. assert index.slice_locs(3, 1.1) == (0, 3)
  389. assert index.slice_locs(3, 3) == (0, 2)
  390. assert index.slice_locs(3.5, 3.3) == (0, 1)
  391. assert index.slice_locs(1, -3) == (2, 3)
  392. slice_locs = index.slice_locs(-1, -1)
  393. assert slice_locs[0] == slice_locs[1]
  394. # To be removed, replaced by test_interval_new.py (see #16316, #16386)
  395. def test_slice_locs_decreasing_int64(self):
  396. self.slice_locs_cases([(2, 4), (1, 3), (0, 2)])
  397. # To be removed, replaced by test_interval_new.py (see #16316, #16386)
  398. def test_slice_locs_decreasing_float64(self):
  399. self.slice_locs_cases([(2., 4.), (1., 3.), (0., 2.)])
  400. # To be removed, replaced by test_interval_new.py (see #16316, #16386)
  401. def test_slice_locs_fails(self):
  402. index = IntervalIndex.from_tuples([(1, 2), (0, 1), (2, 3)])
  403. with pytest.raises(KeyError):
  404. index.slice_locs(1, 2)
  405. # To be removed, replaced by test_interval_new.py (see #16316, #16386)
  406. def test_get_loc_interval(self):
  407. assert self.index.get_loc(Interval(0, 1)) == 0
  408. assert self.index.get_loc(Interval(0, 0.5)) == 0
  409. assert self.index.get_loc(Interval(0, 1, 'left')) == 0
  410. pytest.raises(KeyError, self.index.get_loc, Interval(2, 3))
  411. pytest.raises(KeyError, self.index.get_loc,
  412. Interval(-1, 0, 'left'))
  413. # Make consistent with test_interval_new.py (see #16316, #16386)
  414. @pytest.mark.parametrize('item', [3, Interval(1, 4)])
  415. def test_get_loc_length_one(self, item, closed):
  416. # GH 20921
  417. index = IntervalIndex.from_tuples([(0, 5)], closed=closed)
  418. result = index.get_loc(item)
  419. assert result == 0
  420. # Make consistent with test_interval_new.py (see #16316, #16386)
  421. @pytest.mark.parametrize('breaks', [
  422. date_range('20180101', periods=4),
  423. date_range('20180101', periods=4, tz='US/Eastern'),
  424. timedelta_range('0 days', periods=4)], ids=lambda x: str(x.dtype))
  425. def test_get_loc_datetimelike_nonoverlapping(self, breaks):
  426. # GH 20636
  427. # nonoverlapping = IntervalIndex method and no i8 conversion
  428. index = IntervalIndex.from_breaks(breaks)
  429. value = index[0].mid
  430. result = index.get_loc(value)
  431. expected = 0
  432. assert result == expected
  433. interval = Interval(index[0].left, index[1].right)
  434. result = index.get_loc(interval)
  435. expected = slice(0, 2)
  436. assert result == expected
  437. # Make consistent with test_interval_new.py (see #16316, #16386)
  438. @pytest.mark.parametrize('arrays', [
  439. (date_range('20180101', periods=4), date_range('20180103', periods=4)),
  440. (date_range('20180101', periods=4, tz='US/Eastern'),
  441. date_range('20180103', periods=4, tz='US/Eastern')),
  442. (timedelta_range('0 days', periods=4),
  443. timedelta_range('2 days', periods=4))], ids=lambda x: str(x[0].dtype))
  444. def test_get_loc_datetimelike_overlapping(self, arrays):
  445. # GH 20636
  446. # overlapping = IntervalTree method with i8 conversion
  447. index = IntervalIndex.from_arrays(*arrays)
  448. value = index[0].mid + Timedelta('12 hours')
  449. result = np.sort(index.get_loc(value))
  450. expected = np.array([0, 1], dtype='intp')
  451. assert tm.assert_numpy_array_equal(result, expected)
  452. interval = Interval(index[0].left, index[1].right)
  453. result = np.sort(index.get_loc(interval))
  454. expected = np.array([0, 1, 2], dtype='intp')
  455. assert tm.assert_numpy_array_equal(result, expected)
  456. # To be removed, replaced by test_interval_new.py (see #16316, #16386)
  457. def test_get_indexer(self):
  458. actual = self.index.get_indexer([-1, 0, 0.5, 1, 1.5, 2, 3])
  459. expected = np.array([-1, -1, 0, 0, 1, 1, -1], dtype='intp')
  460. tm.assert_numpy_array_equal(actual, expected)
  461. actual = self.index.get_indexer(self.index)
  462. expected = np.array([0, 1], dtype='intp')
  463. tm.assert_numpy_array_equal(actual, expected)
  464. index = IntervalIndex.from_breaks([0, 1, 2], closed='left')
  465. actual = index.get_indexer([-1, 0, 0.5, 1, 1.5, 2, 3])
  466. expected = np.array([-1, 0, 0, 1, 1, -1, -1], dtype='intp')
  467. tm.assert_numpy_array_equal(actual, expected)
  468. actual = self.index.get_indexer(index[:1])
  469. expected = np.array([0], dtype='intp')
  470. tm.assert_numpy_array_equal(actual, expected)
  471. actual = self.index.get_indexer(index)
  472. expected = np.array([-1, 1], dtype='intp')
  473. tm.assert_numpy_array_equal(actual, expected)
  474. # To be removed, replaced by test_interval_new.py (see #16316, #16386)
  475. def test_get_indexer_subintervals(self):
  476. # TODO: is this right?
  477. # return indexers for wholly contained subintervals
  478. target = IntervalIndex.from_breaks(np.linspace(0, 2, 5))
  479. actual = self.index.get_indexer(target)
  480. expected = np.array([0, 0, 1, 1], dtype='p')
  481. tm.assert_numpy_array_equal(actual, expected)
  482. target = IntervalIndex.from_breaks([0, 0.67, 1.33, 2])
  483. actual = self.index.get_indexer(target)
  484. expected = np.array([0, 0, 1, 1], dtype='intp')
  485. tm.assert_numpy_array_equal(actual, expected)
  486. actual = self.index.get_indexer(target[[0, -1]])
  487. expected = np.array([0, 1], dtype='intp')
  488. tm.assert_numpy_array_equal(actual, expected)
  489. target = IntervalIndex.from_breaks([0, 0.33, 0.67, 1], closed='left')
  490. actual = self.index.get_indexer(target)
  491. expected = np.array([0, 0, 0], dtype='intp')
  492. tm.assert_numpy_array_equal(actual, expected)
  493. # Make consistent with test_interval_new.py (see #16316, #16386)
  494. @pytest.mark.parametrize('item', [
  495. [3], np.arange(1, 5), [Interval(1, 4)], interval_range(1, 4)])
  496. def test_get_indexer_length_one(self, item, closed):
  497. # GH 17284
  498. index = IntervalIndex.from_tuples([(0, 5)], closed=closed)
  499. result = index.get_indexer(item)
  500. expected = np.array([0] * len(item), dtype='intp')
  501. tm.assert_numpy_array_equal(result, expected)
  502. # Make consistent with test_interval_new.py (see #16316, #16386)
  503. @pytest.mark.parametrize('arrays', [
  504. (date_range('20180101', periods=4), date_range('20180103', periods=4)),
  505. (date_range('20180101', periods=4, tz='US/Eastern'),
  506. date_range('20180103', periods=4, tz='US/Eastern')),
  507. (timedelta_range('0 days', periods=4),
  508. timedelta_range('2 days', periods=4))], ids=lambda x: str(x[0].dtype))
  509. def test_get_reindexer_datetimelike(self, arrays):
  510. # GH 20636
  511. index = IntervalIndex.from_arrays(*arrays)
  512. tuples = [(index[0].left, index[0].left + pd.Timedelta('12H')),
  513. (index[-1].right - pd.Timedelta('12H'), index[-1].right)]
  514. target = IntervalIndex.from_tuples(tuples)
  515. result = index._get_reindexer(target)
  516. expected = np.array([0, 3], dtype='intp')
  517. tm.assert_numpy_array_equal(result, expected)
  518. @pytest.mark.parametrize('breaks', [
  519. date_range('20180101', periods=4),
  520. date_range('20180101', periods=4, tz='US/Eastern'),
  521. timedelta_range('0 days', periods=4)], ids=lambda x: str(x.dtype))
  522. def test_maybe_convert_i8(self, breaks):
  523. # GH 20636
  524. index = IntervalIndex.from_breaks(breaks)
  525. # intervalindex
  526. result = index._maybe_convert_i8(index)
  527. expected = IntervalIndex.from_breaks(breaks.asi8)
  528. tm.assert_index_equal(result, expected)
  529. # interval
  530. interval = Interval(breaks[0], breaks[1])
  531. result = index._maybe_convert_i8(interval)
  532. expected = Interval(breaks[0].value, breaks[1].value)
  533. assert result == expected
  534. # datetimelike index
  535. result = index._maybe_convert_i8(breaks)
  536. expected = Index(breaks.asi8)
  537. tm.assert_index_equal(result, expected)
  538. # datetimelike scalar
  539. result = index._maybe_convert_i8(breaks[0])
  540. expected = breaks[0].value
  541. assert result == expected
  542. # list-like of datetimelike scalars
  543. result = index._maybe_convert_i8(list(breaks))
  544. expected = Index(breaks.asi8)
  545. tm.assert_index_equal(result, expected)
  546. @pytest.mark.parametrize('breaks', [
  547. date_range('2018-01-01', periods=5),
  548. timedelta_range('0 days', periods=5)])
  549. def test_maybe_convert_i8_nat(self, breaks):
  550. # GH 20636
  551. index = IntervalIndex.from_breaks(breaks)
  552. to_convert = breaks._constructor([pd.NaT] * 3)
  553. expected = pd.Float64Index([np.nan] * 3)
  554. result = index._maybe_convert_i8(to_convert)
  555. tm.assert_index_equal(result, expected)
  556. to_convert = to_convert.insert(0, breaks[0])
  557. expected = expected.insert(0, float(breaks[0].value))
  558. result = index._maybe_convert_i8(to_convert)
  559. tm.assert_index_equal(result, expected)
  560. @pytest.mark.parametrize('breaks', [
  561. np.arange(5, dtype='int64'),
  562. np.arange(5, dtype='float64')], ids=lambda x: str(x.dtype))
  563. @pytest.mark.parametrize('make_key', [
  564. IntervalIndex.from_breaks,
  565. lambda breaks: Interval(breaks[0], breaks[1]),
  566. lambda breaks: breaks,
  567. lambda breaks: breaks[0],
  568. list], ids=['IntervalIndex', 'Interval', 'Index', 'scalar', 'list'])
  569. def test_maybe_convert_i8_numeric(self, breaks, make_key):
  570. # GH 20636
  571. index = IntervalIndex.from_breaks(breaks)
  572. key = make_key(breaks)
  573. # no conversion occurs for numeric
  574. result = index._maybe_convert_i8(key)
  575. assert result is key
  576. @pytest.mark.parametrize('breaks1, breaks2', permutations([
  577. date_range('20180101', periods=4),
  578. date_range('20180101', periods=4, tz='US/Eastern'),
  579. timedelta_range('0 days', periods=4)], 2), ids=lambda x: str(x.dtype))
  580. @pytest.mark.parametrize('make_key', [
  581. IntervalIndex.from_breaks,
  582. lambda breaks: Interval(breaks[0], breaks[1]),
  583. lambda breaks: breaks,
  584. lambda breaks: breaks[0],
  585. list], ids=['IntervalIndex', 'Interval', 'Index', 'scalar', 'list'])
  586. def test_maybe_convert_i8_errors(self, breaks1, breaks2, make_key):
  587. # GH 20636
  588. index = IntervalIndex.from_breaks(breaks1)
  589. key = make_key(breaks2)
  590. msg = ('Cannot index an IntervalIndex of subtype {dtype1} with '
  591. 'values of dtype {dtype2}')
  592. msg = re.escape(msg.format(dtype1=breaks1.dtype, dtype2=breaks2.dtype))
  593. with pytest.raises(ValueError, match=msg):
  594. index._maybe_convert_i8(key)
  595. # To be removed, replaced by test_interval_new.py (see #16316, #16386)
  596. def test_contains(self):
  597. # Only endpoints are valid.
  598. i = IntervalIndex.from_arrays([0, 1], [1, 2])
  599. # Invalid
  600. assert 0 not in i
  601. assert 1 not in i
  602. assert 2 not in i
  603. # Valid
  604. assert Interval(0, 1) in i
  605. assert Interval(0, 2) in i
  606. assert Interval(0, 0.5) in i
  607. assert Interval(3, 5) not in i
  608. assert Interval(-1, 0, closed='left') not in i
  609. # To be removed, replaced by test_interval_new.py (see #16316, #16386)
  610. def testcontains(self):
  611. # can select values that are IN the range of a value
  612. i = IntervalIndex.from_arrays([0, 1], [1, 2])
  613. assert i.contains(0.1)
  614. assert i.contains(0.5)
  615. assert i.contains(1)
  616. assert i.contains(Interval(0, 1))
  617. assert i.contains(Interval(0, 2))
  618. # these overlaps completely
  619. assert i.contains(Interval(0, 3))
  620. assert i.contains(Interval(1, 3))
  621. assert not i.contains(20)
  622. assert not i.contains(-20)
  623. def test_dropna(self, closed):
  624. expected = IntervalIndex.from_tuples(
  625. [(0.0, 1.0), (1.0, 2.0)], closed=closed)
  626. ii = IntervalIndex.from_tuples([(0, 1), (1, 2), np.nan], closed=closed)
  627. result = ii.dropna()
  628. tm.assert_index_equal(result, expected)
  629. ii = IntervalIndex.from_arrays(
  630. [0, 1, np.nan], [1, 2, np.nan], closed=closed)
  631. result = ii.dropna()
  632. tm.assert_index_equal(result, expected)
  633. # TODO: check this behavior is consistent with test_interval_new.py
  634. def test_non_contiguous(self, closed):
  635. index = IntervalIndex.from_tuples([(0, 1), (2, 3)], closed=closed)
  636. target = [0.5, 1.5, 2.5]
  637. actual = index.get_indexer(target)
  638. expected = np.array([0, -1, 1], dtype='intp')
  639. tm.assert_numpy_array_equal(actual, expected)
  640. assert 1.5 not in index
  641. @pytest.mark.parametrize("sort", [None, False])
  642. def test_union(self, closed, sort):
  643. index = self.create_index(closed=closed)
  644. other = IntervalIndex.from_breaks(range(5, 13), closed=closed)
  645. expected = IntervalIndex.from_breaks(range(13), closed=closed)
  646. result = index[::-1].union(other, sort=sort)
  647. if sort is None:
  648. tm.assert_index_equal(result, expected)
  649. assert tm.equalContents(result, expected)
  650. result = other[::-1].union(index, sort=sort)
  651. if sort is None:
  652. tm.assert_index_equal(result, expected)
  653. assert tm.equalContents(result, expected)
  654. tm.assert_index_equal(index.union(index, sort=sort), index)
  655. tm.assert_index_equal(index.union(index[:1], sort=sort), index)
  656. # GH 19101: empty result, same dtype
  657. index = IntervalIndex(np.array([], dtype='int64'), closed=closed)
  658. result = index.union(index, sort=sort)
  659. tm.assert_index_equal(result, index)
  660. # GH 19101: empty result, different dtypes
  661. other = IntervalIndex(np.array([], dtype='float64'), closed=closed)
  662. result = index.union(other, sort=sort)
  663. tm.assert_index_equal(result, index)
  664. @pytest.mark.parametrize("sort", [None, False])
  665. def test_intersection(self, closed, sort):
  666. index = self.create_index(closed=closed)
  667. other = IntervalIndex.from_breaks(range(5, 13), closed=closed)
  668. expected = IntervalIndex.from_breaks(range(5, 11), closed=closed)
  669. result = index[::-1].intersection(other, sort=sort)
  670. if sort is None:
  671. tm.assert_index_equal(result, expected)
  672. assert tm.equalContents(result, expected)
  673. result = other[::-1].intersection(index, sort=sort)
  674. if sort is None:
  675. tm.assert_index_equal(result, expected)
  676. assert tm.equalContents(result, expected)
  677. tm.assert_index_equal(index.intersection(index, sort=sort), index)
  678. # GH 19101: empty result, same dtype
  679. other = IntervalIndex.from_breaks(range(300, 314), closed=closed)
  680. expected = IntervalIndex(np.array([], dtype='int64'), closed=closed)
  681. result = index.intersection(other, sort=sort)
  682. tm.assert_index_equal(result, expected)
  683. # GH 19101: empty result, different dtypes
  684. breaks = np.arange(300, 314, dtype='float64')
  685. other = IntervalIndex.from_breaks(breaks, closed=closed)
  686. result = index.intersection(other, sort=sort)
  687. tm.assert_index_equal(result, expected)
  688. @pytest.mark.parametrize("sort", [None, False])
  689. def test_difference(self, closed, sort):
  690. index = IntervalIndex.from_arrays([1, 0, 3, 2],
  691. [1, 2, 3, 4],
  692. closed=closed)
  693. result = index.difference(index[:1], sort=sort)
  694. expected = index[1:]
  695. if sort is None:
  696. expected = expected.sort_values()
  697. tm.assert_index_equal(result, expected)
  698. # GH 19101: empty result, same dtype
  699. result = index.difference(index, sort=sort)
  700. expected = IntervalIndex(np.array([], dtype='int64'), closed=closed)
  701. tm.assert_index_equal(result, expected)
  702. # GH 19101: empty result, different dtypes
  703. other = IntervalIndex.from_arrays(index.left.astype('float64'),
  704. index.right, closed=closed)
  705. result = index.difference(other, sort=sort)
  706. tm.assert_index_equal(result, expected)
  707. @pytest.mark.parametrize("sort", [None, False])
  708. def test_symmetric_difference(self, closed, sort):
  709. index = self.create_index(closed=closed)
  710. result = index[1:].symmetric_difference(index[:-1], sort=sort)
  711. expected = IntervalIndex([index[0], index[-1]])
  712. if sort is None:
  713. tm.assert_index_equal(result, expected)
  714. assert tm.equalContents(result, expected)
  715. # GH 19101: empty result, same dtype
  716. result = index.symmetric_difference(index, sort=sort)
  717. expected = IntervalIndex(np.array([], dtype='int64'), closed=closed)
  718. if sort is None:
  719. tm.assert_index_equal(result, expected)
  720. assert tm.equalContents(result, expected)
  721. # GH 19101: empty result, different dtypes
  722. other = IntervalIndex.from_arrays(index.left.astype('float64'),
  723. index.right, closed=closed)
  724. result = index.symmetric_difference(other, sort=sort)
  725. tm.assert_index_equal(result, expected)
  726. @pytest.mark.parametrize('op_name', [
  727. 'union', 'intersection', 'difference', 'symmetric_difference'])
  728. @pytest.mark.parametrize("sort", [None, False])
  729. def test_set_operation_errors(self, closed, op_name, sort):
  730. index = self.create_index(closed=closed)
  731. set_op = getattr(index, op_name)
  732. # non-IntervalIndex
  733. msg = ('the other index needs to be an IntervalIndex too, but '
  734. 'was type Int64Index')
  735. with pytest.raises(TypeError, match=msg):
  736. set_op(Index([1, 2, 3]), sort=sort)
  737. # mixed closed
  738. msg = ('can only do set operations between two IntervalIndex objects '
  739. 'that are closed on the same side')
  740. for other_closed in {'right', 'left', 'both', 'neither'} - {closed}:
  741. other = self.create_index(closed=other_closed)
  742. with pytest.raises(ValueError, match=msg):
  743. set_op(other, sort=sort)
  744. # GH 19016: incompatible dtypes
  745. other = interval_range(Timestamp('20180101'), periods=9, closed=closed)
  746. msg = ('can only do {op} between two IntervalIndex objects that have '
  747. 'compatible dtypes').format(op=op_name)
  748. with pytest.raises(TypeError, match=msg):
  749. set_op(other, sort=sort)
  750. def test_isin(self, closed):
  751. index = self.create_index(closed=closed)
  752. expected = np.array([True] + [False] * (len(index) - 1))
  753. result = index.isin(index[:1])
  754. tm.assert_numpy_array_equal(result, expected)
  755. result = index.isin([index[0]])
  756. tm.assert_numpy_array_equal(result, expected)
  757. other = IntervalIndex.from_breaks(np.arange(-2, 10), closed=closed)
  758. expected = np.array([True] * (len(index) - 1) + [False])
  759. result = index.isin(other)
  760. tm.assert_numpy_array_equal(result, expected)
  761. result = index.isin(other.tolist())
  762. tm.assert_numpy_array_equal(result, expected)
  763. for other_closed in {'right', 'left', 'both', 'neither'}:
  764. other = self.create_index(closed=other_closed)
  765. expected = np.repeat(closed == other_closed, len(index))
  766. result = index.isin(other)
  767. tm.assert_numpy_array_equal(result, expected)
  768. result = index.isin(other.tolist())
  769. tm.assert_numpy_array_equal(result, expected)
  770. def test_comparison(self):
  771. actual = Interval(0, 1) < self.index
  772. expected = np.array([False, True])
  773. tm.assert_numpy_array_equal(actual, expected)
  774. actual = Interval(0.5, 1.5) < self.index
  775. expected = np.array([False, True])
  776. tm.assert_numpy_array_equal(actual, expected)
  777. actual = self.index > Interval(0.5, 1.5)
  778. tm.assert_numpy_array_equal(actual, expected)
  779. actual = self.index == self.index
  780. expected = np.array([True, True])
  781. tm.assert_numpy_array_equal(actual, expected)
  782. actual = self.index <= self.index
  783. tm.assert_numpy_array_equal(actual, expected)
  784. actual = self.index >= self.index
  785. tm.assert_numpy_array_equal(actual, expected)
  786. actual = self.index < self.index
  787. expected = np.array([False, False])
  788. tm.assert_numpy_array_equal(actual, expected)
  789. actual = self.index > self.index
  790. tm.assert_numpy_array_equal(actual, expected)
  791. actual = self.index == IntervalIndex.from_breaks([0, 1, 2], 'left')
  792. tm.assert_numpy_array_equal(actual, expected)
  793. actual = self.index == self.index.values
  794. tm.assert_numpy_array_equal(actual, np.array([True, True]))
  795. actual = self.index.values == self.index
  796. tm.assert_numpy_array_equal(actual, np.array([True, True]))
  797. actual = self.index <= self.index.values
  798. tm.assert_numpy_array_equal(actual, np.array([True, True]))
  799. actual = self.index != self.index.values
  800. tm.assert_numpy_array_equal(actual, np.array([False, False]))
  801. actual = self.index > self.index.values
  802. tm.assert_numpy_array_equal(actual, np.array([False, False]))
  803. actual = self.index.values > self.index
  804. tm.assert_numpy_array_equal(actual, np.array([False, False]))
  805. # invalid comparisons
  806. actual = self.index == 0
  807. tm.assert_numpy_array_equal(actual, np.array([False, False]))
  808. actual = self.index == self.index.left
  809. tm.assert_numpy_array_equal(actual, np.array([False, False]))
  810. with pytest.raises(TypeError, match='unorderable types'):
  811. self.index > 0
  812. with pytest.raises(TypeError, match='unorderable types'):
  813. self.index <= 0
  814. with pytest.raises(TypeError):
  815. self.index > np.arange(2)
  816. with pytest.raises(ValueError):
  817. self.index > np.arange(3)
  818. def test_missing_values(self, closed):
  819. idx = Index([np.nan, Interval(0, 1, closed=closed),
  820. Interval(1, 2, closed=closed)])
  821. idx2 = IntervalIndex.from_arrays(
  822. [np.nan, 0, 1], [np.nan, 1, 2], closed=closed)
  823. assert idx.equals(idx2)
  824. with pytest.raises(ValueError):
  825. IntervalIndex.from_arrays(
  826. [np.nan, 0, 1], np.array([0, 1, 2]), closed=closed)
  827. tm.assert_numpy_array_equal(isna(idx),
  828. np.array([True, False, False]))
  829. def test_sort_values(self, closed):
  830. index = self.create_index(closed=closed)
  831. result = index.sort_values()
  832. tm.assert_index_equal(result, index)
  833. result = index.sort_values(ascending=False)
  834. tm.assert_index_equal(result, index[::-1])
  835. # with nan
  836. index = IntervalIndex([Interval(1, 2), np.nan, Interval(0, 1)])
  837. result = index.sort_values()
  838. expected = IntervalIndex([Interval(0, 1), Interval(1, 2), np.nan])
  839. tm.assert_index_equal(result, expected)
  840. result = index.sort_values(ascending=False)
  841. expected = IntervalIndex([np.nan, Interval(1, 2), Interval(0, 1)])
  842. tm.assert_index_equal(result, expected)
  843. @pytest.mark.parametrize('tz', [None, 'US/Eastern'])
  844. def test_datetime(self, tz):
  845. start = Timestamp('2000-01-01', tz=tz)
  846. dates = date_range(start=start, periods=10)
  847. index = IntervalIndex.from_breaks(dates)
  848. # test mid
  849. start = Timestamp('2000-01-01T12:00', tz=tz)
  850. expected = date_range(start=start, periods=9)
  851. tm.assert_index_equal(index.mid, expected)
  852. # __contains__ doesn't check individual points
  853. assert Timestamp('2000-01-01', tz=tz) not in index
  854. assert Timestamp('2000-01-01T12', tz=tz) not in index
  855. assert Timestamp('2000-01-02', tz=tz) not in index
  856. iv_true = Interval(Timestamp('2000-01-01T08', tz=tz),
  857. Timestamp('2000-01-01T18', tz=tz))
  858. iv_false = Interval(Timestamp('1999-12-31', tz=tz),
  859. Timestamp('2000-01-01', tz=tz))
  860. assert iv_true in index
  861. assert iv_false not in index
  862. # .contains does check individual points
  863. assert not index.contains(Timestamp('2000-01-01', tz=tz))
  864. assert index.contains(Timestamp('2000-01-01T12', tz=tz))
  865. assert index.contains(Timestamp('2000-01-02', tz=tz))
  866. assert index.contains(iv_true)
  867. assert not index.contains(iv_false)
  868. # test get_indexer
  869. start = Timestamp('1999-12-31T12:00', tz=tz)
  870. target = date_range(start=start, periods=7, freq='12H')
  871. actual = index.get_indexer(target)
  872. expected = np.array([-1, -1, 0, 0, 1, 1, 2], dtype='intp')
  873. tm.assert_numpy_array_equal(actual, expected)
  874. start = Timestamp('2000-01-08T18:00', tz=tz)
  875. target = date_range(start=start, periods=7, freq='6H')
  876. actual = index.get_indexer(target)
  877. expected = np.array([7, 7, 8, 8, 8, 8, -1], dtype='intp')
  878. tm.assert_numpy_array_equal(actual, expected)
  879. def test_append(self, closed):
  880. index1 = IntervalIndex.from_arrays([0, 1], [1, 2], closed=closed)
  881. index2 = IntervalIndex.from_arrays([1, 2], [2, 3], closed=closed)
  882. result = index1.append(index2)
  883. expected = IntervalIndex.from_arrays(
  884. [0, 1, 1, 2], [1, 2, 2, 3], closed=closed)
  885. tm.assert_index_equal(result, expected)
  886. result = index1.append([index1, index2])
  887. expected = IntervalIndex.from_arrays(
  888. [0, 1, 0, 1, 1, 2], [1, 2, 1, 2, 2, 3], closed=closed)
  889. tm.assert_index_equal(result, expected)
  890. msg = ('can only append two IntervalIndex objects that are closed '
  891. 'on the same side')
  892. for other_closed in {'left', 'right', 'both', 'neither'} - {closed}:
  893. index_other_closed = IntervalIndex.from_arrays(
  894. [0, 1], [1, 2], closed=other_closed)
  895. with pytest.raises(ValueError, match=msg):
  896. index1.append(index_other_closed)
  897. def test_is_non_overlapping_monotonic(self, closed):
  898. # Should be True in all cases
  899. tpls = [(0, 1), (2, 3), (4, 5), (6, 7)]
  900. idx = IntervalIndex.from_tuples(tpls, closed=closed)
  901. assert idx.is_non_overlapping_monotonic is True
  902. idx = IntervalIndex.from_tuples(tpls[::-1], closed=closed)
  903. assert idx.is_non_overlapping_monotonic is True
  904. # Should be False in all cases (overlapping)
  905. tpls = [(0, 2), (1, 3), (4, 5), (6, 7)]
  906. idx = IntervalIndex.from_tuples(tpls, closed=closed)
  907. assert idx.is_non_overlapping_monotonic is False
  908. idx = IntervalIndex.from_tuples(tpls[::-1], closed=closed)
  909. assert idx.is_non_overlapping_monotonic is False
  910. # Should be False in all cases (non-monotonic)
  911. tpls = [(0, 1), (2, 3), (6, 7), (4, 5)]
  912. idx = IntervalIndex.from_tuples(tpls, closed=closed)
  913. assert idx.is_non_overlapping_monotonic is False
  914. idx = IntervalIndex.from_tuples(tpls[::-1], closed=closed)
  915. assert idx.is_non_overlapping_monotonic is False
  916. # Should be False for closed='both', otherwise True (GH16560)
  917. if closed == 'both':
  918. idx = IntervalIndex.from_breaks(range(4), closed=closed)
  919. assert idx.is_non_overlapping_monotonic is False
  920. else:
  921. idx = IntervalIndex.from_breaks(range(4), closed=closed)
  922. assert idx.is_non_overlapping_monotonic is True
  923. @pytest.mark.parametrize('start, shift, na_value', [
  924. (0, 1, np.nan),
  925. (Timestamp('2018-01-01'), Timedelta('1 day'), pd.NaT),
  926. (Timedelta('0 days'), Timedelta('1 day'), pd.NaT)])
  927. def test_is_overlapping(self, start, shift, na_value, closed):
  928. # GH 23309
  929. # see test_interval_tree.py for extensive tests; interface tests here
  930. # non-overlapping
  931. tuples = [(start + n * shift, start + (n + 1) * shift)
  932. for n in (0, 2, 4)]
  933. index = IntervalIndex.from_tuples(tuples, closed=closed)
  934. assert index.is_overlapping is False
  935. # non-overlapping with NA
  936. tuples = [(na_value, na_value)] + tuples + [(na_value, na_value)]
  937. index = IntervalIndex.from_tuples(tuples, closed=closed)
  938. assert index.is_overlapping is False
  939. # overlapping
  940. tuples = [(start + n * shift, start + (n + 2) * shift)
  941. for n in range(3)]
  942. index = IntervalIndex.from_tuples(tuples, closed=closed)
  943. assert index.is_overlapping is True
  944. # overlapping with NA
  945. tuples = [(na_value, na_value)] + tuples + [(na_value, na_value)]
  946. index = IntervalIndex.from_tuples(tuples, closed=closed)
  947. assert index.is_overlapping is True
  948. # common endpoints
  949. tuples = [(start + n * shift, start + (n + 1) * shift)
  950. for n in range(3)]
  951. index = IntervalIndex.from_tuples(tuples, closed=closed)
  952. result = index.is_overlapping
  953. expected = closed == 'both'
  954. assert result is expected
  955. # common endpoints with NA
  956. tuples = [(na_value, na_value)] + tuples + [(na_value, na_value)]
  957. index = IntervalIndex.from_tuples(tuples, closed=closed)
  958. result = index.is_overlapping
  959. assert result is expected
  960. @pytest.mark.parametrize('tuples', [
  961. lzip(range(10), range(1, 11)),
  962. lzip(date_range('20170101', periods=10),
  963. date_range('20170101', periods=10)),
  964. lzip(timedelta_range('0 days', periods=10),
  965. timedelta_range('1 day', periods=10))])
  966. def test_to_tuples(self, tuples):
  967. # GH 18756
  968. idx = IntervalIndex.from_tuples(tuples)
  969. result = idx.to_tuples()
  970. expected = Index(com.asarray_tuplesafe(tuples))
  971. tm.assert_index_equal(result, expected)
  972. @pytest.mark.parametrize('tuples', [
  973. lzip(range(10), range(1, 11)) + [np.nan],
  974. lzip(date_range('20170101', periods=10),
  975. date_range('20170101', periods=10)) + [np.nan],
  976. lzip(timedelta_range('0 days', periods=10),
  977. timedelta_range('1 day', periods=10)) + [np.nan]])
  978. @pytest.mark.parametrize('na_tuple', [True, False])
  979. def test_to_tuples_na(self, tuples, na_tuple):
  980. # GH 18756
  981. idx = IntervalIndex.from_tuples(tuples)
  982. result = idx.to_tuples(na_tuple=na_tuple)
  983. # check the non-NA portion
  984. expected_notna = Index(com.asarray_tuplesafe(tuples[:-1]))
  985. result_notna = result[:-1]
  986. tm.assert_index_equal(result_notna, expected_notna)
  987. # check the NA portion
  988. result_na = result[-1]
  989. if na_tuple:
  990. assert isinstance(result_na, tuple)
  991. assert len(result_na) == 2
  992. assert all(isna(x) for x in result_na)
  993. else:
  994. assert isna(result_na)
  995. def test_nbytes(self):
  996. # GH 19209
  997. left = np.arange(0, 4, dtype='i8')
  998. right = np.arange(1, 5, dtype='i8')
  999. result = IntervalIndex.from_arrays(left, right).nbytes
  1000. expected = 64 # 4 * 8 * 2
  1001. assert result == expected
  1002. def test_itemsize(self):
  1003. # GH 19209
  1004. left = np.arange(0, 4, dtype='i8')
  1005. right = np.arange(1, 5, dtype='i8')
  1006. expected = 16 # 8 * 2
  1007. with tm.assert_produces_warning(FutureWarning, check_stacklevel=False):
  1008. result = IntervalIndex.from_arrays(left, right).itemsize
  1009. assert result == expected
  1010. @pytest.mark.parametrize('new_closed', [
  1011. 'left', 'right', 'both', 'neither'])
  1012. def test_set_closed(self, name, closed, new_closed):
  1013. # GH 21670
  1014. index = interval_range(0, 5, closed=closed, name=name)
  1015. result = index.set_closed(new_closed)
  1016. expected = interval_range(0, 5, closed=new_closed, name=name)
  1017. tm.assert_index_equal(result, expected)
  1018. @pytest.mark.parametrize('bad_closed', ['foo', 10, 'LEFT', True, False])
  1019. def test_set_closed_errors(self, bad_closed):
  1020. # GH 21670
  1021. index = interval_range(0, 5)
  1022. msg = "invalid option for 'closed': {closed}".format(closed=bad_closed)
  1023. with pytest.raises(ValueError, match=msg):
  1024. index.set_closed(bad_closed)
  1025. def test_is_all_dates(self):
  1026. # GH 23576
  1027. year_2017 = pd.Interval(pd.Timestamp('2017-01-01 00:00:00'),
  1028. pd.Timestamp('2018-01-01 00:00:00'))
  1029. year_2017_index = pd.IntervalIndex([year_2017])
  1030. assert not year_2017_index.is_all_dates