2017-03-17 29 views
6

我有一個包含多索引的數據表。多指數的第一級是與給定序列(DNA)相對應的名稱,多指數的第二級對應於在下面的示例中的特定類型的序列變體wt,m1,m2,m3。並非所有給出的wt序列將具有所有類型的變體(參見下面的seqAseqC)。通過要求MultiIndex中存在多個項來過濾熊貓數據框

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 

我試圖尋找這個網站,並且只發現讓你通過在列表過濾任何項目答案。

回答

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

謝謝!第一個解決方案正是我想要的。我對熊貓比較陌生,並不熟悉'query'或'groupby'功能。 – HikerT

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 

使用filter
它檢查如果子索引與var_l相交相同var_l

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

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

我認爲你的解決方案的問題是如果一些'NaN'行,然後它刪除'seq' :( – jezrael

+0

@jezrael'真'我會嘗試修復 – piRSquared

+0

固定.......... – piRSquared