2012-08-08 2 views
176

Большинство операций в pandas может быть выполнена с помощью оператора цепочки (groupby, aggregate, apply, и т.д.), но единственный способ я нашел для фильтрации строк осуществляется с помощью обычной индексации кронштейнапанды: фильтр строк DataFrame с оператором построения цепочки

df_filtered = df[df['column'] == value] 

Это неприменимо, поскольку для этого требуется присвоить переменную df, прежде чем она сможет фильтровать ее значения. Есть ли что-то более похожее на следующее?

df_filtered = df.mask(lambda x: x['column'] == value) 

ответ

235

Я не совсем уверен, что вы хотите, и ваша последняя строка кода не помогает, но все равно:

«Прикованный» фильтрация осуществляется с помощью «СЦЕПЛЕНИЕ» критериев в булево индекс.

In [96]: df 
Out[96]: 
    A B C D 
a 1 4 9 1 
b 4 5 0 2 
c 5 5 1 0 
d 1 3 9 6 

In [99]: df[(df.A == 1) & (df.D == 6)] 
Out[99]: 
    A B C D 
d 1 3 9 6 

Если вы хотите связать методы, вы можете добавить свой собственный метод маски и использовать его.

In [90]: def mask(df, key, value): 
    ....:  return df[df[key] == value] 
    ....: 

In [92]: pandas.DataFrame.mask = mask 

In [93]: df = pandas.DataFrame(np.random.randint(0, 10, (4,4)), index=list('abcd'), columns=list('ABCD')) 

In [95]: df.ix['d','A'] = df.ix['a', 'A'] 

In [96]: df 
Out[96]: 
    A B C D 
a 1 4 9 1 
b 4 5 0 2 
c 5 5 1 0 
d 1 3 9 6 

In [97]: df.mask('A', 1) 
Out[97]: 
    A B C D 
a 1 4 9 1 
d 1 3 9 6 

In [98]: df.mask('A', 1).mask('D', 6) 
Out[98]: 
    A B C D 
d 1 3 9 6 
+1

Отличный ответ! Итак, в '' '(df.A == 1) & (df.D == 6)' '', является ли «&» перегруженным оператором в Pandas? – Shawn

+2

действительно см. Также http://pandas.pydata.org/pandas-docs/stable/indexing.html?#boolean-operators –

+0

Это действительно приятное решение - я даже не знал, что вы можете использовать методы присяжных как в python. Подобную функцию было бы очень приятно иметь в самой Панде. – naught101

54

Ответ от @lodagro замечательный. Я хотел бы расширить его обобщающую функцию маски как:

def mask(df, f): 
    return df[f(df)] 

Тогда вы можете сделать такие вещи, как:

df.mask(lambda x: x[0] < 0).mask(lambda x: x[1] > 0) 
+5

Полезное обобщение! Хотелось бы, чтобы он был интегрирован непосредственно в 'DataFrame' уже! – duckworthd

+0

см. Запрос функции на странице https://github.com/pydata/pandas/issues/5900 – naught101

4

Если вы хотите, чтобы применить все обычные булевы маски, а также общее назначение маски вы можете швырнуть следующее в файл, а затем просто назначить их все следующим образом:

pd.DataFrame = apply_masks() 

Использование:

