Рубріки: Теория

Способы фильтрации данных в Pandas

Роман Белый

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

В этой статье мы рассмотрим наиболее распространенные операции для выбора подмножества данных из фрейма данных Pandas. Но если вы захотите более подробно вникнуть в этот вопрос, то советуем записаться на курс от наших партнеров Mate Academy.

Содержание:
1. Общая информация и подготовка данных
2. Фильтрация данных pandas
3. Вспомогательные функции для фильтрации
4. Фильтруем пропуски в данных
5. Построение аналитики по сгруппированным данным
6. Как фильтровать данные без использования пакета pandas
В заключение (бонус видео)

1. Общая информация и подготовка данных

Задачи фильтрации являются постоянными спутниками как исследователей данных, так и разработчиков. Одним из вариантов пакетов для работы над такими задачами в Python может быть Pandas, предоставляющий доступ к DataFrame, который является двумерной помеченной структурой данных со столбцами потенциально разных типов.

Концепция аналогична таблице в реляционной базе данных, если вы храните в ней только атомарные значения. Однако, будучи библиотекой Python, Pandas DataFrame естественным образом позволяет хранить объекты в своих ячейках. Это считается плохой практикой в реляционной базе данных, поскольку нарушается первая нормальная форма, что значительно усложняет SQL-запросы. Выбор за вами!

Посмотрим на имеющиеся возможности. Запускаем jupyter notebook. В первую очередь, импортируем библиотеку:

import pandas as pd

Создаем тестовый набор данных, используя конструктор из словаря:

df = pd.DataFrame({'name': ['Taras','Ivan','Stas'], 'surname': ['Shevchenko','Kozak','Gerb'], 'height': [184,170,176]})
# смотрим что внутри df
df
name surname height
0 Taras Shevchenko 184
1 Ivan Kozak 170
2 Stas Gerb 176

2. Фильтрация данных pandas

Метод 1: DataFrame Way

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

df_tall = df[df['height'] > 175]

Из полученного DF отображаем только перечисленные в списке столбцы:

df_tall[['name','surname']]

Множественные условия фильтрации

Мы можем объединить несколько условий, используя оператор | и & (ИЛИ и И), оборачивая при этом логические условия в () :

df_tall_named = df[((df['height'] > 175) | (df['name'] == 'Taras')) & (df['surname'] == 'Shevchenko')]
df_tall_named

На месте ‘height‘, 175, ‘name‘ и ‘Taras‘ могут быть переменные, значения функций, данные из столбцов DataFrame.

Метод 2: Query Function

Переформулируем нужный нам фильтр, описанный выше, на языке запросов query:

max_height = 175
chosen_name = 'Taras'
df_tall_named_sec = df.query('height > @max_height & name == @chosen_name')
df_tall_named_sec

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

df_tall_named_trd = df.query('height < 175 & `name` != "Taras Shevchenko"')
df_tall_named_trd

Метод 3: loc function

DataFrame.loc[] — предоставляет доступ к группе строк и столбцов по меткам или булевому массиву. Рассмотрим несколько примеров использования:

# получаем доступ к строке с индексом 0:
df.loc[0]

Такой синтаксис дает возможность получать доступ к элементам, содержащимся в выбранной строке:

df.loc[0]["height"]

Или то же самое:

df.loc[0][2]

У колонки “height” индекс равен 2.

Передаем список из нужным нам индексов строк:

df.loc[[0,0,1,1,2,2,2]]

Если в результате фильтрации получено более одной строки, то возвращается объект DataFrame:

# выделяем строки с 1 по 2:
df.loc[1:2]
# выделяем все строки в указанных в списке столбцах
df.loc[:,["name","surname"]]

Первая часть выражения в [] задает диапазон строк — в нашем случае, весь имеющийся — в который включается как первый, так и последний индексы (которые могут быть не только числовыми, но принцип остается тот же). Вторая часть выражения определяет из каких столбцов будут выбираться указанные строки (передается как список из нужных столбцов или имя столбца).

В качестве аргумента в функцию .loc[] можно подставлять логическую маску с нужным условием фильтрации:

