python - Pandas MultiIndex DataFrame 引用列计算中的索引值

标签 python pandas dataframe multi-index

我想在某些计算中有效地使用 DataFrame 的 MultiIndex 中的值。例如,从以下内容开始:

np.random.seed(456)
j = [(a, b) for a in ['A','B','C'] for b in random.sample(pd.date_range('2017-01-01', periods=50, freq='W').tolist(), 5)]
i = pd.MultiIndex.from_tuples(j, names=['Name','Num'])
df = pd.DataFrame(np.random.randn(15), i, columns=['Vals'])
df['SmallestNum'] = df.reset_index(level=1).groupby('Name')['Num'].transform('min').values

假设我想计算一个新列Diff = Num - SmallestNum。一种有效但我认为笨拙的方法是将我想要引用的索引级别复制到真正的列中,然后进行差异:

df['NumCol'] = df.index.get_level_values(1)
df['Diff'] = df['NumCol'] - df['SmallestNum']

但我觉得如果我这样做,我仍然不理解使用 DataFrames 的正确方法。我认为“正确”的解决方案如下所示,它不会创建和存储索引值的完整副本:

df['Diff'] = df.transform(lambda x: x.index.get_level_values(1) - x['SmallestNum'])
df['Diff'] = df.reset_index(level=1).apply(lambda x: x['Num'] - x['SmallestNum'])

...然而,这些表达式不仅不起作用*,而且我的理解是,像 .transform.apply 这样的 DataFrame 操作必然会显着增加比对显式“矢量化”行引用进行操作的速度慢。

那么在此示例中为新的 Diff 列编写计算的“正确且有效”的方法是什么?

<小时/>

* 更新:此问题因索引级别 1 值不唯一这一事实(可能是错误)而变得更加复杂,这会导致在索引值唯一时有效的公式失败并显示 NotImplementedError:非唯一索引上的 Index._join_level 未实现。幸运的是,jezrael's answer 包含的解决方法似乎与显式矢量化计算一样高效。

最佳答案

我认为你需要简单地减去:

df['Diff'] = df.index.get_level_values(1) - df['SmallestNum']
print (df)

              Vals  SmallestNum  Diff
Name Num                             
A    28   1.180140           28     0
     44   0.984257           28    16
     90   1.835646           28    62
     43  -1.886823           28    15
     29   0.424763           28     1
B    80  -0.433105           38    42
     61  -0.166838           38    23
     46   0.754634           38     8
     38   1.966975           38     0
     93   0.200671           38    55
C    40   0.742752           12    28
     82  -1.264271           12    70
     12  -0.112787           12     0
     78   0.667358           12    66
     70   0.357900           12    58

编辑:对于第二级工作中的非唯一DatetimeIndex,由values创建的减法numpy数组:

np.random.seed(456)
a = pd.date_range('2015-01-01', periods=6).values
j = [['A'] * 5 + ['B'] * 5 + ['C'] * 5, pd.to_datetime(np.random.choice(a, size=15))]
i = pd.MultiIndex.from_arrays(j, names=['Name','Num'])
df = pd.DataFrame(np.random.randn(15), i, columns=['Vals'])
df['SmallestNum'] = df.reset_index(level=1).groupby('Name')['Num'].transform('min').values
df['Diff'] = df.index.get_level_values(1).values - df['SmallestNum'].values
print (df)
                     Vals SmallestNum   Diff
Name Num                                    
A    2015-01-04 -1.842419  2015-01-02 2 days
     2015-01-06 -0.786788  2015-01-02 4 days
     2015-01-04  1.180140  2015-01-02 2 days
     2015-01-02  0.984257  2015-01-02 0 days
     2015-01-03  1.835646  2015-01-02 1 days
B    2015-01-05 -1.886823  2015-01-03 2 days
     2015-01-03  0.424763  2015-01-03 0 days
     2015-01-05 -0.433105  2015-01-03 2 days
     2015-01-06 -0.166838  2015-01-03 3 days
     2015-01-05  0.754634  2015-01-03 2 days
C    2015-01-06  1.966975  2015-01-02 4 days
     2015-01-06  0.200671  2015-01-02 4 days
     2015-01-05  0.742752  2015-01-02 3 days
     2015-01-02 -1.264271  2015-01-02 0 days
     2015-01-04 -0.112787  2015-01-02 2 days

另一个解决方案:

df['Diff'] = (df.reset_index(level=1)
                .groupby('Name')['Num']
                .transform(lambda x: x - x.min())
                .values)
print (df)
                     Vals   Diff
Name Num                        
A    2015-01-04 -1.842419 2 days
     2015-01-06 -0.786788 4 days
     2015-01-04  1.180140 2 days
     2015-01-02  0.984257 0 days
     2015-01-03  1.835646 1 days
B    2015-01-05 -1.886823 2 days
     2015-01-03  0.424763 0 days
     2015-01-05 -0.433105 2 days
     2015-01-06 -0.166838 3 days
     2015-01-05  0.754634 2 days
C    2015-01-06  1.966975 4 days
     2015-01-06  0.200671 4 days
     2015-01-05  0.742752 3 days
     2015-01-02 -1.264271 0 days
     2015-01-04 -0.112787 2 days

关于python - Pandas MultiIndex DataFrame 引用列计算中的索引值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48977918/

相关文章:

python - 从数组中随机翻转 m 个值

python - 如何在完成后重新启动 python 脚本

python - 使用python生成8个字符的密码,包括小写、大写和数字

python - 仅对 pandas 数据框的给定列中的某些行求和

python - Pandas 中有矢量化的 string.format 吗?

python - 从 Pandas DataFrame 创建时间序列

python - 如何将函数应用于多个 Pandas 数据框

python - 计算 groupby 对象中的多个值

python - 如何根据索引和列值过滤数据帧

python-3.x - 在 Pandas Dataframe 中将多行合并为单行