Я пытаюсь отфильтровать кадр данных панды, используя пороги для трех столбцов

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}. Буду признателен за любые мысли по этому поводу.

21
ahoosh 28 Авг 2017 в 21:33

3 ответа

Лучший ответ

Если вы пытаетесь построить динамический запрос, есть более простые способы. Вот пример использования списка и str.join:

query = ' & '.join(['{}>{}'.format(k, v) for k, v in limits_dic.items()])

Или, используя f - строки с 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 ().

38
cs95 16 Дек 2018 в 04:56

Альтернатива версии @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])
1
Victor Yan 1 Дек 2018 в 15:44

Альтернатива обоим опубликованным, которая может быть или не быть более питонической:

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)]
0
yvesva 17 Окт 2019 в 04:09