Написать пост

Гайд по обработке данных с помощью Pandas: часть первая

Логотип компании МТС

Разбираем, как начать работу с Pandas. В этой части самые базовые приёмы: чтение и запись данных, индексирование, агрегация и другие основы.

Обложка поста Гайд по обработке данных с помощью Pandas: часть первая

Pandas — это библиотека на Python, предназначенная для обработки и анализа структурированных табличных данных. С её помощью можно фильтровать, сортировать, агрегировать и преобразовывать данные, а также интегрироваться с различными источниками для чтения и записи. Благодаря такому богатому функционалу, эффективности и удобству использования, Pandas — один из наиболее популярных инструментов в области анализа данных.

Руководитель группы видеоаналитики MTS AI Андрей Дугин рассказывает, как начать работу с Pandas и избежать распространённых ошибок.

Где и для чего применяется Pandas

Pandas скорее академический исследовательский инструмент: для промышленных масштабов производительность у библиотеки относительно низкая, управление памятью не самое эффективное и нет параллелизма. Тем не менее, библиотека отлично подходит дата-сайентистам и аналитикам для прототипирования и исследования гипотез в нересурснозатратных проектах. Её можно применить практически в любой области, где требуется обработка и анализ табличных данных или временных рядов:

  • Предиктивная аналитика для агрегаторов. Например, на сайтах недвижимости. Pandas поможет структурировать массив данных по квартирам: этаж, площадь, количество комнат, год постройки дома и так далее. На этих данных можно строить модели машинного обучения и предсказывать, сколько будет стоить недвижимость.
  • Финансовый анализ. Pandas пригодится для анализа динамики цен акций, котировок валют, индексов и так далее. Это помогает при прогнозировании рыночных трендов, определении рисков и оценке инвестиционных стратегий.
  • Маркетинговый анализ. Проанализировав с помощью Pandas данные о продажах, потребительском поведении, рекламных кампаниях можно определять оптимальные стратегии маркетинга.
  • Исследования в области биоинформатики. Pandas помогает анализировать и обрабатывать биологические данные для выявления закономерностей, например, в болезнях.
  • Обработка данных сенсоров и IoT. С помощью Pandas можно обрабатывать и анализировать данные сенсоров, полученных от устройств IoT, чтобы управлять системами, мониторить и диагностировать их.

Рассмотрим некоторые базовые приёмы работы с Pandas на основе классического датасета Titanic. Его используют студенты, изучающие машинное обучение, для предсказания шансов пассажиров на выживание в зависимости от их пола, возраста, класса каюты и других факторов. Подробнее почитать о значении полей можно в описании датасета на Kaggle.

Начало работы с Pandas

DISCLAIMER

  • Поскольку примеры кода и вывод выполняются в Jupyter Notebook, то для отображения результата нам не обязательно оборачивать выражения в функцию print(), однако это может понадобиться в других IDE.
  • По этой же причине при демонстрации мы иногда не модифицируем исходные данные (не делаем присваивание A = B, а показываем только правую часть выражения; используем inplace=False), а просто выводим результат.

Установка и импорт

Pandas легко установить через стандартный пакетный менеджер Python, используя команду pip install pandas.

После установки следует импортировать саму библиотеку. Поскольку Pandas строится на базе библиотеки NumPy — инструментария для работы с многомерными массивами — для удобства рекомендуется импортировать и её. Она также будет установлена автоматически в процессе установки Pandas.

			import numpy as np
import pandas as pd
		

Структуры хранения данных

Данные организованы в структурированные таблицы с индексами, что облегчает манипуляции с ними. Операции в Pandas оптимизированы для работы по столбцам — так во многих случаях производительность выше, чем в операциях по строкам.

Структур хранения данных в Pandas две — Series и DataFrame.

  • Series — это одномерный массив данных с метками. Он может хранить различные типы данных, включая числа, строки и произвольные объекты Python. Каждому элементу в Series соответствует метка, доступ к которой можно получить через атрибут index.
			series = pd.Series([1, 2, 3], index=["a", "b", "c"], dtype=np.uint8)
series
		
Гайд по обработке данных с помощью Pandas: часть первая 1
Здесь и далее будет приводиться вывод в Jupyter Notebook
			series.index
		
Гайд по обработке данных с помощью Pandas: часть первая 2

Есть проводить аналогию со словарём dict, то индекс — это ключи словаря, а сам массив данных — значения, к которым можно получить доступ по ключу:

			series["b"]  # Получаем доступ по метке, как по ключу в словаре
		
