test_get_set.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  1. # -*- coding: utf-8 -*-
  2. import numpy as np
  3. import pytest
  4. from pandas.compat import range
  5. import pandas as pd
  6. from pandas import CategoricalIndex, Index, MultiIndex
  7. import pandas.util.testing as tm
  8. def assert_matching(actual, expected, check_dtype=False):
  9. # avoid specifying internal representation
  10. # as much as possible
  11. assert len(actual) == len(expected)
  12. for act, exp in zip(actual, expected):
  13. act = np.asarray(act)
  14. exp = np.asarray(exp)
  15. tm.assert_numpy_array_equal(act, exp, check_dtype=check_dtype)
  16. def test_get_level_number_integer(idx):
  17. idx.names = [1, 0]
  18. assert idx._get_level_number(1) == 0
  19. assert idx._get_level_number(0) == 1
  20. pytest.raises(IndexError, idx._get_level_number, 2)
  21. with pytest.raises(KeyError, match='Level fourth not found'):
  22. idx._get_level_number('fourth')
  23. def test_get_level_values(idx):
  24. result = idx.get_level_values(0)
  25. expected = Index(['foo', 'foo', 'bar', 'baz', 'qux', 'qux'],
  26. name='first')
  27. tm.assert_index_equal(result, expected)
  28. assert result.name == 'first'
  29. result = idx.get_level_values('first')
  30. expected = idx.get_level_values(0)
  31. tm.assert_index_equal(result, expected)
  32. # GH 10460
  33. index = MultiIndex(
  34. levels=[CategoricalIndex(['A', 'B']),
  35. CategoricalIndex([1, 2, 3])],
  36. codes=[np.array([0, 0, 0, 1, 1, 1]),
  37. np.array([0, 1, 2, 0, 1, 2])])
  38. exp = CategoricalIndex(['A', 'A', 'A', 'B', 'B', 'B'])
  39. tm.assert_index_equal(index.get_level_values(0), exp)
  40. exp = CategoricalIndex([1, 2, 3, 1, 2, 3])
  41. tm.assert_index_equal(index.get_level_values(1), exp)
  42. def test_get_value_duplicates():
  43. index = MultiIndex(levels=[['D', 'B', 'C'],
  44. [0, 26, 27, 37, 57, 67, 75, 82]],
  45. codes=[[0, 0, 0, 1, 2, 2, 2, 2, 2, 2],
  46. [1, 3, 4, 6, 0, 2, 2, 3, 5, 7]],
  47. names=['tag', 'day'])
  48. assert index.get_loc('D') == slice(0, 3)
  49. with pytest.raises(KeyError):
  50. index._engine.get_value(np.array([]), 'D')
  51. def test_get_level_values_all_na():
  52. # GH 17924 when level entirely consists of nan
  53. arrays = [[np.nan, np.nan, np.nan], ['a', np.nan, 1]]
  54. index = pd.MultiIndex.from_arrays(arrays)
  55. result = index.get_level_values(0)
  56. expected = pd.Index([np.nan, np.nan, np.nan], dtype=np.float64)
  57. tm.assert_index_equal(result, expected)
  58. result = index.get_level_values(1)
  59. expected = pd.Index(['a', np.nan, 1], dtype=object)
  60. tm.assert_index_equal(result, expected)
  61. def test_get_level_values_int_with_na():
  62. # GH 17924
  63. arrays = [['a', 'b', 'b'], [1, np.nan, 2]]
  64. index = pd.MultiIndex.from_arrays(arrays)
  65. result = index.get_level_values(1)
  66. expected = Index([1, np.nan, 2])
  67. tm.assert_index_equal(result, expected)
  68. arrays = [['a', 'b', 'b'], [np.nan, np.nan, 2]]
  69. index = pd.MultiIndex.from_arrays(arrays)
  70. result = index.get_level_values(1)
  71. expected = Index([np.nan, np.nan, 2])
  72. tm.assert_index_equal(result, expected)
  73. def test_get_level_values_na():
  74. arrays = [[np.nan, np.nan, np.nan], ['a', np.nan, 1]]
  75. index = pd.MultiIndex.from_arrays(arrays)
  76. result = index.get_level_values(0)
  77. expected = pd.Index([np.nan, np.nan, np.nan])
  78. tm.assert_index_equal(result, expected)
  79. result = index.get_level_values(1)
  80. expected = pd.Index(['a', np.nan, 1])
  81. tm.assert_index_equal(result, expected)
  82. arrays = [['a', 'b', 'b'], pd.DatetimeIndex([0, 1, pd.NaT])]
  83. index = pd.MultiIndex.from_arrays(arrays)
  84. result = index.get_level_values(1)
  85. expected = pd.DatetimeIndex([0, 1, pd.NaT])
  86. tm.assert_index_equal(result, expected)
  87. arrays = [[], []]
  88. index = pd.MultiIndex.from_arrays(arrays)
  89. result = index.get_level_values(0)
  90. expected = pd.Index([], dtype=object)
  91. tm.assert_index_equal(result, expected)
  92. def test_set_name_methods(idx, index_names):
  93. # so long as these are synonyms, we don't need to test set_names
  94. assert idx.rename == idx.set_names
  95. new_names = [name + "SUFFIX" for name in index_names]
  96. ind = idx.set_names(new_names)
  97. assert idx.names == index_names
  98. assert ind.names == new_names
  99. with pytest.raises(ValueError, match="^Length"):
  100. ind.set_names(new_names + new_names)
  101. new_names2 = [name + "SUFFIX2" for name in new_names]
  102. res = ind.set_names(new_names2, inplace=True)
  103. assert res is None
  104. assert ind.names == new_names2
  105. # set names for specific level (# GH7792)
  106. ind = idx.set_names(new_names[0], level=0)
  107. assert idx.names == index_names
  108. assert ind.names == [new_names[0], index_names[1]]
  109. res = ind.set_names(new_names2[0], level=0, inplace=True)
  110. assert res is None
  111. assert ind.names == [new_names2[0], index_names[1]]
  112. # set names for multiple levels
  113. ind = idx.set_names(new_names, level=[0, 1])
  114. assert idx.names == index_names
  115. assert ind.names == new_names
  116. res = ind.set_names(new_names2, level=[0, 1], inplace=True)
  117. assert res is None
  118. assert ind.names == new_names2
  119. def test_set_levels_codes_directly(idx):
  120. # setting levels/codes directly raises AttributeError
  121. levels = idx.levels
  122. new_levels = [[lev + 'a' for lev in level] for level in levels]
  123. codes = idx.codes
  124. major_codes, minor_codes = codes
  125. major_codes = [(x + 1) % 3 for x in major_codes]
  126. minor_codes = [(x + 1) % 1 for x in minor_codes]
  127. new_codes = [major_codes, minor_codes]
  128. with pytest.raises(AttributeError):
  129. idx.levels = new_levels
  130. with pytest.raises(AttributeError):
  131. idx.codes = new_codes
  132. def test_set_levels(idx):
  133. # side note - you probably wouldn't want to use levels and codes
  134. # directly like this - but it is possible.
  135. levels = idx.levels
  136. new_levels = [[lev + 'a' for lev in level] for level in levels]
  137. # level changing [w/o mutation]
  138. ind2 = idx.set_levels(new_levels)
  139. assert_matching(ind2.levels, new_levels)
  140. assert_matching(idx.levels, levels)
  141. # level changing [w/ mutation]
  142. ind2 = idx.copy()
  143. inplace_return = ind2.set_levels(new_levels, inplace=True)
  144. assert inplace_return is None
  145. assert_matching(ind2.levels, new_levels)
  146. # level changing specific level [w/o mutation]
  147. ind2 = idx.set_levels(new_levels[0], level=0)
  148. assert_matching(ind2.levels, [new_levels[0], levels[1]])
  149. assert_matching(idx.levels, levels)
  150. ind2 = idx.set_levels(new_levels[1], level=1)
  151. assert_matching(ind2.levels, [levels[0], new_levels[1]])
  152. assert_matching(idx.levels, levels)
  153. # level changing multiple levels [w/o mutation]
  154. ind2 = idx.set_levels(new_levels, level=[0, 1])
  155. assert_matching(ind2.levels, new_levels)
  156. assert_matching(idx.levels, levels)
  157. # level changing specific level [w/ mutation]
  158. ind2 = idx.copy()
  159. inplace_return = ind2.set_levels(new_levels[0], level=0, inplace=True)
  160. assert inplace_return is None
  161. assert_matching(ind2.levels, [new_levels[0], levels[1]])
  162. assert_matching(idx.levels, levels)
  163. ind2 = idx.copy()
  164. inplace_return = ind2.set_levels(new_levels[1], level=1, inplace=True)
  165. assert inplace_return is None
  166. assert_matching(ind2.levels, [levels[0], new_levels[1]])
  167. assert_matching(idx.levels, levels)
  168. # level changing multiple levels [w/ mutation]
  169. ind2 = idx.copy()
  170. inplace_return = ind2.set_levels(new_levels, level=[0, 1],
  171. inplace=True)
  172. assert inplace_return is None
  173. assert_matching(ind2.levels, new_levels)
  174. assert_matching(idx.levels, levels)
  175. # illegal level changing should not change levels
  176. # GH 13754
  177. original_index = idx.copy()
  178. for inplace in [True, False]:
  179. with pytest.raises(ValueError, match="^On"):
  180. idx.set_levels(['c'], level=0, inplace=inplace)
  181. assert_matching(idx.levels, original_index.levels,
  182. check_dtype=True)
  183. with pytest.raises(ValueError, match="^On"):
  184. idx.set_codes([0, 1, 2, 3, 4, 5], level=0,
  185. inplace=inplace)
  186. assert_matching(idx.codes, original_index.codes,
  187. check_dtype=True)
  188. with pytest.raises(TypeError, match="^Levels"):
  189. idx.set_levels('c', level=0, inplace=inplace)
  190. assert_matching(idx.levels, original_index.levels,
  191. check_dtype=True)
  192. with pytest.raises(TypeError, match="^Codes"):
  193. idx.set_codes(1, level=0, inplace=inplace)
  194. assert_matching(idx.codes, original_index.codes,
  195. check_dtype=True)
  196. def test_set_codes(idx):
  197. # side note - you probably wouldn't want to use levels and codes
  198. # directly like this - but it is possible.
  199. codes = idx.codes
  200. major_codes, minor_codes = codes
  201. major_codes = [(x + 1) % 3 for x in major_codes]
  202. minor_codes = [(x + 1) % 1 for x in minor_codes]
  203. new_codes = [major_codes, minor_codes]
  204. # changing codes w/o mutation
  205. ind2 = idx.set_codes(new_codes)
  206. assert_matching(ind2.codes, new_codes)
  207. assert_matching(idx.codes, codes)
  208. # changing label w/ mutation
  209. ind2 = idx.copy()
  210. inplace_return = ind2.set_codes(new_codes, inplace=True)
  211. assert inplace_return is None
  212. assert_matching(ind2.codes, new_codes)
  213. # codes changing specific level w/o mutation
  214. ind2 = idx.set_codes(new_codes[0], level=0)
  215. assert_matching(ind2.codes, [new_codes[0], codes[1]])
  216. assert_matching(idx.codes, codes)
  217. ind2 = idx.set_codes(new_codes[1], level=1)
  218. assert_matching(ind2.codes, [codes[0], new_codes[1]])
  219. assert_matching(idx.codes, codes)
  220. # codes changing multiple levels w/o mutation
  221. ind2 = idx.set_codes(new_codes, level=[0, 1])
  222. assert_matching(ind2.codes, new_codes)
  223. assert_matching(idx.codes, codes)
  224. # label changing specific level w/ mutation
  225. ind2 = idx.copy()
  226. inplace_return = ind2.set_codes(new_codes[0], level=0, inplace=True)
  227. assert inplace_return is None
  228. assert_matching(ind2.codes, [new_codes[0], codes[1]])
  229. assert_matching(idx.codes, codes)
  230. ind2 = idx.copy()
  231. inplace_return = ind2.set_codes(new_codes[1], level=1, inplace=True)
  232. assert inplace_return is None
  233. assert_matching(ind2.codes, [codes[0], new_codes[1]])
  234. assert_matching(idx.codes, codes)
  235. # codes changing multiple levels [w/ mutation]
  236. ind2 = idx.copy()
  237. inplace_return = ind2.set_codes(new_codes, level=[0, 1],
  238. inplace=True)
  239. assert inplace_return is None
  240. assert_matching(ind2.codes, new_codes)
  241. assert_matching(idx.codes, codes)
  242. # label changing for levels of different magnitude of categories
  243. ind = pd.MultiIndex.from_tuples([(0, i) for i in range(130)])
  244. new_codes = range(129, -1, -1)
  245. expected = pd.MultiIndex.from_tuples(
  246. [(0, i) for i in new_codes])
  247. # [w/o mutation]
  248. result = ind.set_codes(codes=new_codes, level=1)
  249. assert result.equals(expected)
  250. # [w/ mutation]
  251. result = ind.copy()
  252. result.set_codes(codes=new_codes, level=1, inplace=True)
  253. assert result.equals(expected)
  254. with tm.assert_produces_warning(FutureWarning):
  255. ind.set_codes(labels=new_codes, level=1)
  256. def test_set_labels_deprecated():
  257. # GH23752
  258. ind = pd.MultiIndex.from_tuples([(0, i) for i in range(130)])
  259. new_labels = range(129, -1, -1)
  260. expected = pd.MultiIndex.from_tuples(
  261. [(0, i) for i in new_labels])
  262. # [w/o mutation]
  263. with tm.assert_produces_warning(FutureWarning):
  264. result = ind.set_labels(labels=new_labels, level=1)
  265. assert result.equals(expected)
  266. # [w/ mutation]
  267. result = ind.copy()
  268. with tm.assert_produces_warning(FutureWarning):
  269. result.set_labels(labels=new_labels, level=1, inplace=True)
  270. assert result.equals(expected)
  271. def test_set_levels_codes_names_bad_input(idx):
  272. levels, codes = idx.levels, idx.codes
  273. names = idx.names
  274. with pytest.raises(ValueError, match='Length of levels'):
  275. idx.set_levels([levels[0]])
  276. with pytest.raises(ValueError, match='Length of codes'):
  277. idx.set_codes([codes[0]])
  278. with pytest.raises(ValueError, match='Length of names'):
  279. idx.set_names([names[0]])
  280. # shouldn't scalar data error, instead should demand list-like
  281. with pytest.raises(TypeError, match='list of lists-like'):
  282. idx.set_levels(levels[0])
  283. # shouldn't scalar data error, instead should demand list-like
  284. with pytest.raises(TypeError, match='list of lists-like'):
  285. idx.set_codes(codes[0])
  286. # shouldn't scalar data error, instead should demand list-like
  287. with pytest.raises(TypeError, match='list-like'):
  288. idx.set_names(names[0])
  289. # should have equal lengths
  290. with pytest.raises(TypeError, match='list of lists-like'):
  291. idx.set_levels(levels[0], level=[0, 1])
  292. with pytest.raises(TypeError, match='list-like'):
  293. idx.set_levels(levels, level=0)
  294. # should have equal lengths
  295. with pytest.raises(TypeError, match='list of lists-like'):
  296. idx.set_codes(codes[0], level=[0, 1])
  297. with pytest.raises(TypeError, match='list-like'):
  298. idx.set_codes(codes, level=0)
  299. # should have equal lengths
  300. with pytest.raises(ValueError, match='Length of names'):
  301. idx.set_names(names[0], level=[0, 1])
  302. with pytest.raises(TypeError, match='Names must be a'):
  303. idx.set_names(names, level=0)
  304. @pytest.mark.parametrize('inplace', [True, False])
  305. def test_set_names_with_nlevel_1(inplace):
  306. # GH 21149
  307. # Ensure that .set_names for MultiIndex with
  308. # nlevels == 1 does not raise any errors
  309. expected = pd.MultiIndex(levels=[[0, 1]],
  310. codes=[[0, 1]],
  311. names=['first'])
  312. m = pd.MultiIndex.from_product([[0, 1]])
  313. result = m.set_names('first', level=0, inplace=inplace)
  314. if inplace:
  315. result = m
  316. tm.assert_index_equal(result, expected)
  317. @pytest.mark.parametrize('ordered', [True, False])
  318. def test_set_levels_categorical(ordered):
  319. # GH13854
  320. index = MultiIndex.from_arrays([list("xyzx"), [0, 1, 2, 3]])
  321. cidx = CategoricalIndex(list("bac"), ordered=ordered)
  322. result = index.set_levels(cidx, 0)
  323. expected = MultiIndex(levels=[cidx, [0, 1, 2, 3]],
  324. codes=index.codes)
  325. tm.assert_index_equal(result, expected)
  326. result_lvl = result.get_level_values(0)
  327. expected_lvl = CategoricalIndex(list("bacb"),
  328. categories=cidx.categories,
  329. ordered=cidx.ordered)
  330. tm.assert_index_equal(result_lvl, expected_lvl)
  331. def test_set_value_keeps_names():
  332. # motivating example from #3742
  333. lev1 = ['hans', 'hans', 'hans', 'grethe', 'grethe', 'grethe']
  334. lev2 = ['1', '2', '3'] * 2
  335. idx = pd.MultiIndex.from_arrays([lev1, lev2], names=['Name', 'Number'])
  336. df = pd.DataFrame(
  337. np.random.randn(6, 4),
  338. columns=['one', 'two', 'three', 'four'],
  339. index=idx)
  340. df = df.sort_index()
  341. assert df._is_copy is None
  342. assert df.index.names == ('Name', 'Number')
  343. df.at[('grethe', '4'), 'one'] = 99.34
  344. assert df._is_copy is None
  345. assert df.index.names == ('Name', 'Number')
  346. def test_set_levels_with_iterable():
  347. # GH23273
  348. sizes = [1, 2, 3]
  349. colors = ['black'] * 3
  350. index = pd.MultiIndex.from_arrays([sizes, colors], names=['size', 'color'])
  351. result = index.set_levels(map(int, ['3', '2', '1']), level='size')
  352. expected_sizes = [3, 2, 1]
  353. expected = pd.MultiIndex.from_arrays([expected_sizes, colors],
  354. names=['size', 'color'])
  355. tm.assert_index_equal(result, expected)