通过要求MultiIndex级别中存在多个项目来过滤pandas数据框

时间:2017-03-17 20:48:25

标签: python pandas multi-index

我有一个带有多索引的数据表。多索引的第一级是对应于给定序列(DNA)的名称,多索引的第二级对应于特定类型的序列变体wtm1,{{ 1}},m2在下面的示例中。并非所有给定的m3序列都包含所有类型的变体(请参阅下面的wtseqA)。

seqC

我想对仅具有特定变体类型(本例中为df = pd.DataFrame(data={'A':range(1,9), 'B':range(1,9), 'C': range(1,9)}, index=pd.MultiIndex.from_tuples([('seqA', 'wt'), ('seqA', 'm1'), ('seqA', 'm2'), ('seqB', 'wt'), ('seqB', 'm1'), ('seqB', 'm2'), ('seqB', 'm3'), ('seqC', 'wt') ])) df.index.rename(['seq_name','type'], inplace=True) print df A B C seq_name type seqA wt 1 1 1 m1 2 2 2 m2 3 3 3 seqB wt 4 4 4 m1 5 5 5 m2 6 6 6 m3 7 7 7 seqC wt 8 8 8 m1)的序列的数据进行后续分析。因此,我想过滤我的数据框,要求给定m2具有seq_name中指定的所有变体类型。

我目前的解决方案非常笨重,并不是非常美观的IMO。

list

我觉得必须是一个可以做到这一点的单行。类似的东西:

var_l = ['wt', 'm1', 'm2']
df1 = df[df.index.get_level_values('type').isin(var_l)] #Filter varaints not of interest

set_l = []
for v in var_l: #Filter for each variant individually, and store seq_names
    df2 = df[df.index.get_level_values('type').isin([v])]
    set_l.append(set(df2.index.get_level_values('seq_name')))

seq_s = set.intersection(*set_l) # Get seq_names that only have all three variants
df3 = df1[df1.index.get_level_values('seq_name').isin(seq_s)] #Filter based on seq_name
print df3

               A  B  C
seq_name type         
seqA     wt    1  1  1
         m1    2  2  2
         m2    3  3  3
seqB     wt    4  4  4
         m1    5  5  5
         m2    6  6  6

我已尝试搜索此网站,并且只找到了可以过滤列表中任何项目的答案。

2 个答案:

答案 0 :(得分:2)

选项1
使用query + stack
正如@jezrael指出的那样,这取决于要分析的行中不存在NaN

df.query('type in @var_l').unstack().dropna().stack()

                 A    B    C
seq_name type               
seqA     m1    2.0  2.0  2.0
         m2    3.0  3.0  3.0
         wt    1.0  1.0  1.0
seqB     m1    5.0  5.0  5.0
         m2    6.0  6.0  6.0
         wt    4.0  4.0  4.0

保留dtypes

df.query('type in @var_l').unstack().dropna().stack().astype(df.dtypes)

               A  B  C
seq_name type         
seqA     m1    2  2  2
         m2    3  3  3
         wt    1  1  1
seqB     m1    5  5  5
         m2    6  6  6
         wt    4  4  4

选项2
使用filter
它检查与var_l相交的子索引是否与var_l

相同
def correct_vars(df, v):
    x = set(v)
    n = df.name
    y = set(df.xs(n).index.intersection(v))
    return x == y

df.groupby(level=0).filter(correct_vars, v=var_l)

               A  B  C
seq_name type         
seqA     wt    1  1  1
         m1    2  2  2
         m2    3  3  3
seqB     wt    4  4  4
         m1    5  5  5
         m2    6  6  6
         m3    7  7  7

答案 1 :(得分:2)

您可以query使用filter

var_l = ['wt', 'm1', 'm2']

filtered_df=df.query('type in @var_l').groupby(level=0).filter(lambda x: len(x)==len(var_l))
print (filtered_df)
               A  B  C
seq_name type         
seqA     wt    1  1  1
         m1    2  2  2
         m2    3  3  3
seqB     wt    4  4  4
         m1    5  5  5
         m2    6  6  6

使用transform size然后按boolean indexing过滤的另一种解决方案:

filtered_df = df.query('type in @var_l')
filtered_df = filtered_df[filtered_df.groupby(level=0)['A']
                                     .transform('size')
                                     .eq(len(var_l))
                                     .rename(None)]

print (filtered_df)
               A  B  C
seq_name type         
seqA     wt    1  1  1
         m1    2  2  2
         m2    3  3  3
seqB     wt    4  4  4
         m1    5  5  5
         m2    6  6  6

它的工作原因是:

print (filtered_df.groupby(level=0)['A'].transform('size'))
seq_name  type
seqA      wt      3
          m1      3
          m2      3
seqB      wt      3
          m1      3
          m2      3
seqC      wt      1
Name: A, dtype: int32

print (filtered_df.groupby(level=0)['A']
                  .transform('size')
                  .eq(len(var_l))
                  .rename(None))
seq_name  type
seqA      wt       True
          m1       True
          m2       True
seqB      wt       True
          m1       True
          m2       True
seqC      wt      False
dtype: bool