Python 熊猫:将掩码应用于多索引数据帧

Python 熊猫:将掩码应用于多索引数据帧,python,pandas,multi-index,Python,Pandas,Multi Index,我有一个带有多索引列的熊猫数据框架,有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.prod

我有一个带有多索引列的熊猫数据框架,有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.indexlice
,但它以静默方式失败(即,我可以运行以下代码,但没有效果:

dfmi.loc[:, pd.IndexSlice[:, a_mask]] = 0  #dfmi is unchanged
如何实现这一目标,有什么建议吗

编辑 在我的用例中,标签是用笛卡尔积构造的,因此会有(lev0,lev1,lev2)的所有组合。
但是在这种情况下,lev0可以假设2个值{A,B},而lev1可以假设3个值{A,B,c}

使用底层数组数据进行内存效率的现场编辑(不创建任何其他数据帧)-

样本运行-

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

更新的解决方案更加健壮,而不是级别值的硬编码:

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
       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
输出:

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
       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

我会这样做:

mask = pd.concat({k: a_mask for k in dfmi.columns.levels[0]}, axis=1)
dfmi.where(~mask, 0)

我认为用这种方法更安全

dfmi.where(a_mask.loc[:,dfmi.columns.droplevel(0)].values,0)
Out[191]: 
lvl0   A               B            
lvl1   a       b       a       b    
lvl2 bar foo bar foo bar foo bar foo
A0     0   0   0   2   0   0   0   6
A1     9   8  11   0  13  12  15   0
A2     0  16  19  18   0  20  23  22
A3    25   0   0   0  29   0   0   0

谢谢你的回答。你的解决方案是否依赖于每个级别的列都有相同数量的标签(2,2,2)?@FLab确实如此!呃…不幸的是,在我的用例中情况并非如此(我的错误使示例不那么通用,我将更新它)。我真的很喜欢你的回答:我想知道它是否可以针对这种情况进行修改/推广?@FLab以及只要dfmi.A和dfmi.B具有相同的形状,这应该可以工作,无论它们的形状如何。这是唯一的要求。另一个澄清:你是否假设顶部列级别只有两个条目?如果我有A、B、C、wo您是否可以按如下操作?a[:,:n][a_mask]=0;a[:,n:2*n][a_mask]=0;a[:,2*n:][a_mask]=0谢谢,此解决方案有效,不过我会等待其他建议,然后再接受。我会建议以下改进,使其更通用。mask=pd.concat(dict.fromkeys(dfmi.columns.get_level_values(0),a_mask),axis=1)dfmi=dfmi.mask(mask,0)这是一个聪明的解决方案,但在您更新的问题中,标签仍然是简单的“重复”(a、b、c/a、b、c)…如果情况并非如此,连接解决方案将不起作用。我仍然认为
.loc
应该可以在这里以某种方式使用。例如,如果要删除
dfmi[('A','A','foo')]
,则在answer@BradSolomon添加.loc方法。:-)以便,
dfmi.A
dfmi.B
将具有相同的形状,对吗?是的,这在我的情况下是正确的。很好地使用了液滴液位。我一直在尝试使用一个空的,没有得到任何地方。谢谢,非常好answer@FLabyw~快乐编码