Отфильтруйте фрейм данных 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
Другое решение с 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
Опция 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