Pandas — это золотой стандарт в обработке данных. А функциональные возможности библиотеки по загрузке, фильтрации, обработке и изучению данных быстро сделали ее излюбленным инструментом аналитиков.
Конечно же, большинство из нас работает с самыми примитивными возможностями: загрузка данных из CSV
-файла, фильтр нескольких столбцов и переход к визуализации данных. Однако в Pandas есть и малоизвестные, но от того не менее полезные функции, которые значительно упрощают обработку данных.
В данной статье мы рассмотрим 5 продвинутых функций, узнаем, для чего они нужны, и как ими пользоваться. Теперь обработка данных станет еще веселее!
(1) Настройка опций и параметров
Pandas идет в комплекте с набором настраиваемых опций и параметров. Они отлично повышают продуктивность аналитиков, поскольку позволяют им настроить Pandas-среду под себя.
Например, в настройках отображения можно задать количество показываемых строк и столбцов, а также настроить количество знаков после запятой.
import pandas as pd
display_settings = {
'max_columns': 10,
'expand_frame_repr': True, # Развернуть на несколько страниц
'max_rows': 10,
'precision': 2,
'show_dimensions': True
}
for op, value in display_settings.items():
pd.set_option("display.{}".format(op), value)
В коде выше показывается, что Pandas может отображать не более 10 строк и 10 столбцов, а максимально допустимое количество знаков после запятой — 2. Таким образом, при добавлении большого DataFrame
в терминал или Jupyter Notebook, он не превратится в мешанину из данных.
Это лишь базовый пример. Существует куда больше полезных возможностей Pandas. Почитать о других опциях можно в официальной документации.
(2) Объединение DataFrame
Мало кто знает, что DataFrame
в Pandas можно объединить несколькими способами. Каждый из этих способов выдает разный результат, поэтому при выборе руководствуйтесь собственными целями. Кроме того, в оба способа заложены наборы параметров для более глубокой настройки слияния. Давайте поговорим о них подробнее.
Конкатенация
Конкатенация относится к самым известным методам объединения фреймов данных; ее можно рассматривать, как «стекинг». И этот самый стекинг бывает вертикальным или горизонтальным.
К примеру, у вас есть огромный набор данных в CSV
-формате, и для упрощения обработки имеет смысл разделить его на несколько файлов. Это весьма распространенная практика для огромных наборов данных и довольно часто она называется «фрагментацией» или «шардингом».
При загрузке данных в Pandas вы можете создать единый DataFrame
с помощью вертикальной пристыковки фрейма данных из каждого CSV
-файла. Предположим, у нас есть 3 фрагмента по 5 миллионов строк в каждом. Тогда после вертикального стекинга наш итоговый фрейм данных будет содержать 15 миллионов строк.
В коде ниже показана вертикальная конкатенация DataFrame
в Pandas.
# Вертикальная конкатенация
pd.concat([october_df, november_df, december_df], axis=0)
Похожий результат можно получить при разделении набора данных не по строкам, а по столбцам. В таком случае, из каждого CSV
-файла берется по нескольку столбцов со всеми строками. Это чем-то похоже на фрагментацию наборов данных по признакам. Далее выполняется горизонтальный стекинг для объединения столбцов/признаков.
# Горизонтальная конкатенация
pd.concat([features_1to5_df, features_6to10_df, features_11to15_df], axis=1)
Слияние
Слияние — это более сложный и мощный тип объединения DataFrame
в стиле SQL
. Фреймы данных объединяются по какому-то общему атрибуту.
Допустим, у вас есть два DataFrame
с описанием YouTube-канала. Один из фреймов — это список ID пользователей и общее количество времени, которое каждый пользователь провел на канале. Другой фрейм содержит тот же список с ID и количество просмотренных роликов по каждому пользователю. Благодаря слиянию мы сможем объединить два фрейма данных в один по ID пользователя, а затем добавим туда единой строкой ID, затраченное время и количество просмотренных роликов.
Слияние двух DataFrame
в Pandas делается через функцию merge
. Пример ее использования можно найти ниже. Параметры left
и right
задают два DataFrame
для объединения, а в on
отмечается столбец для сопоставления данных.
pd.merge(left=ids_and_time_df,
right=ids_and_videos_df,
on="id")
Чтобы еще качественнее имитировать SQL
-объединение, воспользуйтесь параметром how
. Он позволяет выбрать тип SQL
-объединения: внутреннее, внешнее, слева или справа. Подробнее об SQL
-объединениях читайте на W3Schools.
(3) Изменение формы DataFrame
Существует несколько способов изменения формы и структуры фреймов данных в Pandas. От простых и легких до мощных и сложных. Давайте рассмотрим три наиболее популярные разновидности. Во всех примерах мы будем работать с набором данных о супергероях.
import pandas as pd
players_data = {'Player': ['Superman', 'Batman', 'Thanos', 'Batman', 'Thanos',
'Superman', 'Batman', 'Thanos', 'Black Widow', 'Batman', 'Thanos', 'Superman'],
'Year': [2000,2000,2000,2001,2001,2002,2002,2002,2003,2004,2004,2005],
'Points':[23,43,45,65,76,34,23,78,89,76,92,87]}
df = pd.DataFrame(players_data)
print(df)
"""
Player Year Points
0 Superman 2000 23
1 Batman 2000 43
2 Thanos 2000 45
3 Batman 2001 65
4 Thanos 2001 76
5 Superman 2002 34
6 Batman 2002 23
7 Thanos 2002 78
8 Black Widow 2003 89
9 Batman 2004 76
10 Thanos 2004 92
Транспонирование
Простейший способ. Здесь мы заменяем строки DataFrame
на столбцы. Если у вас есть 5 000 строк и 10 столбцов, то после транспонирования вы получите 10 строк и 5 000 столбцов.
import pandas as pd
players_data = {'Player': ['Superman', 'Batman', 'Thanos', 'Batman', 'Thanos',
'Superman', 'Batman', 'Thanos', 'Black Widow', 'Batman', 'Thanos', 'Superman'],
'Year': [2000,2000,2000,2001,2001,2002,2002,2002,2003,2004,2004,2005],
'Points':[23,43,45,65,76,34,23,78,89,76,92,87]}
df = pd.DataFrame(players_data)
print(df)
"""
Player Year Points
0 Superman 2000 23
1 Batman 2000 43
2 Thanos 2000 45
3 Batman 2001 65
4 Thanos 2001 76
5 Superman 2002 34
6 Batman 2002 23
7 Thanos 2002 78
8 Black Widow 2003 89
9 Batman 2004 76
10 Thanos 2004 92
11 Superman 2005 87
"""
Groupby
Основное назначение Groupby
— это разделение фреймов данных на части по определенным ключам. После разделения DataFrame
вы сможете пройтись циклом по каждой из частей или выполнить дополнительные действия.
Например, в коде ниже мы создали фрейм данных по игрокам с их стажем (Years
) и количеством очков (Points
). Затем с помощью groupby
мы разделили DataFrame
на несколько частей по полю «игрок» (Player
). Таким образом, каждый игрок оказался в собственной группе, показывающей, сколько очков данный игрок набрал за каждый год активности.
groups_df = df.groupby('Player')
for player, group in groups_df:
print("----- {} -----".format(player))
print(group)
print("")
### Это выводит следующее
"""
----- Batman -----
Player Year Points
1 Batman 2000 43
3 Batman 2001 65
6 Batman 2002 23
9 Batman 2004 76
----- Black Widow -----
Player Year Points
8 Black Widow 2003 89
----- Superman -----
Player Year Points
0 Superman 2000 23
5 Superman 2002 34
11 Superman 2005 87
----- Thanos -----
Player Year Points
2 Thanos 2000 45
4 Thanos 2001 76
7 Thanos 2002 78
10 Thanos 2004 92
"""
Стекинг
Стекинг в Pandas— это способ преобразования фреймов данных, при котором в DataFrame
появляется многоуровневый индекс, т.е. каждая строка разбивается на несколько частей. Эти части создаются из столбцов DataFrame
с дальнейшим сжатием их в мультииндекс. В общем и целом, стекинг можно рассматривать как сжатие столбцов в строки с мультииндексом.
Лучше всего все это показано в коде ниже.
df = df.stack()
print(df)
"""
0 Player Superman
Year 2000
Points 23
1 Player Batman
Year 2000
Points 43
2 Player Thanos
Year 2000
Points 45
3 Player Batman
Year 2001
Points 65
4 Player Thanos
Year 2001
Points 76
5 Player Superman
Year 2002
Points 34
6 Player Batman
Year 2002
Points 23
7 Player Thanos
Year 2002
Points 78
8 Player Black Widow
Year 2003
Points 89
9 Player Batman
Year 2004
Points 76
10 Player Thanos
Year 2004
Points 92
11 Player Superman
Year 2005
Points 87
"""
(4) Работа с данными о дате и времени
Библиотека Datetime — это неотъемлемая часть Python. Всякий раз, встретив данные о дате или времени, вы обращаетесь к Datetime
. К счастью для нас, Pandas придумала собственные возможности для использования объектов Datetime
.
Давайте рассмотрим их на примере. В коде ниже мы создаем DataFrame
с 4 столбцами (день, месяц, год, нужные данные), а затем сортируем их по году и месяцу. Как видите, получается слишком громоздко: для хранения данных используется целых 3 столбца, тогда как по факту календарная дата — это всего лишь одно значение.
from itertools import product
import pandas as pd
import numpy as np
col_names = ["Day", "Month", "Year"]
df = pd.DataFrame(list(product([10, 11, 12], [8, 9], [2018, 2019])),
columns=col_names)
df['data'] = np.random.randn(len(df))
df = df.sort_values(['Year', 'Month'], ascending=[True, True])
print(df)
"""
Day Month Year data
0 10 8 2018 1.685356
4 11 8 2018 0.441383
8 12 8 2018 1.276089
2 10 9 2018 -0.260338
6 11 9 2018 0.404769
10 12 9 2018 -0.359598
1 10 8 2019 0.145498
5 11 8 2019 -0.731463
9 12 8 2019 -1.451633
3 10 9 2019 -0.988294
7 11 9 2019 -0.687049
11 12 9 2019 -0.067432
"""
Навести здесь порядок нам поможет datetime
.
Pandas придумала отличную функцию to_datetime()
, которая сжимает несколько столбцов DataFrame
и преобразует их в единый объект Datetime
. Сразу при задании нужного формата нам становятся доступны все гибкие возможности библиотеки Datetime
.
Чтобы воспользоваться функцией to_datetime()
, передадим ей все данные о дате из соответствующих столбцов (Day
, Month
, Year
). После преобразования этих данных в формат Datetime
, остальные столбцы нам больше не потребуются. Если хотите, можете от них избавиться. Вот, как все работает:
from itertools import product
import pandas as pd
import numpy as np
col_names = ["Day", "Month", "Year"]
df = pd.DataFrame(list(product([10, 11, 12], [8, 9], [2018, 2019])),
columns=col_names)
df['data'] = np.random.randn(len(df))
df = df.sort_values(['Year', 'Month'], ascending=[True, True])
df.insert(loc=0, column="date", value=pd.to_datetime(df[col_names]))
df = df.drop(col_names, axis=1).squeeze()
print(df)
"""
date data
0 2018-08-10 -0.328973
4 2018-08-11 -0.670790
8 2018-08-12 -1.360565
2 2018-09-10 -0.401973
6 2018-09-11 -1.238754
10 2018-09-12 0.957695
1 2019-08-10 0.571126
5 2019-08-11 -1.320735
9 2019-08-12 0.196036
3 2019-09-10 -1.717800
7 2019-09-11 0.074606
11 2019-09-12 -0.643198
"""
(5) Классификация элементов по группам
Классификация — это хитрый прием для организации категориальных данных. Предположим, что у нас есть огромный фрейм данных с тысячами строк, а один из столбцов содержит нужный нам элемент для классификации. Такое действие упростило бы процесс машинного обучения моделей и визуализации данных.
Взгляните на код ниже. Здесь приводится список продуктов, которые хотелось бы классифицировать:
import pandas as pd
foods = pd.Series(["Bread", "Rice", "Steak", "Ham", "Chicken",
"Apples", "Potatoes", "Mangoes", "Fish",
"Bread", "Rice", "Steak", "Ham", "Chicken",
"Apples", "Potatoes", "Mangoes", "Fish",
"Apples", "Potatoes", "Mangoes", "Fish",
"Apples", "Potatoes", "Mangoes", "Fish",
"Bread", "Rice", "Steak", "Ham", "Chicken",
"Bread", "Rice", "Steak", "Ham", "Chicken",
"Bread", "Rice", "Steak", "Ham", "Chicken",
"Apples", "Potatoes", "Mangoes", "Fish",
"Apples", "Potatoes", "Mangoes", "Fish",
"Apples", "Potatoes", "Mangoes", "Fish",
"Bread", "Rice", "Steak", "Ham", "Chicken",
"Bread", "Rice", "Steak", "Ham", "Chicken",])
groups_dict = {
"Protein": ["Steak", "Ham", "Chicken", "Fish"],
"Carbs": ["Bread", "Rice", "Apples", "Potatoes", "Mangoes"]
}
Здесь мы выносим наш список в Series
и создаем словарь с нужной нам классификацией Protein
или Carbs
. Это простейший пример. Но если бы эта Series
была достаточно крупной (например, в 1 000 000 элементов длиной), то прохождение по ней циклом оказалось бы совершенно не разумным решением.
В таком случае, вместо обычного цикла for
, лучше написать отдельную функцию, которая будет использовать встроенную Pandas-функцию map()
для оптимизации процесса. В коде ниже все подробно расписано.
def membership_map(pandas_series, groups_dict):
groups = {x: k for k, v in groups_dict.items() for x in v}
mapped_series = pandas_series.map(groups)
return mapped_series
mapped_data = membership_map(foods, groups_dict)
print(list(mapped_data))
В этой функции мы проходимся циклом по всему словарю и создаем новый словарь, в котором ключами служат все вариации элементов из Series
, а значениями являются признаки Protein
или Carbs
. Далее мы просто применяем встроенную функцию map()
и классифицируем все значения из Series
.
Вот, что у нас получается:
['Carbs', 'Carbs', 'Protein', 'Protein', 'Protein', 'Carbs', 'Carbs', 'Carbs', 'Protein', 'Carbs', 'Carbs', 'Protein', 'Protein', 'Protein', 'Carbs', 'Carbs', 'Carbs', 'Protein', 'Carbs', 'Carbs', 'Carbs', 'Protein', 'Carbs', 'Carbs', 'Carbs', 'Protein', 'Carbs', 'Carbs', 'Protein', 'Protein', 'Protein', 'Carbs', 'Carbs', 'Protein', 'Protein', 'Protein', 'Carbs', 'Carbs', 'Protein', 'Protein', 'Protein', 'Carbs', 'Carbs', 'Carbs', 'Protein', 'Carbs', 'Carbs', 'Carbs', 'Protein', 'Carbs', 'Carbs', 'Carbs', 'Protein', 'Carbs', 'Carbs', 'Protein', 'Protein', 'Protein', 'Carbs', 'Carbs', 'Protein', 'Protein', 'Protein']
Заключение
Вот и все. Теперь вы узнали о 5 продвинутых возможностях Pandas и вариантах их применения.
Читайте также:
- Строим конвейеры с Pandas, используя «pdpipe»
- Одно слово для «быстрой» Pandas
- Анализ автоаварий в Барселоне с использованием Pandas, Matplotlib и Folium
Перевод статьи George Seif: 5 Advanced Features of Pandas and How to Use Them