В работе с данными приходится собирать их из разных источников и объединять в единую структуру. В библиотеке Pandas реализован функционал для таких операций над табличными данными в виде методов:
concat()
join()
merge()
На данном уроке рассмотрим практические случаи, в которых они применяются.
Метод concat()
За основу возьмем данные о кликах на сайтах 4 магазинов за 28 дней.
df_clicks = pd.read_csv('./data/Cite_clicks.csv', index_col=0)
print(df_clicks.head())
# => SHOP1 SHOP2 SHOP3 SHOP4
# day
# 1 319.0 -265.0 319.0 328.0
# 2 292.0 274.0 292.0 301.0
# 3 283.0 301.0 274.0 283.0
# 4 328.0 364.0 328.0 NaN
# 5 391.0 355.0 373.0 337.0
Разобьем исходные данные на два датафрейма за первую и вторую недели месяца. Это операция делается с использованием срезов.
df_clicks_first_week = df_clicks[:7]
df_clicks_second_week = df_clicks[7:14]
print(df_clicks_first_week)
print(df_clicks_second_week)
# => SHOP1 SHOP2 SHOP3 SHOP4
# day
# 1 319.0 -265.0 319.0 328.0
# 2 292.0 274.0 292.0 301.0
# 3 283.0 301.0 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 409.0 445.0
# 7 481.0 400.0 481.0 409.0
#
# SHOP1 SHOP2 SHOP3 SHOP4
# day
# 8 NaN 267.0 333.0 344.0
# 9 300.0 278.0 300.0 311.0
# 10 289.0 311.0 -278.0 289.0
# 11 344.0 388.0 344.0 333.0
# 12 421.0 377.0 399.0 355.0
# 13 487.0 454.0 -443.0 487.0
# 14 531.0 432.0 531.0 443.0
Операция, обратная разбиению — конкатенация, применяется, чтобы собрать куски данных в единый фрагмент. Если предположить, что отчетные материалы за разные недели лежат в разных таблицах, а для аналитика необходимо поработать над всеми значениями сразу, то можно использовать метод concat()
. В качестве параметров ему передается список тех датасетов, которые необходимо объединить:
df_weeks_concat = pd.concat([
df_clicks_first_week,
df_clicks_second_week
])
print(df_weeks_concat)
# => SHOP1 SHOP2 SHOP3 SHOP4
# day
# 1 319.0 -265.0 319.0 328.0
# 2 292.0 274.0 292.0 301.0
# 3 283.0 301.0 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 409.0 445.0
# 7 481.0 400.0 481.0 409.0
# 8 NaN 267.0 333.0 344.0
# 9 300.0 278.0 300.0 311.0
# 10 289.0 311.0 -278.0 289.0
# 11 344.0 388.0 344.0 333.0
# 12 421.0 377.0 399.0 355.0
# 13 487.0 454.0 -443.0 487.0
# 14 531.0 432.0 531.0 443.0
Рассмотрим ситуацию, в которой данные разбиты по парам магазинов. Возможно, это данные из разных городов, и лежат они в разных таблицах баз данных:
df_clicks_two_first = df_clicks[['SHOP1', 'SHOP2']]
df_clicks_two_last = df_clicks[['SHOP3', 'SHOP4']]
print(df_clicks_two_first.head())
print(df_clicks_two_last.head())
# => SHOP1 SHOP2
# day
# 1 319.0 -265.0
# 2 292.0 274.0
# 3 283.0 301.0
# 4 328.0 364.0
# 5 391.0 355.0
#
# SHOP3 SHOP4
# day
# 1 319.0 328.0
# 2 292.0 301.0
# 3 274.0 283.0
# 4 328.0 NaN
# 5 373.0 337.0
Чтобы собрать их в единое целое, применяется метод concat()
, но указывается направление объединения с помощью параметра axis
:
- 0 - объединение происходит по строкам
- 1 - по столбцам
df_shop_concat = pd.concat([
df_clicks_two_first,
df_clicks_two_last
], axis=1)
print(df_shop_concat.head())
# => SHOP1 SHOP2 SHOP3 SHOP4
# day
# 1 319.0 -265.0 319.0 328.0
# 2 292.0 274.0 292.0 301.0
# 3 283.0 301.0 274.0 283.0
# 4 328.0 364.0 328.0 NaN
# 5 391.0 355.0 373.0 337.0
Стоит быть внимательными с направлением объединения. Если его не указать в данном примере, то результат будет не тот, который ожидается:
print(pd.concat([
df_clicks_two_first,
df_clicks_two_last
]))
# => SHOP1 SHOP2 SHOP3 SHOP4
# day
# 1 319.0 -265.0 NaN NaN
# 2 292.0 274.0 NaN NaN
# 3 283.0 301.0 NaN NaN
# 4 328.0 364.0 NaN NaN
# 5 391.0 355.0 NaN NaN
Фрагменты данных наподобие срезов по неделям и парам магазинов довольно типичная ситуация. Однако бывают случаи, когда срезы делаются по разным пересекающимся промежуткам времени. Рассмотрим данные кликов для пар магазинов за пять рабочих дней:
df_clicks_two_first = df_clicks[['SHOP1', 'SHOP2']][:5]
df_clicks_two_last = df_clicks[['SHOP3', 'SHOP4']][2:7]
print(df_clicks_two_first)
print(df_clicks_two_last)
# => SHOP1 SHOP2
# day
# 1 319.0 -265.0
# 2 292.0 274.0
# 3 283.0 301.0
# 4 328.0 364.0
# 5 391.0 355.0
#
# SHOP3 SHOP4
# day
# 3 274.0 283.0
# 4 328.0 NaN
# 5 373.0 337.0
# 6 409.0 445.0
# 7 481.0 409.0
Здесь дни месяца не совпадают, но пересекаются. Для объединения также используется метод concat()
, который объединит по соответствующим индексам и оставит пропуски в тех днях, для которых значений нет.
df_concat = pd.concat([
df_clicks_two_first,
df_clicks_two_last
],axis=1)
print(df_concat)
# => SHOP1 SHOP2 SHOP3 SHOP4
# day
# 1 319.0 -265.0 NaN NaN
# 2 292.0 274.0 NaN NaN
# 3 283.0 301.0 274.0 283.0
# 4 328.0 364.0 328.0 NaN
# 5 391.0 355.0 373.0 337.0
# 6 NaN NaN 409.0 445.0
# 7 NaN NaN 481.0 409.0
Метод join()
Метод concat()
позволяет производить операции конкатенации по направлениям. Однако при работе с данными требуются более сложные объединения данных. Одним из методов, который поддерживает различные сценарии объединения данных по индексам, является метод join()
:
df_join_to_first = df_clicks_two_first.join(
df_clicks_two_last
)
print(df_join_to_first)
# => SHOP1 SHOP2 SHOP3 SHOP4
# day
# 1 319.0 -265.0 NaN NaN
# 2 292.0 274.0 NaN NaN
# 3 283.0 301.0 274.0 283.0
# 4 328.0 364.0 328.0 NaN
# 5 391.0 355.0 373.0 337.0
Важно отметить, что join()
не является методом Pandas, а применяется к датафрейму. Также важно, к какому датафрейму при объединении он применяется. Если поменять местами датафреймы в примере выше, то результат будет отличаться:
df_join_to_last = df_clicks_two_last.join(
df_clicks_two_first
)
print(df_join_to_last)
# => SHOP3 SHOP4 SHOP1 SHOP2
# day
# 3 274.0 283.0 283.0 301.0
# 4 328.0 NaN 328.0 364.0
# 5 373.0 337.0 391.0 355.0
# 6 409.0 445.0 NaN NaN
# 7 481.0 409.0 NaN NaN
В примерах выше в результирующем датафрейме присутствуют только индексы датафрейма, к которому применялся данный метод. Такой способ объединения называется left join
и применяется по умолчанию. Метод join()
поддерживает различные сценарии объединения и включает такие случаи:
inner join
— объединение по пересечению индексовright join
— внешнее объединение по всем индексам объединяемых датафреймов
Для их использования в примерах ниже указываются соответствующие значения параметра how
:
print('left join:')
print(df_clicks_two_first.join(
df_clicks_two_last,
how='left'
))
print('right join:')
print(df_clicks_two_first.join(
df_clicks_two_last,
how='right'
))
print('inner join:')
print(df_clicks_two_first.join(
df_clicks_two_last,
how='inner'
))
print('outer join:')
print(df_clicks_two_first.join(
df_clicks_two_last,
how='outer'
))
# => left join:
# SHOP1 SHOP2 SHOP3 SHOP4
# day
# 1 319.0 -265.0 NaN NaN
# 2 292.0 274.0 NaN NaN
# 3 283.0 301.0 274.0 283.0
# 4 328.0 364.0 328.0 NaN
# 5 391.0 355.0 373.0 337.0
#
# right join:
# SHOP1 SHOP2 SHOP3 SHOP4
# day
# 3 283.0 301.0 274.0 283.0
# 4 328.0 364.0 328.0 NaN
# 5 391.0 355.0 373.0 337.0
# 6 NaN NaN 409.0 445.0
# 7 NaN NaN 481.0 409.0
#
# inner join:
# SHOP1 SHOP2 SHOP3 SHOP4
# day
# 3 283.0 301.0 274.0 283.0
# 4 328.0 364.0 328.0 NaN
# 5 391.0 355.0 373.0 337.0
#
# outer join:
# SHOP1 SHOP2 SHOP3 SHOP4
# day
# 1 319.0 -265.0 NaN NaN
# 2 292.0 274.0 NaN NaN
# 3 283.0 301.0 274.0 283.0
# 4 328.0 364.0 328.0 NaN
# 5 391.0 355.0 373.0 337.0
# 6 NaN NaN 409.0 445.0
# 7 NaN NaN 481.0 409.0
Метод merge()
Объединение данных можно производить не только по индексам, но и по столбцам значений двух датафреймов. Для этого не достаточно функционала метода join()
, который может производить объединения по индексам датафреймов. Для таких случаев в Pandas используется метод merge()
.
Рассмотрим датасет с кликами, в котором дни месяца указаны в столбце day
, а не в индексе строк:
df_clicks = df_clicks.reset_index()
print(df_clicks.head())
# => day SHOP1 SHOP2 SHOP3 SHOP4
# 0 1 319.0 -265.0 319.0 328.0
# 1 2 292.0 274.0 292.0 301.0
# 2 3 283.0 301.0 274.0 283.0
# 3 4 328.0 364.0 328.0 NaN
# 4 5 391.0 355.0 373.0 337.0
Будем решать задачу по объединению двух датасетов, содержащих пятидневные срезы по парам магазинов:
df_clicks_two_first = df_clicks[['day', 'SHOP1', 'SHOP2']][:5]
df_clicks_two_last = df_clicks[['day', 'SHOP3', 'SHOP4']][2:7]
print(df_clicks_two_first)
print(df_clicks_two_last)
# => day SHOP1 SHOP2
# 0 1 319.0 -265.0
# 1 2 292.0 274.0
# 2 3 283.0 301.0
# 3 4 328.0 364.0
# 4 5 391.0 355.0
#
# day SHOP3 SHOP4
# 2 3 274.0 283.0
# 3 4 328.0 NaN
# 4 5 373.0 337.0
# 5 6 409.0 445.0
# 6 7 481.0 409.0
Для их объединения необходимо указать сперва левый, а затем правый датафреймы. Также нужно определить по каким столбцам в каждом из датафреймов будет происходить объединение.
df_merged = pd.merge(
df_clicks_two_first, df_clicks_two_last,
left_on='day',
right_on='day'
)
print(df_merged)
# => day SHOP1 SHOP2 SHOP3 SHOP4
# 0 3 283.0 301.0 274.0 283.0
# 1 4 328.0 364.0 328.0 NaN
# 2 5 391.0 355.0 373.0 337.0
Также как и в методе join()
в методе merge()
поддерживаются различные сценарии объединения данных:
print('inner merge:')
print(pd.merge(
df_clicks_two_first, df_clicks_two_last,
left_on='day',
right_on='day',
how='inner'
))
print('left merge:')
print(pd.merge(
df_clicks_two_first, df_clicks_two_last,
left_on='day',
right_on='day',
how='left'
))
print('right merge:')
print(pd.merge(
df_clicks_two_first, df_clicks_two_last,
left_on='day',
right_on='day',
how='right'
))
print('outer merge:')
print(pd.merge(
df_clicks_two_first, df_clicks_two_last,
left_on='day',
right_on='day',
how='outer'
))
# => inner merge:
# day SHOP1 SHOP2 SHOP3 SHOP4
# 0 3 283.0 301.0 274.0 283.0
# 1 4 328.0 364.0 328.0 NaN
# 2 5 391.0 355.0 373.0 337.0
#
# left merge:
# day SHOP1 SHOP2 SHOP3 SHOP4
# 0 1 319.0 -265.0 NaN NaN
# 1 2 292.0 274.0 NaN NaN
# 2 3 283.0 301.0 274.0 283.0
# 3 4 328.0 364.0 328.0 NaN
# 4 5 391.0 355.0 373.0 337.0
#
# right merge:
# day SHOP1 SHOP2 SHOP3 SHOP4
# 0 3 283.0 301.0 274.0 283.0
# 1 4 328.0 364.0 328.0 NaN
# 2 5 391.0 355.0 373.0 337.0
# 3 6 NaN NaN 409.0 445.0
# 4 7 NaN NaN 481.0 409.0
#
# outer merge:
# day SHOP1 SHOP2 SHOP3 SHOP4
# 0 1 319.0 -265.0 NaN NaN
# 1 2 292.0 274.0 NaN NaN
# 2 3 283.0 301.0 274.0 283.0
# 3 4 328.0 364.0 328.0 NaN
# 4 5 391.0 355.0 373.0 337.0
# 5 6 NaN NaN 409.0 445.0
# 6 7 NaN NaN 481.0 409.0
Выводы
На данном уроке мы познакомились с различными методами Pandas для объединения табличных данных. Рассмотренные методы применяются по мере усложнения производимой операции:
concat()
- данные объединяются по строкам или по столбцам с сохранением всех значенийjoin()
- объединяюся датафреймы по индексамmerge()
- объединяются датафреймы по наборам столбцов
Как мы увидели, методы join()
и merge()
поддерживают при этом различные сценарии объединений. Используя набор данных методов, аналитик может собирать довольно сложные таблицы по набору фрагментов данных.
Остались вопросы? Задайте их в разделе «Обсуждение»
Вам ответят команда поддержки Хекслета или другие студенты
- Статья «Как учиться и справляться с негативными мыслями»
- Статья «Ловушки обучения»
- Статья «Сложные простые задачи по программированию»
- Вебинар «Как самостоятельно учиться»
Для полного доступа к курсу нужен базовый план
Базовый план откроет полный доступ ко всем курсам, упражнениям и урокам Хекслета, проектам и пожизненный доступ к теории пройденных уроков. Подписку можно отменить в любой момент.