所以我了解到我可以使用 DataFrame.groupby 而不需要 MultiIndex 来进行子采样/横截面。
另一方面,当我在 DataFrame 上有一个 MultiIndex 时,我仍然需要使用 DataFrame.groupby 来进行子采样/横截面。
那么除了打印时层次结构的非常有用和漂亮的显示之外,MultiIndex 还有什么好处呢?
所以我了解到我可以使用 DataFrame.groupby 而不需要 MultiIndex 来进行子采样/横截面。
另一方面,当我在 DataFrame 上有一个 MultiIndex 时,我仍然需要使用 DataFrame.groupby 来进行子采样/横截面。
那么除了打印时层次结构的非常有用和漂亮的显示之外,MultiIndex 还有什么好处呢?
在 pandas 0.4 版本中引入了分层索引(也称为“多级”索引)。
这为一些非常复杂的数据分析和操作打开了大门,特别是对于处理更高维度的数据。从本质上讲,它使您能够在二维表格结构 (DataFrame) 中有效地存储和操作任意高维数据。
想象一下使用MultiIndex
这样的构造数据框:-
import pandas as pd
import numpy as np
np.arrays = [['one','one','one','two','two','two'],[1,2,3,1,2,3]]
df = pd.DataFrame(np.random.randn(6,2),index=pd.MultiIndex.from_tuples(list(zip(*np.arrays))),columns=['A','B'])
df # This is the dataframe we have generated
A B
one 1 -0.732470 -0.313871
2 -0.031109 -2.068794
3 1.520652 0.471764
two 1 -0.101713 -1.204458
2 0.958008 -0.455419
3 -0.191702 -0.915983
这df
只是一个二维的数据结构
df.ndim
2
但是我们可以把它想象成一个 3 维数据结构。
one
与1
数据-0.732470 -0.313871
。one
与2
数据-0.031109 -2.068794
。one
与3
数据1.520652 0.471764
。又名:“在二维表格结构中有效地存储和操作任意高维数据”
这不仅仅是一个“漂亮的展示”。由于我们现在有一个层次索引,因此它具有易于检索数据的好处。
例如。
In [44]: df.ix["one"]
Out[44]:
A B
1 -0.732470 -0.313871
2 -0.031109 -2.068794
3 1.520652 0.471764
只会为属于“一个”的数据组给我们一个新的数据框。
我们可以通过这样做进一步缩小我们的数据选择范围:-
In [45]: df.ix["one"].ix[1]
Out[45]:
A -0.732470
B -0.313871
Name: 1
当然,如果我们想要一个特定的值,这里有一个例子:-
In [46]: df.ix["one"].ix[1]["A"]
Out[46]: -0.73247029752040727
因此,如果我们有更多索引(除了上面示例中显示的 2 个索引),我们基本上可以向下钻取并选择我们真正感兴趣的数据集,而无需groupby
.
我们甚至可以从数据框中获取横截面(行或列)...
按行:-
In [47]: df.xs('one')
Out[47]:
A B
1 -0.732470 -0.313871
2 -0.031109 -2.068794
3 1.520652 0.471764
按列:-
In [48]: df.xs('B', axis=1)
Out[48]:
one 1 -0.313871
2 -2.068794
3 0.471764
two 1 -1.204458
2 -0.455419
3 -0.915983
Name: B
@Calvin Cheng 的好帖子,但我想我也会尝试一下。
何时使用 MultiIndex:
为什么(您的核心问题)- 至少这些是 IMO 的最大好处:
例子:
Dollars Units
Date Store Category Subcategory UPC EAN
2018-07-10 Store 1 Alcohol Liqour 80480280024 154.77 7
Store 2 Alcohol Liqour 80480280024 82.08 4
Store 3 Alcohol Liqour 80480280024 259.38 9
Store 1 Alcohol Liquor 80432400630 477.68 14
674545000001 139.68 4
Store 2 Alcohol Liquor 80432400630 203.88 6
674545000001 377.13 13
Store 3 Alcohol Liquor 80432400630 239.19 7
674545000001 432.32 14
Store 1 Beer Ales 94922755711 65.17 7
702770082018 174.44 14
736920111112 50.70 5
Store 2 Beer Ales 94922755711 129.60 12
702770082018 107.40 10
736920111112 59.65 5
Store 3 Beer Ales 94922755711 154.00 14
702770082018 137.40 10
736920111112 107.88 12
Store 1 Beer Lagers 702770081011 156.24 12
Store 2 Beer Lagers 702770081011 137.06 11
Store 3 Beer Lagers 702770081011 119.52 8
1) 如果我们想轻松比较不同商店的销售额,我们可以使用df.unstack('Store')
并排排列所有内容:
Dollars Units
Store Store 1 Store 2 Store 3 Store 1 Store 2 Store 3
Date Category Subcategory UPC EAN
2018-07-10 Alcohol Liqour 80480280024 154.77 82.08 259.38 7 4 9
Liquor 80432400630 477.68 203.88 239.19 14 6 7
674545000001 139.68 377.13 432.32 4 13 14
Beer Ales 94922755711 65.17 129.60 154.00 7 12 14
702770082018 174.44 107.40 137.40 14 10 10
736920111112 50.70 59.65 107.88 5 5 12
Lagers 702770081011 156.24 137.06 119.52 12 11 8
2)我们还可以轻松地对多列进行数学运算。例如,df['Dollars'] / df['Units']
然后将每个商店的美元除以其单位,对于没有多重操作的每个商店:
Store Store 1 Store 2 Store 3
Date Category Subcategory UPC EAN
2018-07-10 Alcohol Liqour 80480280024 22.11 20.52 28.82
Liquor 80432400630 34.12 33.98 34.17
674545000001 34.92 29.01 30.88
Beer Ales 94922755711 9.31 10.80 11.00
702770082018 12.46 10.74 13.74
736920111112 10.14 11.93 8.99
Lagers 702770081011 13.02 12.46 14.94
3)如果我们想要过滤到特定的行,而不是使用
df[(df[col1] == val1) and (df[col2] == val2) and (df[col3] == val3)]
格式,我们可以改为 .xs 或 .query (是的,这些适用于常规 dfs,但它不是很有用)。语法将改为:
df.xs((val1, val2, val3), level=(col1, col2, col3))
更多示例可以在我整理的本教程笔记本中找到。
使用多索引的替代方法是使用数据框的多列存储数据。人们会期望多索引能够提供比原始列存储更高的性能,但从 Pandas v 1.1.4 开始,情况似乎并非如此。
import numpy as np
import pandas as pd
np.random.seed(2020)
inv = pd.DataFrame({
'store_id': np.random.choice(10000, size=10**7),
'product_id': np.random.choice(1000, size=10**7),
'stock': np.random.choice(100, size=10**7),
})
# Create a DataFrame with a multiindex
inv_multi = inv.groupby(['store_id', 'product_id'])[['stock']].agg('sum')
print(inv_multi)
stock
store_id product_id
0 2 48
4 18
5 58
7 149
8 158
... ...
9999 992 132
995 121
996 105
998 99
999 16
[6321869 rows x 1 columns]
# Create a DataFrame without a multiindex
inv_cols = inv_multi.reset_index()
print(inv_cols)
store_id product_id stock
0 0 2 48
1 0 4 18
2 0 5 58
3 0 7 149
4 0 8 158
... ... ... ...
6321864 9999 992 132
6321865 9999 995 121
6321866 9999 996 105
6321867 9999 998 99
6321868 9999 999 16
[6321869 rows x 3 columns]
%%timeit
inv_multi.xs(key=100, level='store_id')
10 loops, best of 3: 20.2 ms per loop
%%timeit
inv_cols.loc[inv_cols.store_id == 100]
The slowest run took 8.79 times longer than the fastest. This could mean that an intermediate result is being cached.
100 loops, best of 3: 11.5 ms per loop
%%timeit
inv_multi.xs(key=100, level='product_id')
100 loops, best of 3: 9.08 ms per loop
%%timeit
inv_cols.loc[inv_cols.product_id == 100]
100 loops, best of 3: 12.2 ms per loop
%%timeit
inv_multi.xs(key=(100, 100), level=('store_id', 'product_id'))
10 loops, best of 3: 29.8 ms per loop
%%timeit
inv_cols.loc[(inv_cols.store_id == 100) & (inv_cols.product_id == 100)]
10 loops, best of 3: 28.8 ms per loop
使用 MultiIndex 的好处在于语法糖、自记录数据以及来自unstack()
@ZaxR 回答中提到的功能的小便利;性能不是好处,这似乎是一个真正错失的机会。