Отфильтруйте фрейм данных Pandas, указав наличие нескольких элементов на уровне MultiIndex.

У меня есть таблица данных с мультииндексом. Первый уровень мультииндекса представляет собой имя, соответствующее данной последовательности (ДНК), второй уровень мультииндекса соответствует определенному типу варианта последовательности. wt, m1,m2, m3 в примере ниже. Не все дано wt последовательности будут иметь все типы вариантов (см. seqA, а также 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 имеет все типы вариантов, указанные в 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 ответа

Решение

Ты можешь использовать 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

Другое решение с 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

Опция 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
Другие вопросы по тегам