Динамическая фильтрация фрейма pandas
Я пытаюсь отфильтровать кадр данных pandas, используя пороговые значения для трех столбцов
import pandas as pd
df = pd.DataFrame({"A" : [6, 2, 10, -5, 3],
"B" : [2, 5, 3, 2, 6],
"C" : [-5, 2, 1, 8, 2]})
df = df.loc[(df.A > 0) & (df.B > 2) & (df.C > -1)].reset_index(drop = True)
df
A B C
0 2 5 2
1 10 3 1
2 3 6 2
Однако я хочу сделать это внутри функции, где имена столбцов и их пороговые значения даны мне в словаре. Здесь моя первая попытка, которая работает нормально. По сути, я помещаю фильтр внутри переменной cond
и просто запускаю его:
df = pd.DataFrame({"A" : [6, 2, 10, -5, 3],
"B" : [2, 5, 3, 2, 6],
"C" : [-5, 2, 1, 8, 2]})
limits_dic = {"A" : 0, "B" : 2, "C" : -1}
cond = "df = df.loc["
for key in limits_dic.keys():
cond += "(df." + key + " > " + str(limits_dic[key])+ ") & "
cond = cond[:-2] + "].reset_index(drop = True)"
exec(cond)
df
A B C
0 2 5 2
1 10 3 1
2 3 6 2
Теперь, наконец, я помещаю все внутри функции и перестает работать (возможно, функция exec
не нравится использоваться внутри функции!):
df = pd.DataFrame({"A" : [6, 2, 10, -5, 3],
"B" : [2, 5, 3, 2, 6],
"C" : [-5, 2, 1, 8, 2]})
limits_dic = {"A" : 0, "B" : 2, "C" : -1}
def filtering(df, limits_dic):
cond = "df = df.loc["
for key in limits_dic.keys():
cond += "(df." + key + " > " + str(limits_dic[key])+ ") & "
cond = cond[:-2] + "].reset_index(drop = True)"
exec(cond)
return(df)
df = filtering(df, limits_dic)
df
A B C
0 6 2 -5
1 2 5 2
2 10 3 1
3 -5 2 8
4 3 6 2
Я знаю, что функция exec
действует по-разному, когда используется внутри функции, но не была уверена, как решить проблему. Кроме того, мне интересно, должен ли быть более элегантный способ определить функцию для фильтрации данных двух входных данных: 1) df
и 2) limits_dic = {"A" : 0, "B" : 2, "C" : -1}
. Я был бы признателен за любые мысли по этому поводу.
Ответы
Ответ 1
Если вы пытаетесь построить динамический запрос, есть более простые способы. Вот тот, который использует понимание списка и str.join
:
query = ' & '.join(['{}>{}'.format(k, v) for k, v in limits_dic.items()])
Или, используя f
-strings с python-3. 6+,
query = ' & '.join([f'{k}>{v}' for k, v in limits_dic.items()])
print(query)
'A>0 & C>-1 & B>2'
Передайте строку запроса в df.query
, она предназначена именно для этой цели:
out = df.query(query)
print(out)
A B C
1 2 5 2
2 10 3 1
4 3 6 2
Вы также можете использовать df.eval
, если хотите получить логическую маску для своего запроса, и после этого индексирование становится простым:
mask = df.eval(query)
print(mask)
0 False
1 True
2 True
3 False
4 True
dtype: bool
out = df[mask]
print(out)
A B C
1 2 5 2
2 10 3 1
4 3 6 2
Строковые данные
Если вам нужно запросить столбцы, которые используют строковые данные, приведенный выше код потребует небольшого изменения.
Рассмотрим (данные из этого ответа):
df = pd.DataFrame({'gender':list('MMMFFF'),
'height':[4,5,4,5,5,4],
'age':[70,80,90,40,2,3]})
print (df)
gender height age
0 M 4 70
1 M 5 80
2 M 4 90
3 F 5 40
4 F 5 2
5 F 4 3
И список столбцов, операторов и значений:
column = ['height', 'age', 'gender']
equal = ['>', '>', '==']
condition = [1.68, 20, 'F']
Соответствующая модификация здесь:
query = ' & '.join(f'{i} {j} {repr(k)}' for i, j, k in zip(column, equal, condition))
df.query(query)
age gender height
3 40 F 5
Информацию о семействе функций pd.eval()
, их функциях и вариантах использования см. на странице Оценка динамических выражений в пандах с использованием pd.eval().
Ответ 2
Альтернатива версии @coldspeed:
conditions = None
for key, val in limit_dic.items():
cond = df[key] > val
if conditions is None:
conditions = cond
else:
conditions = conditions & cond
print(df[conditions])
Ответ 3
Альтернатива обоим опубликованным, которые могут быть или не быть более питоническими:
import pandas as pd
import operator
from functools import reduce
df = pd.DataFrame({"A": [6, 2, 10, -5, 3],
"B": [2, 5, 3, 2, 6],
"C": [-5, 2, 1, 8, 2]})
limits_dic = {"A": 0, "B": 2, "C": -1}
# equiv to [df['A'] > 0, df['B'] > 2 ...]
loc_elements = [df[key] > val for key, val in limits_dic.items()]
df = df.loc[reduce(operator.and_, loc_elements)]