Гайд по обработке данных с помощью Pandas: часть первая 3
  • DataFrame — это двумерная структура данных, представляющая собой таблицу с метками для строк и столбцов. Каждый столбец в DataFrame является объектом типа Series. Вместе они формируют двумерную таблицу с общим индексом. В DataFrame присутствуют две оси индексации: index для строк и columns для столбцов. Метки столбцов — это их названия.
			dataframe = pd.DataFrame([[1, "Ivan", 5.0], [2, "Sergey", 4.3], [3, "Dmitry", 4.5]], columns=["#", "Name", "Score"])
dataframe
		
Гайд по обработке данных с помощью Pandas: часть первая 4
			dataframe["Name"]  # Колонка — это объект Series
		
Гайд по обработке данных с помощью Pandas: часть первая 5
			dataframe["Name"].name  # У объекта Series есть собственное имя
		
Гайд по обработке данных с помощью Pandas: часть первая 6

Операции чтения и записи данных

С помощью Pandas можно читать и записывать данные из различных источников: баз данных, файлов в форматах CSV, Excel, JSON и тому подобных. Для каждого типа данных существуют специализированные функции: read_csv(), read_excel() и другие вида read_*().

			titanic = pd.read_csv("titanic.csv")
titanic.head()
		
Гайд по обработке данных с помощью Pandas: часть первая 7

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

			from urllib.parse import quote
url = quote("https://ru.wikipedia.org/wiki/Таблица", safe=":/")  # Кодируем кириллицу
pd.read_html(url)[0]  # Берём первую таблицу из списка всех найденных на веб-странице
		
Гайд по обработке данных с помощью Pandas: часть первая 8

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

Записать данные в файл так же просто, как и прочитать — используйте семейство методов .to_*(), например, .to_excel():

			titanic.to_excel("titanic.xlsx")
		

Первичное исследование данных

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

Первые несколько строк датафрейма можно получить с помощью метода .head(), а последние — .tail():

			titanic.head(5)
		
Гайд по обработке данных с помощью Pandas: часть первая 9

Тип данных object чаще всего соответствует строковым значениям:

			titanic.dtypes
		
Гайд по обработке данных с помощью Pandas: часть первая 10

Pandas позволяет автоматически подобрать наиболее эффективные представления с помощью метода .convert_dtypes():

			titanic.convert_dtypes().dtypes  # Строковые значения теперь представлены типом string, а не object
		
Гайд по обработке данных с помощью Pandas: часть первая 11

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

			titanic.info()
		
Гайд по обработке данных с помощью Pandas: часть первая 12
Например, в датасете 891 строка, однако количество ненулевых (non-null) значений в колонках Age, Cabin и Embarked меньше, значит, некоторые данные отсутствуют

Некоторую статистику по числовым столбцам можно собрать с помощью метода .describe():

			titanic.describe()
		
Гайд по обработке данных с помощью Pandas: часть первая 13

Количество различных значений можно посчитать с помощью .value_counts() — как для категориальных, так и для непрерывных данных:

			titanic["Survived"].value_counts(normalize=True) # Какой процент пассажиров выжил?
		
Гайд по обработке данных с помощью Pandas: часть первая 14
			titanic["Pclass"].value_counts(dropna=False)  # Сколько пассажиров было в каждом классе?
		
Гайд по обработке данных с помощью Pandas: часть первая 15

Индексирование

Операции обращения к элементам данных называются индексированием.

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

			titanic.set_index("Name", inplace=True)  # Производим изменения на месте — inplace
		
			titanic.head()
		
Гайд по обработке данных с помощью Pandas: часть первая 16

Элементы индекса называются метками (лейблами):

			titanic.index  # Это вертикальный индекс; для списка используйте titanic.index.tolist()
		
Гайд по обработке данных с помощью Pandas: часть первая 17

Названия колонок — тоже метки:

			titanic.columns  # Это горизонтальный индекс
		
Гайд по обработке данных с помощью Pandas: часть первая 18

К колонкам можно обращаться по их названиям:

			titanic["Ticket"]
		
Гайд по обработке данных с помощью Pandas: часть первая 19

Если имя колонки удовлетворяет правилам именования переменных в Python и не совпадает с одним из уже существующих методов или атрибутов DataFrame, то к колонке можно обратиться как к атрибуту DataFrame:

			titanic.Ticket;  # То же самое, что titanic["Ticket"]
		

Метод .loc[] есть и у Series, и у DataFrame. Он позволяет обращаться к данным по меткам:

			titanic.loc["Heikkinen, Miss. Laina", "Ticket"]  # Метод объекта DataFrame
		
Гайд по обработке данных с помощью Pandas: часть первая 20
			titanic["Ticket"].loc["Heikkinen, Miss. Laina"]  # Метод объекта Series
		
