test_names.py 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. import pytest
  2. import pandas as pd
  3. from pandas import MultiIndex
  4. import pandas._testing as tm
  5. def check_level_names(index, names):
  6. assert [level.name for level in index.levels] == list(names)
  7. def test_slice_keep_name():
  8. x = MultiIndex.from_tuples([("a", "b"), (1, 2), ("c", "d")], names=["x", "y"])
  9. assert x[1:].names == x.names
  10. def test_index_name_retained():
  11. # GH9857
  12. result = pd.DataFrame({"x": [1, 2, 6], "y": [2, 2, 8], "z": [-5, 0, 5]})
  13. result = result.set_index("z")
  14. result.loc[10] = [9, 10]
  15. df_expected = pd.DataFrame(
  16. {"x": [1, 2, 6, 9], "y": [2, 2, 8, 10], "z": [-5, 0, 5, 10]}
  17. )
  18. df_expected = df_expected.set_index("z")
  19. tm.assert_frame_equal(result, df_expected)
  20. def test_changing_names(idx):
  21. assert [level.name for level in idx.levels] == ["first", "second"]
  22. view = idx.view()
  23. copy = idx.copy()
  24. shallow_copy = idx._view()
  25. # changing names should not change level names on object
  26. new_names = [name + "a" for name in idx.names]
  27. idx.names = new_names
  28. check_level_names(idx, ["firsta", "seconda"])
  29. # and not on copies
  30. check_level_names(view, ["first", "second"])
  31. check_level_names(copy, ["first", "second"])
  32. check_level_names(shallow_copy, ["first", "second"])
  33. # and copies shouldn't change original
  34. shallow_copy.names = [name + "c" for name in shallow_copy.names]
  35. check_level_names(idx, ["firsta", "seconda"])
  36. def test_take_preserve_name(idx):
  37. taken = idx.take([3, 0, 1])
  38. assert taken.names == idx.names
  39. def test_copy_names():
  40. # Check that adding a "names" parameter to the copy is honored
  41. # GH14302
  42. multi_idx = MultiIndex.from_tuples([(1, 2), (3, 4)], names=["MyName1", "MyName2"])
  43. multi_idx1 = multi_idx.copy()
  44. assert multi_idx.equals(multi_idx1)
  45. assert multi_idx.names == ["MyName1", "MyName2"]
  46. assert multi_idx1.names == ["MyName1", "MyName2"]
  47. multi_idx2 = multi_idx.copy(names=["NewName1", "NewName2"])
  48. assert multi_idx.equals(multi_idx2)
  49. assert multi_idx.names == ["MyName1", "MyName2"]
  50. assert multi_idx2.names == ["NewName1", "NewName2"]
  51. multi_idx3 = multi_idx.copy(name=["NewName1", "NewName2"])
  52. assert multi_idx.equals(multi_idx3)
  53. assert multi_idx.names == ["MyName1", "MyName2"]
  54. assert multi_idx3.names == ["NewName1", "NewName2"]
  55. # gh-35592
  56. with pytest.raises(ValueError, match="Length of new names must be 2, got 1"):
  57. multi_idx.copy(names=["mario"])
  58. with pytest.raises(TypeError, match="MultiIndex.name must be a hashable type"):
  59. multi_idx.copy(names=[["mario"], ["luigi"]])
  60. def test_names(idx, index_names):
  61. # names are assigned in setup
  62. assert index_names == ["first", "second"]
  63. level_names = [level.name for level in idx.levels]
  64. assert level_names == index_names
  65. # setting bad names on existing
  66. index = idx
  67. with pytest.raises(ValueError, match="^Length of names"):
  68. setattr(index, "names", list(index.names) + ["third"])
  69. with pytest.raises(ValueError, match="^Length of names"):
  70. setattr(index, "names", [])
  71. # initializing with bad names (should always be equivalent)
  72. major_axis, minor_axis = idx.levels
  73. major_codes, minor_codes = idx.codes
  74. with pytest.raises(ValueError, match="^Length of names"):
  75. MultiIndex(
  76. levels=[major_axis, minor_axis],
  77. codes=[major_codes, minor_codes],
  78. names=["first"],
  79. )
  80. with pytest.raises(ValueError, match="^Length of names"):
  81. MultiIndex(
  82. levels=[major_axis, minor_axis],
  83. codes=[major_codes, minor_codes],
  84. names=["first", "second", "third"],
  85. )
  86. # names are assigned on index, but not transferred to the levels
  87. index.names = ["a", "b"]
  88. level_names = [level.name for level in index.levels]
  89. assert level_names == ["a", "b"]
  90. def test_duplicate_level_names_access_raises(idx):
  91. # GH19029
  92. idx.names = ["foo", "foo"]
  93. with pytest.raises(ValueError, match="name foo occurs multiple times"):
  94. idx._get_level_number("foo")
  95. def test_get_names_from_levels():
  96. idx = MultiIndex.from_product([["a"], [1, 2]], names=["a", "b"])
  97. assert idx.levels[0].name == "a"
  98. assert idx.levels[1].name == "b"
  99. def test_setting_names_from_levels_raises():
  100. idx = MultiIndex.from_product([["a"], [1, 2]], names=["a", "b"])
  101. with pytest.raises(RuntimeError, match="set_names"):
  102. idx.levels[0].name = "foo"
  103. with pytest.raises(RuntimeError, match="set_names"):
  104. idx.levels[1].name = "foo"
  105. new = pd.Series(1, index=idx.levels[0])
  106. with pytest.raises(RuntimeError, match="set_names"):
  107. new.index.name = "bar"
  108. assert pd.Index._no_setting_name is False
  109. assert pd.RangeIndex._no_setting_name is False
  110. @pytest.mark.parametrize("func", ["rename", "set_names"])
  111. @pytest.mark.parametrize(
  112. "rename_dict, exp_names",
  113. [
  114. ({"x": "z"}, ["z", "y", "z"]),
  115. ({"x": "z", "y": "x"}, ["z", "x", "z"]),
  116. ({"y": "z"}, ["x", "z", "x"]),
  117. ({}, ["x", "y", "x"]),
  118. ({"z": "a"}, ["x", "y", "x"]),
  119. ({"y": "z", "a": "b"}, ["x", "z", "x"]),
  120. ],
  121. )
  122. def test_name_mi_with_dict_like_duplicate_names(func, rename_dict, exp_names):
  123. # GH#20421
  124. mi = MultiIndex.from_arrays([[1, 2], [3, 4], [5, 6]], names=["x", "y", "x"])
  125. result = getattr(mi, func)(rename_dict)
  126. expected = MultiIndex.from_arrays([[1, 2], [3, 4], [5, 6]], names=exp_names)
  127. tm.assert_index_equal(result, expected)
  128. @pytest.mark.parametrize("func", ["rename", "set_names"])
  129. @pytest.mark.parametrize(
  130. "rename_dict, exp_names",
  131. [
  132. ({"x": "z"}, ["z", "y"]),
  133. ({"x": "z", "y": "x"}, ["z", "x"]),
  134. ({"a": "z"}, ["x", "y"]),
  135. ({}, ["x", "y"]),
  136. ],
  137. )
  138. def test_name_mi_with_dict_like(func, rename_dict, exp_names):
  139. # GH#20421
  140. mi = MultiIndex.from_arrays([[1, 2], [3, 4]], names=["x", "y"])
  141. result = getattr(mi, func)(rename_dict)
  142. expected = MultiIndex.from_arrays([[1, 2], [3, 4]], names=exp_names)
  143. tm.assert_index_equal(result, expected)
  144. def test_index_name_with_dict_like_raising():
  145. # GH#20421
  146. ix = pd.Index([1, 2])
  147. msg = "Can only pass dict-like as `names` for MultiIndex."
  148. with pytest.raises(TypeError, match=msg):
  149. ix.set_names({"x": "z"})
  150. def test_multiindex_name_and_level_raising():
  151. # GH#20421
  152. mi = MultiIndex.from_arrays([[1, 2], [3, 4]], names=["x", "y"])
  153. with pytest.raises(TypeError, match="Can not pass level for dictlike `names`."):
  154. mi.set_names(names={"x": "z"}, level={"x": "z"})