A = pd.DataFrame(np.random.randn(4, 4), columns=["A", "B", "C", "D"]) 
A.le_mask("A", 0.7).ge_mask("B", 0.2)... (May be repeated as necessary 

Это немного взломанный, но он может сделать вещи немного более чистыми, если вы постоянно рубите и меняете наборы данных в соответствии с фильтрами. Существует также фильтр общего назначения, адаптированный из Daniel Velkov выше в функции gen_mask, которую вы можете использовать с лямбда-функциями или, при желании, иначе.

Файл будет сохранен (я использую masks.py):

import pandas as pd 

def eq_mask(df, key, value): 
    return df[df[key] == value] 

def ge_mask(df, key, value): 
    return df[df[key] >= value] 

def gt_mask(df, key, value): 
    return df[df[key] > value] 

def le_mask(df, key, value): 
    return df[df[key] <= value] 

def lt_mask(df, key, value): 
    return df[df[key] < value] 

def ne_mask(df, key, value): 
    return df[df[key] != value] 

def gen_mask(df, f): 
    return df[f(df)] 

def apply_masks(): 

    pd.DataFrame.eq_mask = eq_mask 
    pd.DataFrame.ge_mask = ge_mask 
    pd.DataFrame.gt_mask = gt_mask 
    pd.DataFrame.le_mask = le_mask 
    pd.DataFrame.lt_mask = lt_mask 
    pd.DataFrame.ne_mask = ne_mask 
    pd.DataFrame.gen_mask = gen_mask 

    return pd.DataFrame 

if __name__ == '__main__': 
    pass 
51

Фильтры могут быть соединены с помощью панд query:

df = pd.DataFrame(np.random.randn(30,3), columns = ['a','b','c']) 
df_filtered = df.query('a>0').query('0<b<2') 

Фильтры могут быть объединены в одном запросе:

df_filtered = df.query('a>0 and 0<b<2') 
+1

Если вам нужно обратиться к переменным python в своем запросе, [документация] (http://pandas.pydata.org/pandas -docs/stable/generated/pandas.DataFrame.query.html) говорит: «Вы можете ссылаться на переменные в среде, префикс их с символом« @ », таким как @a + b». Обратите внимание, что допустимы следующие действия: 'df.query ('a в списке ([1,2])')', 's = set ([1,2]); df.query ('a in @s') '. – user3780389

+1

С другой стороны, похоже, что оценка запроса завершится неудачно, если имя вашего столбца имеет определенные специальные символы: например. "Укажите имя". – user3780389

8

У меня был тот же вопрос, за исключением того, что я хотел совместить критерии с OR co ndition.Формат задается Wouter Overmeire сочетает в себе критерии в элемент И состояние такое, что оба должны быть удовлетворены:

In [96]: df 
Out[96]: 
    A B C D 
a 1 4 9 1 
b 4 5 0 2 
c 5 5 1 0 
d 1 3 9 6 

In [99]: df[(df.A == 1) & (df.D == 6)] 
Out[99]: 
    A B C D 
d 1 3 9 6 

Но я обнаружил, что, если вы оберните каждое условие в (... == True) и присоединиться к критериям с трубой, эти критерии объединенные в состоянии ИЛИ, удовлетворяются всякий раз, когда выполняется любое из них:

df[((df.A==1) == True) | ((df.D==6) == True)] 
+9

Не будет 'df [(df.A == 1) | (df.D == 6)] 'достаточно для того, что вы пытаетесь выполнить? – eenblam

5

Мой ответ аналогичен другим. Если вы не хотите создавать новую функцию, вы можете использовать то, что уже определили для вас панды. Используйте метод трубы.

df.pipe(lambda d: d[d['column'] == value]) 
+0

** THIS ** - это то, что вы хотите, если хотите цепочки команд, таких как 'a.join (b) .pipe (lambda df: df [df.column_to_filter == 'VALUE'])' – displayname

11

pandas.DataFrame.query
query было сделано именно для этой цели. Рассмотрим dataframe df

import pandas as pd 
import numpy as np 

np.random.seed([3,1415]) 
df = pd.DataFrame(
    np.random.randint(10, size=(10, 5)), 
    columns=list('ABCDE') 
) 

df 

    A B C D E 
0 0 2 7 3 8 
1 7 0 6 8 6 
2 0 2 0 4 9 
3 7 3 2 4 3 
4 3 6 7 7 4 
5 5 3 7 5 9 
6 8 7 6 4 7 
7 6 2 6 6 5 
8 2 8 7 5 8 
9 4 7 6 1 5 

Давайте использовать query фильтровать все строки, в которых D > B

df.query('D > B') 

    A B C D E 
0 0 2 7 3 8 
1 7 0 6 8 6 
2 0 2 0 4 9 
3 7 3 2 4 3 
4 3 6 7 7 4 
5 5 3 7 5 9 
7 6 2 6 6 5 

Что мы приковать

df.query('D > B').query('C > B') 
# equivalent to 
# df.query('D > B and C > B') 
# but defeats the purpose of demonstrating chaining 

    A B C D E 
0 0 2 7 3 8 
1 7 0 6 8 6 
4 3 6 7 7 4 
5 5 3 7 5 9 
7 6 2 6 6 5 
0

Если вы установите колонки для поиска в качестве индексов, то вы можете используйте DataFrame.xs(), чтобы принять поперечное сечение. Это не так много, как ответы query, но может быть полезно в некоторых ситуациях.

import pandas as pd 
import numpy as np 

np.random.seed([3,1415]) 
df = pd.DataFrame(
    np.random.randint(3, size=(10, 5)), 
    columns=list('ABCDE') 
) 

df 
# Out[55]: 
# A B C D E 
# 0 0 2 2 2 2 
# 1 1 1 2 0 2 
# 2 0 2 0 0 2 
# 3 0 2 2 0 1 
# 4 0 1 1 2 0 
# 5 0 0 0 1 2 
# 6 1 0 1 1 1 
# 7 0 0 2 0 2 
# 8 2 2 2 2 2 
# 9 1 2 0 2 1 

df.set_index(['A', 'D']).xs([0, 2]).reset_index() 
# Out[57]: 
# A D B C E 
# 0 0 2 2 2 2 
# 1 0 2 1 1 0 
6

Поскольку version 0.18.1 метод .loc принимает вызываемый для выбора. Вместе с лямбда-функциям можно создавать очень гибкие змеевидных фильтры:

import numpy as np 
import pandas as pd 

df = pd.DataFrame(np.random.randint(0,100,size=(100, 4)), columns=list('ABCD')) 
df.loc[lambda df: df.A == 80] # equivalent to df[df.A == 80] but chainable 

df.sort_values('A').loc[lambda df: df.A > 80].loc[lambda df: df.B > df.A] 

Если все, что вы делаете фильтрацию, вы также можете опустить .loc.

+1

Это правильный ответ –

1

Это решение более хакерское с точки зрения реализации, но я нахожу его более чистым с точки зрения использования, и он, безусловно, более общий, чем другие предлагаемые.

https://github.com/toobaz/generic_utils/blob/master/generic_utils/pandas/condition.py

Вам не нужно загружать весь репозиторий: сохранение файла и делает

from condition import Condition as C 

должно хватить.Затем вы используете это так:

df = pd.DataFrame([[1, 2, True], 
        [3, 4, False], 
        [5, 7, True]], 
        index=range(3), columns=['a', 'b', 'c']) 
# On specific column: 
print(df.loc[C('a') > 2]) 
print(df.loc[-C('a') == C('b')]) 
print(df.loc[~C('c')]) 
# On entire DataFrame: 
print(df.loc[C().sum(axis=1) > 3]) 
print(df.loc[C(['a', 'b']).diff(axis=1)['b'] > 1]) 

чуть менее глупый пример использования:

data = pd.read_csv('ugly_db.csv').loc[~(C() == '$null$').any(axis=1)] 

Кстати: даже в том случае, когда вы только с помощью логической CO,

df.loc[C('cond1')].loc[C('cond2')] 

может быть намного более эффективным, чем

df.loc[C('cond1') & C('cond2')] 

потому что он оценивает cond2 только там, где cond1 является True.

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: Я впервые дал этот ответ elsewhere, потому что я этого не видел.

0

Вы также можете использовать библиотеку numpy для логических операций. Это довольно быстро.

df[np.logical_and(df['A'] == 1 ,df['B'] == 6)] 
1

Просто хочу добавить демонстрацию с помощью loc фильтровать не только строки, но и по столбцам и некоторые заслуги перед прикованным операции.

Код ниже может фильтровать строки по значению.

df_filtered = df.loc[df['column'] == value] 

Изменяя его, вы также можете фильтровать столбцы.

df_filtered = df.loc[df['column'] == value, ['year', 'column']] 

Так почему мы хотим цепной метод? Ответ заключается в том, что его легко читать, если у вас много операций. Например,

res = df\ 
    .loc[df['station']=='USA', ['TEMP', 'RF']]\ 
    .groupby('year')\ 
    .agg(np.nanmean) 
Смежные вопросы