loc_df_sec = df.loc[df.height > 175]
loc_df_sec

Совмещаем вместе синтаксис из двух последних примеров:

loc_df_thr = df.loc[df.height > 175,["name","surname"]]
loc_df_thr

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

df.loc[lambda df: df['height'] >= 170, "name"]

Метод 4: iloc function

Рассмотрим функцию .iloc[], которая принимает целочисленные аргументы разных типов в качестве нужной нам позиции в DF (от 0 до длины -1 оси), но также может использоваться с булевым массивом. Посмотрим на примеры работы:

df.iloc[0]

Попали в нулевую строку, представленную в виде индексированного Series.

print(df.iloc[0].index)

В качестве индексов у нас наименования столбцов из исходного DF:

print(df.iloc[0].index[0])

Можно доставать индексы, они лежат в списке. Следующий пример:

df.iloc[0][0]

Попали в значение элемента. Похожая с .loc[] механика. В качестве упражнения запишем то же самое иначе:

# первый аргумент — числовой индекс строки, второй — столбца:
df.iloc[0,0]

В качестве аргументов можем передавать слайсы:

df.iloc[0:3,0:3]

И лямбда-функции:

df.iloc[lambda x: range(3), lambda x: range(3)]

Функция .iloc[] позволяет выбрать строки с указанными индексами в нужном порядке, передав в качестве аргумента в нее упорядоченный слева направо список номеров нужных строк:

df.iloc[[0,2,1]]

Также можно указать нужные столбцы:

df.iloc[[0,0,1,1,2],[1,1,2]]

Как вы заметили, синтаксис позволяет повторы нужных вам строк/столбцов.

Еще один способ передать в .iloc[] информацию о нужных столбцах, используя функцию get_indexer(), которая принимает список нужных столбцов с их названиями:

df.iloc[:, df.columns.get_indexer(['name', 'height'])]

Освоить язык программирования Python вам могут помочь онлайн курсы от наших партнеров Mate Academy и Powercode. Получите качественный уровень знаний от практикующих специалистов.

Разница между функциями loc и iloc

Основная разница двух методов состоит в том, что .loc[] работает с метками столбцов и строк, в то время как iloc[] работает с целочисленными позициями нужных элементов.

3. Вспомогательные функции для фильтрации

Давайте создадим функцию, которая будет генерировать столбец, содержащий True/False в зависимости от расчетного параметра по указанному столбцу в DataFrame. Для этого воспользуемся функциями .stack() и .unstack(), которые добавляют и избавляются от уровня индекса из столбца, а также функцией .apply(), которая применит к выбранному массиву данных лямбда-функцию:

mask = df.iloc[:,2:3].stack().apply(lambda x: int(x) > 175).unstack()
# фильтруем всех, по указанной маске, подставляя в [] обернутый в () столбец с результатом apply, который у нас лежит в поле height
df[(mask.height)]

Использование побитового отрицания

Интересный факт: мы можем применить побитовое отрицание к маске, поставляемой в [] и получить противоположный описанным условиям выше результат фильтрации. Для этого используем тильду:

df[~(mask.height)]

Проверка на вхождение символов в значение столбца

Выделим из исходной таблицы всех, у кого в имени (поле ‘name’) есть буква ‘T’:

df[df['name'].str.contains('T')]

Сгруппируем строки, содержащие в имени ‘v‘ или ‘S‘:

df[df['name'].str.contains('v') | df['name'].str.startswith('S')]

Отфильтруем строки, имеющие длину больше трех в столбце фамилия (поле “surname”):

Использование .stack() и .apply() для формирования логической маски фильтра:

mask_surn = df.iloc[:,1:2].stack().apply(lambda x: len(x) >= 5).unstack()
df[(mask_surn.surname)]

4. Фильтруем пропуски в данных

Часто бывает, что нам нужно убрать из набора данных те части, в которых есть пропуски. Для начала добавим еще одну строку в наш DF, используя .append():

df=df.append({"name":"Tony","surname":"Stark","height":None},ignore_index=True)

Теперь вызовем метод .isnull() и просуммируем результаты:

df.isnull().sum()

Видим, что появилась одна строка с отсутствующими данными. Чтобы избавиться от нее, и от всех других строк с пропусками, используем метод .dropna():

df = df.dropna()

Все. Строк с пропусками в данных больше нет.

5. Построение аналитики по сгруппированным данным

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

h_calc_df = df[df.height > 175].groupby("height").height.aggregate(lambda x: (x-165)/165*100)
h_calc_df

Сначала мы фильтруем DF по полю “height”, которое должно быть более 175. После чего группируем результаты по росту и применяем к каждому значению поля рост лямбда-функцию. Значения поля “height” исходной таблицы становятся индексами в сгруппированном результате.

6. Как фильтровать данные без использования пакета pandas

Посмотрим на примеры организации фильтрации списков средствами чистого Python. Допустим, у нас есть список с int данными и мы хотим выделить список элементов, которые меньше 8:

some_lst = [10, 9, 0, 8, 2, 6, 4, 3, 1, 7, 5]
small = [x for x in some_lst if x < 8]
small

По такому же принципу выделим четные элементы:

even = [x for x in some_lst if x%2==0]
even

Нечетные:

odd = [x for x in some_lst if x%2]
odd

Переформулируем фильтр для чисел меньше 8, используя встроенную в Python функцию filter(func,iterable), которая принимает в качестве первого аргумента функцию, возвращающую логический ответ на вопрос «должен ли текущий элемент списка отображаться фильтром», а второй аргумент — итерируемый объект.

Посмотрим на код:

small = filter(lambda x: x<8, some_lst)
list(small)

Так как функция filter() возвращает итерируемый объект, его нужно обернуть в list(), чтобы прочитать.

Генератор списка можно подставлять в качестве аргумента в функции, что позволяет более лаконично писать код и не ухудшать читаемость. Посчитаем сумму всех элементов списка, меньших 8:

sum(x for x in some_lst if x < 8)

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

def condition(x): 
    return x > 3 and x < 7 
filtered = [x for x in some_lst if condition(x)] 
filtered

Отфильтруем теперь данные одного списка по условию, рассчитанному на базе данных другого списка:

some_lst = [10, 9, 0, 8, 2, 6, 4, 3, 1, 7, 5]
b_lst = [True, False, False, True, True, False, True, False, True, False,True]
res = [x for (x, boo) in zip(some_lst, b_lst) if boo]
res

Такие же техники можно применять к словарям с данными.

В заключение (бонус видео)

Пакет Python Pandas как и сам Python дает широкие возможности для манипуляции с данными, препроцессинга и фильтрации. В проектах, где нет критической необходимости переносить логику фильтрации на сторону базы данных — использование таких пакетов и техник может быть оправдано.

В силу сложности представленной темы, прикладываем полезное тематическое видео по работе с индексами в Pandas:

Останні статті

Обучение Power BI – какие онлайн курсы аналитики выбрать

Сегодня мы поговорим о том, как выбрать лучшие курсы Power BI в Украине, особенно для…

13.01.2024

Work.ua назвал самые конкурентные вакансии в IТ за 2023 год

В 2023 году во всех крупнейших регионах конкуренция за вакансию выросла на 5–12%. Не исключением…

08.12.2023

Украинская IT-рекрутерка создала бесплатный трекер поиска работы

Unicorn Hunter/Talent Manager Лина Калиш создала бесплатный трекер поиска работы в Notion, систематизирующий все этапы…

07.12.2023

Mate academy отправит работников в 10-дневный оплачиваемый отпуск

Edtech-стартап Mate academy принял решение отправить своих работников в десятидневный отпуск – с 25 декабря…

07.12.2023

Переписки, фото, история браузера: киевский программист зарабатывал на шпионаже

Служба безопасности Украины задержала в Киеве 46-летнего программиста, который за деньги устанавливал шпионские программы и…

07.12.2023

Как вырасти до сеньйора? Девелопер создал популярную подборку на Github

IT-специалист Джордан Катлер создал и выложил на Github подборку разнообразных ресурсов, которые помогут достичь уровня…

07.12.2023