Гайд по обработке данных с помощью Pandas: часть первая 21
			titanic.loc["Heikkinen, Miss. Laina", ["Ticket", "Age"]]  # Можно выбрать несколько строк или колонок
		
Гайд по обработке данных с помощью Pandas: часть первая 22

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

			titanic.iloc[2, 7]  # Нумерация начинается с нуля
		
Гайд по обработке данных с помощью Pandas: часть первая 23
			titanic.iloc[0:3, 2:8]
		
Гайд по обработке данных с помощью Pandas: часть первая 24

С помощью .at[] можно обратиться к конкретной ячейке данных — как в Excel:

			titanic.at["Heikkinen, Miss. Laina", "Ticket"]
		
Гайд по обработке данных с помощью Pandas: часть первая 25

При совершении операций над объектами DataFrame и Series Pandas пытается сопоставить элементы с одинаковыми индексами. Рассмотрим для примера последовательность Series с геометрической прогрессией, где каждый следующий элемент больше предыдущего в два раза:

			series = pd.Series(2 ** np.arange(10))
series
		
Гайд по обработке данных с помощью Pandas: часть первая 26

Попытка попарно посчитать отношение следующего элемента к предыдущему приведёт к «странному» результату:

			series[1:] / series[:-1]
		
Гайд по обработке данных с помощью Pandas: часть первая 27

На самом деле мы хотели получить следующее:

			series[1:] / series[:-1].values  # У массива .values уже нет индекса, и элементы сопоставляются просто последовательными парами
		
Гайд по обработке данных с помощью Pandas: часть первая 28

Объединение данных

Для объединения нескольких датафреймов можно использовать близкие по смыслу функции: merge(), concat() и join(). Вот несколько базовых примеров:

			df1 = pd.DataFrame({"key": ["A", "B", "C", "D"], "value": [1, 2, 3, 4]})
df1
		
Гайд по обработке данных с помощью Pandas: часть первая 29
			df2 = pd.DataFrame({"key": ["B", "D", "E", "F"], "value": [5, 6, 7, 8]})
df2
		
Гайд по обработке данных с помощью Pandas: часть первая 30
			pd.merge(df1, df2, on="key", how="inner")
		
Гайд по обработке данных с помощью Pandas: часть первая 31
			pd.concat([df1, df2], axis=0, ignore_index=True)
		
Гайд по обработке данных с помощью Pandas: часть первая 32
			df1.join(df2, how="inner", lsuffix="_1", rsuffix="_2")
		
Гайд по обработке данных с помощью Pandas: часть первая 33

Агрегация данных

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

  • groupby() — для группировки и агрегации данных с большой гибкостью;
  • pivot_table() — для создания сводных таблиц с возможностью применения множественных агрегаций;
  • crosstab() — для подсчёта частоты встречаемости категорий.

Рассчитаем средний возраст пассажиров с разбиением по полу:

			titanic.groupby("Sex").Age.mean()
		
Гайд по обработке данных с помощью Pandas: часть первая 34

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

			pd.pivot_table(titanic, values="Survived", index="Sex", columns="Pclass", aggfunc="mean")
		
Гайд по обработке данных с помощью Pandas: часть первая 35

Посчитаем, сколько человек каждого пола было в трёх классах кают:

			pd.crosstab(titanic.Sex, titanic.Pclass)
		
Гайд по обработке данных с помощью Pandas: часть первая 36

Групповые операции

При обработке данных новички часто используют циклы для итераций по строкам или ячейкам. Такой код выполняется медленно. Лучше использовать методы .apply(), .map(), .transform(), которые работают быстрее за счёт векторизации.

			# Функция, которая в зависимости от возраста будет возвращать child/adult/senior
def categorize_age(age):
  if age < 18:
    return"child"
  if age < 60:
    return"adult"
  else:       
    return "senior"

# Создадим новую колонку, применив нашу функцию к существующей
titanic["Age_category"] = titanic["Age"].apply(categorize_age)
titanic[["Age", "Age_category"]].head()
		
Гайд по обработке данных с помощью Pandas: часть первая 37

Кстати, такое же разбиение на возрастные группы можно выполнить с помощью функции pd.cut():

			pd.cut(titanic.Age, bins=[0, 18, 60, titanic.Age.max()], labels=["child", "adult", "senior"]).head()
		
Гайд по обработке данных с помощью Pandas: часть первая 38

Работа с пропущенными данными

