test_multilevel.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. import numpy as np
  2. import pytest
  3. import pandas as pd
  4. from pandas import (
  5. DataFrame,
  6. MultiIndex,
  7. Series,
  8. )
  9. import pandas._testing as tm
  10. AGG_FUNCTIONS = [
  11. "sum",
  12. "prod",
  13. "min",
  14. "max",
  15. "median",
  16. "mean",
  17. "skew",
  18. "mad",
  19. "std",
  20. "var",
  21. "sem",
  22. ]
  23. class TestMultiLevel:
  24. def test_reindex_level(self, multiindex_year_month_day_dataframe_random_data):
  25. # axis=0
  26. ymd = multiindex_year_month_day_dataframe_random_data
  27. with tm.assert_produces_warning(FutureWarning):
  28. month_sums = ymd.sum(level="month")
  29. result = month_sums.reindex(ymd.index, level=1)
  30. expected = ymd.groupby(level="month").transform(np.sum)
  31. tm.assert_frame_equal(result, expected)
  32. # Series
  33. result = month_sums["A"].reindex(ymd.index, level=1)
  34. expected = ymd["A"].groupby(level="month").transform(np.sum)
  35. tm.assert_series_equal(result, expected, check_names=False)
  36. # axis=1
  37. with tm.assert_produces_warning(FutureWarning):
  38. month_sums = ymd.T.sum(axis=1, level="month")
  39. result = month_sums.reindex(columns=ymd.index, level=1)
  40. expected = ymd.groupby(level="month").transform(np.sum).T
  41. tm.assert_frame_equal(result, expected)
  42. def test_binops_level(self, multiindex_year_month_day_dataframe_random_data):
  43. ymd = multiindex_year_month_day_dataframe_random_data
  44. def _check_op(opname):
  45. op = getattr(DataFrame, opname)
  46. with tm.assert_produces_warning(FutureWarning):
  47. month_sums = ymd.sum(level="month")
  48. result = op(ymd, month_sums, level="month")
  49. broadcasted = ymd.groupby(level="month").transform(np.sum)
  50. expected = op(ymd, broadcasted)
  51. tm.assert_frame_equal(result, expected)
  52. # Series
  53. op = getattr(Series, opname)
  54. result = op(ymd["A"], month_sums["A"], level="month")
  55. broadcasted = ymd["A"].groupby(level="month").transform(np.sum)
  56. expected = op(ymd["A"], broadcasted)
  57. expected.name = "A"
  58. tm.assert_series_equal(result, expected)
  59. _check_op("sub")
  60. _check_op("add")
  61. _check_op("mul")
  62. _check_op("div")
  63. def test_reindex(self, multiindex_dataframe_random_data):
  64. frame = multiindex_dataframe_random_data
  65. expected = frame.iloc[[0, 3]]
  66. reindexed = frame.loc[[("foo", "one"), ("bar", "one")]]
  67. tm.assert_frame_equal(reindexed, expected)
  68. def test_reindex_preserve_levels(
  69. self, multiindex_year_month_day_dataframe_random_data
  70. ):
  71. ymd = multiindex_year_month_day_dataframe_random_data
  72. new_index = ymd.index[::10]
  73. chunk = ymd.reindex(new_index)
  74. assert chunk.index is new_index
  75. chunk = ymd.loc[new_index]
  76. assert chunk.index is new_index
  77. ymdT = ymd.T
  78. chunk = ymdT.reindex(columns=new_index)
  79. assert chunk.columns is new_index
  80. chunk = ymdT.loc[:, new_index]
  81. assert chunk.columns is new_index
  82. def test_groupby_transform(self, multiindex_dataframe_random_data):
  83. frame = multiindex_dataframe_random_data
  84. s = frame["A"]
  85. grouper = s.index.get_level_values(0)
  86. grouped = s.groupby(grouper)
  87. applied = grouped.apply(lambda x: x * 2)
  88. expected = grouped.transform(lambda x: x * 2)
  89. result = applied.reindex(expected.index)
  90. tm.assert_series_equal(result, expected, check_names=False)
  91. def test_groupby_corner(self):
  92. midx = MultiIndex(
  93. levels=[["foo"], ["bar"], ["baz"]],
  94. codes=[[0], [0], [0]],
  95. names=["one", "two", "three"],
  96. )
  97. df = DataFrame([np.random.rand(4)], columns=["a", "b", "c", "d"], index=midx)
  98. # should work
  99. df.groupby(level="three")
  100. def test_groupby_level_no_obs(self):
  101. # #1697
  102. midx = MultiIndex.from_tuples(
  103. [
  104. ("f1", "s1"),
  105. ("f1", "s2"),
  106. ("f2", "s1"),
  107. ("f2", "s2"),
  108. ("f3", "s1"),
  109. ("f3", "s2"),
  110. ]
  111. )
  112. df = DataFrame([[1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12]], columns=midx)
  113. df1 = df.loc(axis=1)[df.columns.map(lambda u: u[0] in ["f2", "f3"])]
  114. grouped = df1.groupby(axis=1, level=0)
  115. result = grouped.sum()
  116. assert (result.columns == ["f2", "f3"]).all()
  117. def test_setitem_with_expansion_multiindex_columns(
  118. self, multiindex_year_month_day_dataframe_random_data
  119. ):
  120. ymd = multiindex_year_month_day_dataframe_random_data
  121. df = ymd[:5].T
  122. df[2000, 1, 10] = df[2000, 1, 7]
  123. assert isinstance(df.columns, MultiIndex)
  124. assert (df[2000, 1, 10] == df[2000, 1, 7]).all()
  125. def test_alignment(self):
  126. x = Series(
  127. data=[1, 2, 3], index=MultiIndex.from_tuples([("A", 1), ("A", 2), ("B", 3)])
  128. )
  129. y = Series(
  130. data=[4, 5, 6], index=MultiIndex.from_tuples([("Z", 1), ("Z", 2), ("B", 3)])
  131. )
  132. res = x - y
  133. exp_index = x.index.union(y.index)
  134. exp = x.reindex(exp_index) - y.reindex(exp_index)
  135. tm.assert_series_equal(res, exp)
  136. # hit non-monotonic code path
  137. res = x[::-1] - y[::-1]
  138. exp_index = x.index.union(y.index)
  139. exp = x.reindex(exp_index) - y.reindex(exp_index)
  140. tm.assert_series_equal(res, exp)
  141. @pytest.mark.parametrize("op", AGG_FUNCTIONS)
  142. @pytest.mark.parametrize("level", [0, 1])
  143. @pytest.mark.parametrize("skipna", [True, False])
  144. @pytest.mark.parametrize("sort", [True, False])
  145. def test_series_group_min_max(
  146. self, op, level, skipna, sort, series_with_multilevel_index
  147. ):
  148. # GH 17537
  149. ser = series_with_multilevel_index
  150. grouped = ser.groupby(level=level, sort=sort)
  151. # skipna=True
  152. leftside = grouped.agg(lambda x: getattr(x, op)(skipna=skipna))
  153. with tm.assert_produces_warning(FutureWarning):
  154. rightside = getattr(ser, op)(level=level, skipna=skipna)
  155. if sort:
  156. rightside = rightside.sort_index(level=level)
  157. tm.assert_series_equal(leftside, rightside)
  158. @pytest.mark.parametrize("op", AGG_FUNCTIONS)
  159. @pytest.mark.parametrize("level", [0, 1])
  160. @pytest.mark.parametrize("axis", [0, 1])
  161. @pytest.mark.parametrize("skipna", [True, False])
  162. @pytest.mark.parametrize("sort", [True, False])
  163. def test_frame_group_ops(
  164. self, op, level, axis, skipna, sort, multiindex_dataframe_random_data
  165. ):
  166. # GH 17537
  167. frame = multiindex_dataframe_random_data
  168. frame.iloc[1, [1, 2]] = np.nan
  169. frame.iloc[7, [0, 1]] = np.nan
  170. level_name = frame.index.names[level]
  171. if axis == 0:
  172. frame = frame
  173. else:
  174. frame = frame.T
  175. grouped = frame.groupby(level=level, axis=axis, sort=sort)
  176. pieces = []
  177. def aggf(x):
  178. pieces.append(x)
  179. return getattr(x, op)(skipna=skipna, axis=axis)
  180. leftside = grouped.agg(aggf)
  181. with tm.assert_produces_warning(FutureWarning):
  182. rightside = getattr(frame, op)(level=level, axis=axis, skipna=skipna)
  183. if sort:
  184. rightside = rightside.sort_index(level=level, axis=axis)
  185. frame = frame.sort_index(level=level, axis=axis)
  186. # for good measure, groupby detail
  187. level_index = frame._get_axis(axis).levels[level].rename(level_name)
  188. tm.assert_index_equal(leftside._get_axis(axis), level_index)
  189. tm.assert_index_equal(rightside._get_axis(axis), level_index)
  190. tm.assert_frame_equal(leftside, rightside)
  191. def test_std_var_pass_ddof(self):
  192. index = MultiIndex.from_arrays(
  193. [np.arange(5).repeat(10), np.tile(np.arange(10), 5)]
  194. )
  195. df = DataFrame(np.random.randn(len(index), 5), index=index)
  196. for meth in ["var", "std"]:
  197. ddof = 4
  198. alt = lambda x: getattr(x, meth)(ddof=ddof)
  199. with tm.assert_produces_warning(FutureWarning):
  200. result = getattr(df[0], meth)(level=0, ddof=ddof)
  201. expected = df[0].groupby(level=0).agg(alt)
  202. tm.assert_series_equal(result, expected)
  203. with tm.assert_produces_warning(FutureWarning):
  204. result = getattr(df, meth)(level=0, ddof=ddof)
  205. expected = df.groupby(level=0).agg(alt)
  206. tm.assert_frame_equal(result, expected)
  207. def test_agg_multiple_levels(
  208. self, multiindex_year_month_day_dataframe_random_data, frame_or_series
  209. ):
  210. ymd = multiindex_year_month_day_dataframe_random_data
  211. if frame_or_series is Series:
  212. ymd = ymd["A"]
  213. with tm.assert_produces_warning(FutureWarning):
  214. result = ymd.sum(level=["year", "month"])
  215. expected = ymd.groupby(level=["year", "month"]).sum()
  216. tm.assert_equal(result, expected)
  217. def test_groupby_multilevel(self, multiindex_year_month_day_dataframe_random_data):
  218. ymd = multiindex_year_month_day_dataframe_random_data
  219. result = ymd.groupby(level=[0, 1]).mean()
  220. k1 = ymd.index.get_level_values(0)
  221. k2 = ymd.index.get_level_values(1)
  222. expected = ymd.groupby([k1, k2]).mean()
  223. # TODO groupby with level_values drops names
  224. tm.assert_frame_equal(result, expected, check_names=False)
  225. assert result.index.names == ymd.index.names[:2]
  226. result2 = ymd.groupby(level=ymd.index.names[:2]).mean()
  227. tm.assert_frame_equal(result, result2)
  228. def test_groupby_multilevel_with_transform(self):
  229. pass
  230. def test_multilevel_consolidate(self):
  231. index = MultiIndex.from_tuples(
  232. [("foo", "one"), ("foo", "two"), ("bar", "one"), ("bar", "two")]
  233. )
  234. df = DataFrame(np.random.randn(4, 4), index=index, columns=index)
  235. df["Totals", ""] = df.sum(1)
  236. df = df._consolidate()
  237. def test_level_with_tuples(self):
  238. index = MultiIndex(
  239. levels=[[("foo", "bar", 0), ("foo", "baz", 0), ("foo", "qux", 0)], [0, 1]],
  240. codes=[[0, 0, 1, 1, 2, 2], [0, 1, 0, 1, 0, 1]],
  241. )
  242. series = Series(np.random.randn(6), index=index)
  243. frame = DataFrame(np.random.randn(6, 4), index=index)
  244. result = series[("foo", "bar", 0)]
  245. result2 = series.loc[("foo", "bar", 0)]
  246. expected = series[:2]
  247. expected.index = expected.index.droplevel(0)
  248. tm.assert_series_equal(result, expected)
  249. tm.assert_series_equal(result2, expected)
  250. with pytest.raises(KeyError, match=r"^\(\('foo', 'bar', 0\), 2\)$"):
  251. series[("foo", "bar", 0), 2]
  252. result = frame.loc[("foo", "bar", 0)]
  253. result2 = frame.xs(("foo", "bar", 0))
  254. expected = frame[:2]
  255. expected.index = expected.index.droplevel(0)
  256. tm.assert_frame_equal(result, expected)
  257. tm.assert_frame_equal(result2, expected)
  258. index = MultiIndex(
  259. levels=[[("foo", "bar"), ("foo", "baz"), ("foo", "qux")], [0, 1]],
  260. codes=[[0, 0, 1, 1, 2, 2], [0, 1, 0, 1, 0, 1]],
  261. )
  262. series = Series(np.random.randn(6), index=index)
  263. frame = DataFrame(np.random.randn(6, 4), index=index)
  264. result = series[("foo", "bar")]
  265. result2 = series.loc[("foo", "bar")]
  266. expected = series[:2]
  267. expected.index = expected.index.droplevel(0)
  268. tm.assert_series_equal(result, expected)
  269. tm.assert_series_equal(result2, expected)
  270. result = frame.loc[("foo", "bar")]
  271. result2 = frame.xs(("foo", "bar"))
  272. expected = frame[:2]
  273. expected.index = expected.index.droplevel(0)
  274. tm.assert_frame_equal(result, expected)
  275. tm.assert_frame_equal(result2, expected)
  276. def test_reindex_level_partial_selection(self, multiindex_dataframe_random_data):
  277. frame = multiindex_dataframe_random_data
  278. result = frame.reindex(["foo", "qux"], level=0)
  279. expected = frame.iloc[[0, 1, 2, 7, 8, 9]]
  280. tm.assert_frame_equal(result, expected)
  281. result = frame.T.reindex(["foo", "qux"], axis=1, level=0)
  282. tm.assert_frame_equal(result, expected.T)
  283. result = frame.loc[["foo", "qux"]]
  284. tm.assert_frame_equal(result, expected)
  285. result = frame["A"].loc[["foo", "qux"]]
  286. tm.assert_series_equal(result, expected["A"])
  287. result = frame.T.loc[:, ["foo", "qux"]]
  288. tm.assert_frame_equal(result, expected.T)
  289. @pytest.mark.parametrize("d", [4, "d"])
  290. def test_empty_frame_groupby_dtypes_consistency(self, d):
  291. # GH 20888
  292. group_keys = ["a", "b", "c"]
  293. df = DataFrame({"a": [1], "b": [2], "c": [3], "d": [d]})
  294. g = df[df.a == 2].groupby(group_keys)
  295. result = g.first().index
  296. expected = MultiIndex(
  297. levels=[[1], [2], [3]], codes=[[], [], []], names=["a", "b", "c"]
  298. )
  299. tm.assert_index_equal(result, expected)
  300. def test_duplicate_groupby_issues(self):
  301. idx_tp = [
  302. ("600809", "20061231"),
  303. ("600809", "20070331"),
  304. ("600809", "20070630"),
  305. ("600809", "20070331"),
  306. ]
  307. dt = ["demo", "demo", "demo", "demo"]
  308. idx = MultiIndex.from_tuples(idx_tp, names=["STK_ID", "RPT_Date"])
  309. s = Series(dt, index=idx)
  310. result = s.groupby(s.index).first()
  311. assert len(result) == 3
  312. def test_subsets_multiindex_dtype(self):
  313. # GH 20757
  314. data = [["x", 1]]
  315. columns = [("a", "b", np.nan), ("a", "c", 0.0)]
  316. df = DataFrame(data, columns=MultiIndex.from_tuples(columns))
  317. expected = df.dtypes.a.b
  318. result = df.a.b.dtypes
  319. tm.assert_series_equal(result, expected)
  320. class TestSorted:
  321. """everything you wanted to test about sorting"""
  322. def test_sort_non_lexsorted(self):
  323. # degenerate case where we sort but don't
  324. # have a satisfying result :<
  325. # GH 15797
  326. idx = MultiIndex(
  327. [["A", "B", "C"], ["c", "b", "a"]], [[0, 1, 2, 0, 1, 2], [0, 2, 1, 1, 0, 2]]
  328. )
  329. df = DataFrame({"col": range(len(idx))}, index=idx, dtype="int64")
  330. assert df.index.is_monotonic is False
  331. sorted = df.sort_index()
  332. assert sorted.index.is_monotonic is True
  333. expected = DataFrame(
  334. {"col": [1, 4, 5, 2]},
  335. index=MultiIndex.from_tuples(
  336. [("B", "a"), ("B", "c"), ("C", "a"), ("C", "b")]
  337. ),
  338. dtype="int64",
  339. )
  340. result = sorted.loc[pd.IndexSlice["B":"C", "a":"c"], :]
  341. tm.assert_frame_equal(result, expected)