我有一个带有MultiIndex列的pandas数据框,有3个级别:
import itertools
import numpy as np
def mklbl(prefix, n):
return ["%s%s" % (prefix, i) for i in range(n)]
miindex = pd.MultiIndex.from_product([mklbl('A', 4)])
micolumns = pd.MultiIndex.from_tuples(list(itertools.product(['A', 'B'], ['a', 'b', 'c'], ['foo', 'bar'])),
names=['lvl0', 'lvl1', 'lvl2'])
dfmi = pd.DataFrame(np.arange(len(miindex) * len(micolumns)).reshape((len(miindex), len(micolumns))),
index=miindex,
columns=micolumns).sort_index().sort_index(axis=1)
lvl0 A B
lvl1 a b c a b c
lvl2 bar foo bar foo bar foo bar foo bar foo bar foo
A0 1 0 3 2 5 4 7 6 9 8 11 10
A1 13 12 15 14 17 16 19 18 21 20 23 22
A2 25 24 27 26 29 28 31 30 33 32 35 34
A3 37 36 39 38 41 40 43 42 45 44 47 46
我想基于另一个数据帧屏蔽此数据帧,该数据帧具有索引的最后两个级别:
cols = micolumns.droplevel(0).unique()
a_mask = pd.DataFrame(np.random.randn(len(dfmi.index), len(cols)), index=dfmi.index, columns=cols)
a_mask = (np.sign(a_mask) > 0).astype(bool)
a b c
foo bar foo bar foo bar
A0 False False False True True False
A1 True False True False True True
A2 True True True True False False
A3 True False False True True False
我想要做的是根据a_mask
屏蔽原始数据帧。
假设a_mask
为真,我想将原始条目设置为零。
我尝试使用pd.IndexSlice
,但它无声地失败(即我可以运行以下代码,但没有效果:
dfmi.loc[:, pd.IndexSlice[:, a_mask]] = 0 #dfmi is unchanged
有任何建议如何实现这一目标?
修改 在我的用例中,标签是用笛卡尔积构成的,因此会有(lev0,lev1,lev2)的所有组合。 但是,lev0可以假设2个值{A,B},而lev1可以假设3个值{a,b,c}
答案 0 :(得分:2)
我会这样做:
mask = pd.concat({k: a_mask for k in dfmi.columns.levels[0]}, axis=1)
dfmi.where(~mask, 0)
答案 1 :(得分:1)
使用底层数组数据进行原位编辑以提高内存效率(不会创建任何其他数据帧) -
d = len(dfmi.columns.levels[0])
n = dfmi.shape[1]//d
for i in range(0,d*n,n):
dfmi.values[:,i:i+n][a_mask] = 0
示例运行 -
In [833]: dfmi
Out[833]:
lvl0 A B
lvl1 a b c a b c
lvl2 bar foo bar foo bar foo bar foo bar foo bar foo
A0 1 0 3 2 5 4 7 6 9 8 11 10
A1 13 12 15 14 17 16 19 18 21 20 23 22
A2 25 24 27 26 29 28 31 30 33 32 35 34
A3 37 36 39 38 41 40 43 42 45 44 47 46
In [834]: a_mask
Out[834]:
a b c
foo bar foo bar foo bar
A0 True True True False False False
A1 False True False False True False
A2 False True True True False False
A3 False False False False False True
In [835]: d = len(dfmi.columns.levels[0])
...: n = dfmi.shape[1]//d
...: for i in range(0,d*n,n):
...: dfmi.values[:,i:i+n][a_mask] = 0
In [836]: dfmi
Out[836]:
lvl0 A B
lvl1 a b c a b c
lvl2 bar foo bar foo bar foo bar foo bar foo bar foo
A0 0 0 0 2 5 4 0 0 0 8 11 10
A1 13 0 15 14 0 16 19 0 21 20 0 22
A2 25 0 0 0 29 28 31 0 0 0 35 34
A3 37 36 39 38 41 0 43 42 45 44 47 0
答案 2 :(得分:0)
更新的解决方案更多roboust不是硬编码的级别值:
lvl0_values = dfmi.columns.get_level_values(0).unique()
pd.concat([dfmi[i].mask(a_mask.rename_axis(['lvl1','lvl2'],axis=1),0) for i in lvl0_values],
keys=lvl0_values, axis=1)
输出:
lvl0 A B
lvl1 a b a b
lvl2 bar foo bar foo bar foo bar foo
A0 1 0 0 0 5 0 0 0
A1 9 0 11 0 13 0 15 0
A2 17 16 19 0 21 20 23 0
A3 0 24 0 26 0 28 0 30
一种方法可以做到这一点:
pd.concat([dfmi['A'].mask(a_mask.rename_axis(['lvl1','lvl2'],axis=1),0),
dfmi['B'].mask(a_mask.rename_axis(['lvl1','lvl2'],axis=1),0)],
keys=['A','B'], axis=1)
print(a_mask)
lvl1 a b
lvl2 foo bar foo bar
A0 True False True True
A1 True False True False
A2 False False True False
A3 False True False True
输出:
A B
lvl1 a b a b
lvl2 bar foo bar foo bar foo bar foo
A0 1 0 0 0 5 0 0 0
A1 9 0 11 0 13 0 15 0
A2 17 16 19 0 21 20 23 0
A3 0 24 0 26 0 28 0 30