Зарегистрируйтесь, чтобы продолжить обучение

Фильтрация значений и подготовка данных для анализа Python: Pandas

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

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

Использование булевых масок

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

import pandas as pd

df_clicks = pd.read_csv('./data/Cite_clicks_week.csv', index_col=0)
print(df_clicks)
# =>    SHOP1  SHOP2   SHOP3  SHOP4
# day
# 1    1319.0 -265.0   319.0    NaN
# 2       NaN  267.0   333.0  344.0
# 3     283.0    NaN   274.0  283.0
# 4     328.0  364.0   328.0    NaN
# 5     391.0  355.0   373.0  337.0
# 6     445.0 -418.0  1409.0  445.0
# 7     481.0    NaN   481.0  409.0

Среди показателей есть NaN-значения, которые указывают на пропуски в данных. Также есть отрицательные значения и показатели, которые существенно выше всех остальных. Это вполне обычная ситуация.

Такие значения могут влиять на точность анализа данных и даже на возможность его проведения. Поэтому аналитику приходится находить их и исправлять.

Структура DataFrame поддерживает операции среза последовательных элементов в данных по аналогии со стандартными списками:

print(df_clicks[1:5])
# => SHOP1  SHOP2   SHOP3   SHOP4
# day
# 2 NaN     267.0   333.0   344.0
# 3 283.0   NaN     274.0   283.0
# 4 328.0   364.0   328.0   NaN
# 5 391.0   355.0   373.0   337.0

Чтобы извлечь конкретные строки, можно использовать булевы маски. Это массивы значений True и False. Строка берется, если по ее порядковому номеру в булевой маске стоит значение True:

print(df_clicks[[True, True, False, True, False, True, False]])
# =>    SHOP1  SHOP2   SHOP3  SHOP4
# day
# 1    1319.0 -265.0   319.0    NaN
# 2       NaN  267.0   333.0  344.0
# 4     328.0  364.0   328.0    NaN
# 6     445.0 -418.0  1409.0  445.0

Чтобы извлечь конкретные столбцы в срезе, используется метод loc(), в параметры которого передается срез и массив меток столбцов:

print(df_clicks.loc[1:5, ['SHOP1']])
# => SHOP1
# day
# 1 1319.0
# 2 NaN
# 3 283.0
# 4 328.0
# 5 391.0

print(df_clicks.loc[1:5, ['SHOP1', 'SHOP3']])
# => SHOP1  SHOP3
# day
# 1 1319.0  319.0
# 2 NaN     333.0
# 3 283.0   274.0
# 4 328.0   328.0
# 5 391.0   373.0

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

print(df_clicks['SHOP1'] < 300)
# => day
# 1    False
# 2    False
# 3     True
# 4    False
# 5    False
# 6    False
# 7    False
# Name: SHOP1, dtype: bool

Чтобы получить нужные строки согласно булевой маске, достаточно передать ее в качестве индекса в DataFrame:

print(df_clicks[df_clicks['SHOP1'] < 300])
# => SHOP1  SHOP2   SHOP3   SHOP4
# day
# 3 283.0   NaN 274.0   283.0

Выбор нужных столбцов в таблице осуществляется по аналогии с примером выше:

print(df_clicks.loc[df_clicks['SHOP1'] < 300, ['SHOP1', 'SHOP3']])
# => SHOP1  SHOP3
# day
# 3  283.0  274.0

Поиск пропусков в данных

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

print(df_clicks[df_clicks['SHOP1'].isna()])
# => SHOP1  SHOP2   SHOP3   SHOP4
# day
# 2    NaN  267.0   333.0   344.0

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

print(df_clicks[~df_clicks['SHOP1'].isna()])
# => SHOP1   SHOP2  SHOP3   SHOP4
# day
# 1 1319.0  -265.0  319.0   NaN
# 3 283.0      NaN  274.0   283.0
# 4 328.0    364.0  328.0   NaN
# 5 391.0    355.0  373.0   337.0
# 6 445.0   -418.0  1409.0  445.0
# 7 481.0      NaN  481.0   409.0

Данный метод применим не только к столбцам, но и ко всей таблице:

print(df_clicks.isna())
# => SHOP1  SHOP2   SHOP3   SHOP4
# day
# 1 False   False   False   True
# 2 True    False   False   False
# 3 False   True    False   False
# 4 False   False   False   True
# 5 False   False   False   False
# 6 False   False   False   False
# 7 False   True    False   False

Замена пропусков на определенные значения

Когда мы находим пропуски в данных, это помогает контролировать их качество. Но часто приходится не только их находить, но и исправлять.

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

df_clicks_mean = df_clicks.mean().mean()
print(df_clicks_mean)
# => 366.94

Чтобы заполнить пропуски, используется метод fillna() с параметром, на который происходит замена пропущенных значений:

print(df_clicks.fillna(df_clicks_mean))
# =>        SHOP1      SHOP2   SHOP3      SHOP4
# day
# 1    1319.00000 -265.00000   319.0  366.94881
# 2     366.94881  267.00000   333.0  344.00000
# 3     283.00000  366.94881   274.0  283.00000
# 4     328.00000  364.00000   328.0  366.94881
# 5     391.00000  355.00000   373.0  337.00000
# 6     445.00000 -418.00000  1409.0  445.00000
# 7     481.00000  366.94881   481.0  409.00000

Замена значений в данных согласно условию

Помимо пропусков в данных могут быть значения, которые попали туда по ошибке, были некорректно введены или искажены при записи. Такие случаи называют выбросами.

Анализ данных и поиск выбросов приводит к формированию некоторого условия, которому должны удовлетворять элементы. Если элементы ему не удовлетворяют, то данные значения можно заменить на среднее. Для этого используют метод where():

print(df_clicks.where(df_clicks < 1000, df_clicks_mean))
# =>    SHOP1       SHOP2       SHOP3       SHOP4
# day
# 1 366.94881   -265.00000  319.00000   366.94881
# 2 366.94881   267.00000   333.00000   344.00000
# 3 283.00000   366.94881   274.00000   283.00000
# 4 328.00000   364.00000   328.00000   366.94881
# 5 391.00000   355.00000   373.00000   337.00000
# 6 445.00000   -418.00000  366.94881   445.00000
# 7 481.00000   366.94881   481.00000   409.00000

При работе с Pandas это условие формируется в виде булевой маски. Для удобства ее выносят в отдельную переменную:

mask = (0 < df_clicks) & (df_clicks < 1000)
print(mask)
# =>   SHOP1  SHOP2  SHOP3  SHOP4
# day
# 1    False  False   True  False
# 2    False   True   True   True
# 3     True  False   True   True
# 4     True   True   True  False
# 5     True   True   True   True
# 6     True  False  False   True
# 7     True  False   True   True
print(df_clicks.where(
    mask,
    df_clicks_mean,
    )
)
# =>       SHOP1      SHOP2      SHOP3      SHOP4
# day
# 1    366.94881  366.94881  319.00000  366.94881
# 2    366.94881  267.00000  333.00000  344.00000
# 3    283.00000  366.94881  274.00000  283.00000
# 4    328.00000  364.00000  328.00000  366.94881
# 5    391.00000  355.00000  373.00000  337.00000
# 6    445.00000  366.94881  366.94881  445.00000
# 7    481.00000  366.94881  481.00000  409.00000

Выводы

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


Аватары экспертов Хекслета

Остались вопросы? Задайте их в разделе «Обсуждение»

Вам ответят команда поддержки Хекслета или другие студенты

Для полного доступа к курсу нужен базовый план

Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.

Получить доступ
1000
упражнений
2000+
часов теории
3200
тестов

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов
Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»

Наши выпускники работают в компаниях:

Логотип компании Альфа Банк
Логотип компании Aviasales
Логотип компании Yandex
Логотип компании Tinkoff
Рекомендуемые программы
профессия
от 24 542 ₸ в месяц
новый
Сбор, анализ и интерпретация данных
9 месяцев
с нуля
Старт 26 декабря

Используйте Хекслет по-максимуму!

  • Задавайте вопросы по уроку
  • Проверяйте знания в квизах
  • Проходите практику прямо в браузере
  • Отслеживайте свой прогресс

Зарегистрируйтесь или войдите в свой аккаунт

Отправляя форму, вы принимаете «Соглашение об обработке персональных данных» и условия «Оферты», а также соглашаетесь с «Условиями использования»