В реальных датасетах часть данных может отсутствовать. В библиотеке Pandas эти пропуски обычно представлены как NaN (Not a Number) в ячейках DataFrame или Series. Чтобы с ними работать можно использовать ключевые методы:

  • .isna() — идентифицирует отсутствующие значения и возвращает булевую маску, где True указывает на пропущенные данные;
  • .fillna() — позволяет заполнить отсутствующие значения с помощью указанных значений или методов интерполяции;
  • .dropna() — удаляет строки или столбцы, содержащие отсутствующие значения.

Метод .isna() есть как у DataFrame, так и у Series, а результатом его вызова будет булевая маска того же класса:

			titanic.isna().head()
		
Гайд по обработке данных с помощью Pandas: часть первая 39
			titanic.loc[titanic.Age.isna()]  # Найдём строки, в которых отсутствует возраст пассажира
		
Гайд по обработке данных с помощью Pandas: часть первая 40
			titanic.dropna(subset=["Age"], inplace=False) # Удалим строки, в которых отсутствует возраст пассажира
		
Гайд по обработке данных с помощью Pandas: часть первая 41

Если датасет небольшой, то удалять отсутствующие данные слишком расточительно. Лучше придумать, как заполнить недостающие значения.

Например, можно заменить их средними значениями в столбце:

			titanic.loc[titanic.Age.isna(), "Age"] = titanic.Age.mean()
		

Или заполнить предыдущим, либо последующим значением:

			titanic.Age.fillna(method="ffill", inplace=True)
		
Методы генерации и заполнения отсутствующих значений называются импутингом. В библиотекеscikit-learnесть модульsklearn.impute, в котором представлены несколько классов импутеров, основанных на различных алгоритмах.

Визуализация данных

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

			import matplotlib.pyplot as plt
titanic.loc[titanic.Sex == "male", "Age"].hist(bins=10);
		
Гайд по обработке данных с помощью Pandas: часть первая 42

То же самое можно сделать через прямой вызов функций matplotlib:

			plt.hist(titanic[titanic.Sex == "male"].Age, bins=10);
plt.grid(True)
plt.title("Распределение пассажиров-мужчин по возрасту");
plt.xlabel("Возрастной диапазон");
plt.ylabel("Количество человек");
		
Гайд по обработке данных с помощью Pandas: часть первая 43

Работа с временными рядами

Индексная колонка датафрейма представлена датой и временем, что позволяет индексировать данные по дате и времени, ресемплировать (осуществлять выборку с большей или с меньшей частотой), интерполировать отсутствующие данные, применять оконные функции.

Допустим, у нас есть статистика перевозки пассажиров некой авиакомпании. Сгенерируем эти данные по месяцам:

			# Сгенерируем диапазон дат с частотой 1 месяц
date_rng = pd.date_range(start="1949-01-01", end="1960-12-01", freq="MS")

# Создадим соответствующий датафрейм
df = pd.DataFrame(date_rng, columns=["date"])

# Случайным образом сгенерируем колонку с числом пассажиров в каждом месяце
df["passengers"] = np.random.randint(100, 500, size=len(date_rng))

# Сделаем колонку с датами индексной
df.set_index("date", inplace=True)

df.head()
		
Гайд по обработке данных с помощью Pandas: часть первая 44

А теперь рассчитаем среднемесячное количество пассажиров по каждому году:

			df_downsampled = df.resample("A").mean()
		
			plt.figure(figsize=(10, 6))
plt.plot(df.index, df["passengers"], label="Исходные данные")
plt.plot(df_downsampled.index, df_downsampled["passengers"], label="Среднее по годам", linestyle="--")
plt.xlabel("Год")
plt.ylabel("Количество пассажиров")
plt.grid(True)
plt.legend()
plt.show()
		
Гайд по обработке данных с помощью Pandas: часть первая 45

Применим скользящее окно и рассчитаем среднее количество пассажиров за последние 12 месяцев:

			df_rolled = df.rolling(12).mean()
		
			df_rolled.head(15)
		
Гайд по обработке данных с помощью Pandas: часть первая 46

В первых 11 строках по умолчанию появились значения NaN, так как количество проанализированных строк меньше размера окна. Это поведение можно изменить, задав параметр min_periods — минимальное количество строк, для которых определён результат:

			df.rolling(12, min_periods=1).mean().head()
		
Гайд по обработке данных с помощью Pandas: часть первая 47

В заключение

В этой части статьи мы рассмотрели, как использовать Pandas для разных базовых задач. Во второй части разберём, как сделать работу с Pandas эффективнее, узнаем, какие есть альтернативы для этой библиотеки и какие источники можно почитать, чтобы ещё лучше в ней ориентироваться. Следите за обновлениями!

Следите за новыми постами
Следите за новыми постами по любимым темам
8К открытий11К показов