【问题标题】:Filter a pandas data frame by requiring presence of multiple items in a MultiIndex level通过要求在 MultiIndex 级别中存在多个项目来过滤 pandas 数据框
【发布时间】:2017-08-09 13:27:41
【问题描述】:

我有一个带有多索引的数据表。多索引的第一级是与给定序列(DNA)对应的名称,多索引的第二级对应于特定类型的序列变体wtm1m2,@987654324 @ 在下面的示例中。并非所有给定的wt 序列都会具有所有类型的变体(请参阅下面的seqAseqC)。

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

我想仅对具有特定类型变体的序列(本例中为m1m2)的数据进行后续分析。所以我想过滤我的数据框以要求给定的seq_name 具有list 中指定的所有 变体类型。

我目前的解决方案相当笨拙,而且在 IMO 上不太美观。

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

我觉得那里必须是一个可以做到这一点的单线。比如:

var_l = ['wt', 'm1', 'm2']
filtered_df = filterDataframe(df1, 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

我已尝试搜索此站点,但只找到了可让您按列表中的任何项进行过滤的答案。

【问题讨论】:

    标签: python pandas multi-index


    【解决方案1】:

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

    【讨论】:

    • 我认为你的解决方案的问题是如果有一些NaN 行,那么它会删除seq :(
    • @jezrael True 我会努力解决的
    【解决方案2】:

    您可以将queryfilter 一起使用:

    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
    

    transformsize 的另一种解决方案,然后按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
    

    【讨论】:

    • 谢谢!第一个解决方案正是我想要的。我对 pandas 比较陌生,不熟悉 querygroupby 功能。
    猜你喜欢
    • 2017-03-08
    • 1970-01-01
    • 2018-04-04
    • 1970-01-01
    • 2020-05-26
    • 2020-11-29
    • 2017-08-25
    • 2018-06-24
    相关资源
    最近更新 更多