Способы фильтрации данных в 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:
Сообщить об опечатке
Текст, который будет отправлен нашим редакторам: