как вы фильтруете фреймы данных pandas по нескольким столбцам


103

Чтобы отфильтровать фрейм данных (df) по одному столбцу, если мы рассмотрим данные с мужчинами и женщинами, мы могли бы:

males = df[df[Gender]=='Male']

Вопрос 1. Но что, если данные охватывают несколько лет, и я хотел бы видеть только мужчин за 2014 год?

На других языках я мог бы сделать что-нибудь вроде:

if A = "Male" and if B = "2014" then 

(за исключением того, что я хочу сделать это и получить подмножество исходного фрейма данных в новом объекте фрейма данных)

Вопрос 2. Как сделать это в цикле и создать объект фрейма данных для каждого уникального набора года и пола (т.е. df для: 2013-Мужской, 2013-Женский, 2014-Мужской и 2014-Женский

for y in year:

for g in gender:

df = .....

Вы хотите его отфильтровать или сгруппировать ? Если вы хотите создать отдельный DataFrame для каждого уникального набора года и пола, посмотрите groupby.
BrenBarn

1
Этот ответ дает исчерпывающий обзор логической индексации и логических операторов в пандах.
cs95

Ответы:


183

Используя &оператор, не забудьте заключить подвыражения в ():

males = df[(df[Gender]=='Male') & (df[Year]==2014)]

Чтобы сохранить фреймы данных в dictцикле for:

from collections import defaultdict
dic={}
for g in ['male', 'female']:
  dic[g]=defaultdict(dict)
  for y in [2013, 2014]:
    dic[g][y]=df[(df[Gender]==g) & (df[Year]==y)] #store the DataFrames to a dict of dict

РЕДАКТИРОВАТЬ:

Демо для вашего getDF:

def getDF(dic, gender, year):
  return dic[gender][year]

print genDF(dic, 'male', 2014)

отличный ответ zhangxaochen - не могли бы вы отредактировать свой ответ, чтобы показать внизу, как вы можете сделать цикл for, который создает фреймы данных (с данными года и пола), но добавляет их в словарь, чтобы к ним можно было получить доступ позже с помощью моего метода getDF? def GetDF (dict, key): return dict [key]
yoshiserry

@yoshiserry , что это keyкак в вашей getDF? один параметр или набор ключей? будьте конкретны плз;)
zhangxaochen

привет, это единственный ключ, просто слово, которое будет соответствовать полу (мужской или женский) или году (13, 14). Не знал, что у вас может быть кортеж ключей. Не могли бы вы привести пример того, когда и как бы вы это сделали?
yoshiserry

не могли бы вы взглянуть и на этот вопрос. Я чувствую, что ты мог бы на это ответить. Снова относится к фреймам данных pandas. stackoverflow.com/questions/22086619/…
yoshiserry

1
Обратите внимание , что Genderи Yearоба должны быть строками, т.е. 'Gender'и 'Year'.
Стивен К. Хауэлл

22

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

df = df[df[['col_1','col_2']].apply(lambda x: f(*x), axis=1)]

где f - это функция, которая применяется к каждой паре элементов (x1, x2) из ​​col_1 и col_2 и возвращает True или False в зависимости от любого условия, которое вы хотите (x1, x2).


13

Начните с панды 0.13 , это наиболее эффективный способ.

df.query('Gender=="Male" & Year=="2014" ')

1
Почему это должно быть более эффективным, чем принятый ответ?
Bouncner 04

@Bouncner, просто сверьте это с высоко оцененным ответом.
redreamality

6
Этот ответ можно улучшить,
указав

10

Если кому-то интересно, какой способ фильтрации более быстрый (принятый ответ или ответ от @redreamality):

import pandas as pd
import numpy as np

length = 100_000
df = pd.DataFrame()
df['Year'] = np.random.randint(1950, 2019, size=length)
df['Gender'] = np.random.choice(['Male', 'Female'], length)

%timeit df.query('Gender=="Male" & Year=="2014" ')
%timeit df[(df['Gender']=='Male') & (df['Year']==2014)]

Результаты для 100 000 строк:

6.67 ms ± 557 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
5.54 ms ± 536 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Результаты для 10 000 000 строк:

326 ms ± 6.52 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
472 ms ± 25.1 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

Так что результаты зависят от размера и данных. На моем ноутбуке query()становится быстрее после 500 тыс. Строк. Кроме того, поиск по строке Year=="2014"имеет ненужные накладные расходы ( Year==2014быстрее).


2
Однако я думаю, что queryсинтаксис более аккуратный и близок к SQL, что с тех пор делает его удобным для данных. Самое приятное в том, что он быстрее с множеством строк :)
csgroen

1

Вы можете создать свою собственную функцию фильтра, используя queryin pandas. Здесь есть фильтрация dfрезультатов по всем kwargsпараметрам. Не забудьте добавить несколько валидаторов ( kwargsфильтрацию), чтобы получить функцию фильтрации для себя df.

def filter(df, **kwargs):
    query_list = []
    for key in kwargs.keys():
        query_list.append(f'{key}=="{kwargs[key]}"')
    query = ' & '.join(query_list)
    return df.query(query)

Спасибо за шикарное решение! Думаю, это лучший из всех остальных. Он сочетает в себе эффективность использования запроса с универсальностью использования его в качестве функции.
A Merii

0

Вы можете фильтровать по нескольким столбцам (более двух), используя np.logical_andоператор для замены &(или np.logical_orдля замены| )

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

def filter_df(df, filter_values):
    """Filter df by matching targets for multiple columns.

    Args:
        df (pd.DataFrame): dataframe
        filter_values (None or dict): Dictionary of the form:
                `{<field>: <target_values_list>}`
            used to filter columns data.
    """
    import numpy as np
    if filter_values is None or not filter_values:
        return df
    return df[
        np.logical_and.reduce([
            df[column].isin(target_values) 
            for column, target_values in filter_values.items()
        ])
    ]

Применение:

df = pd.DataFrame({'a': [1, 2, 3, 4], 'b': [1, 2, 3, 4]})

filter_df(df, {
    'a': [1, 2, 3],
    'b': [1, 2, 4]
})
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.