Pandas: повторные таймеры с groupby
Учитывая ниже pandas DataFrame:
In [115]: times = pd.to_datetime(pd.Series(['2014-08-25 21:00:00','2014-08-25 21:04:00',
'2014-08-25 22:07:00','2014-08-25 22:09:00']))
locations = ['HK', 'LDN', 'LDN', 'LDN']
event = ['foo', 'bar', 'baz', 'qux']
df = pd.DataFrame({'Location': locations,
'Event': event}, index=times)
df
Out[115]:
Event Location
2014-08-25 21:00:00 foo HK
2014-08-25 21:04:00 bar LDN
2014-08-25 22:07:00 baz LDN
2014-08-25 22:09:00 qux LDN
Я бы хотел, чтобы данные были пересчитаны ежечасно по счету, группируя по местоположению, чтобы создать кадр данных, который выглядит следующим образом:
Out[115]:
HK LDN
2014-08-25 21:00:00 1 1
2014-08-25 22:00:00 0 2
Я пробовал различные комбинации resample() и groupby(), но не повезло. Как я могу это сделать?
Ответы
Ответ 1
В своем оригинальном сообщении я предложил использовать pd.TimeGrouper
.
В настоящее время используйте pd.Grouper
вместо pd.TimeGrouper
. Синтаксис в основном такой же, но TimeGrouper
теперь не рекомендуется в пользу pd.Grouper
.
Более того, хотя pd.TimeGrouper
может группировать только по DatetimeIndex, pd.Grouper
может группировать по столбцам datetime, которые можно указать с помощью параметра key
.
Вы можете использовать pd.Grouper
, чтобы сгруппировать фрейм данных DatetimeIndex по часам:
grouper = df.groupby([pd.Grouper(freq='1H'), 'Location'])
используйте count
для подсчета количества событий в каждой группе:
grouper['Event'].count()
# Location
# 2014-08-25 21:00:00 HK 1
# LDN 1
# 2014-08-25 22:00:00 LDN 2
# Name: Event, dtype: int64
используйте unstack
, чтобы переместить уровень индекса Location
на уровень столбца:
grouper['Event'].count().unstack()
# Out[49]:
# Location HK LDN
# 2014-08-25 21:00:00 1 1
# 2014-08-25 22:00:00 NaN 2
а затем используйте fillna
, чтобы изменить NaN на нули.
Собираем все вместе,
grouper = df.groupby([pd.Grouper(freq='1H'), 'Location'])
result = grouper['Event'].count().unstack('Location').fillna(0)
дает
Location HK LDN
2014-08-25 21:00:00 1 1
2014-08-25 22:00:00 0 2
Ответ 2
Для этого есть два варианта. Фактически они могут давать разные результаты на основе ваших данных. Первые группы опций по местоположению и внутри группы местоположения по часам. Вторая группа вариантов по местоположению и часу в то же время.
Вариант 1: используйте groupby + resample
grouped = df.groupby('Location').resample('H')['Event'].count()
Вариант 2: группируйте местоположение и DatetimeIndex вместе с groupby(pd.Grouper)
grouped = df.groupby(['Location', pd.Grouper(freq='H')])['Event'].count()
Они оба приведут к следующему:
Location
HK 2014-08-25 21:00:00 1
LDN 2014-08-25 21:00:00 1
2014-08-25 22:00:00 2
Name: Event, dtype: int64
И затем измените форму:
grouped.unstack('Location', fill_value=0)
Выведет
Location HK LDN
2014-08-25 21:00:00 1 1
2014-08-25 22:00:00 0 2
Ответ 3
Группировка по нескольким столбцам
Untubu точно отвечает своим ответом, но я хотел бы добавить, что вы могли бы сделать, если бы у вас был третий столбец, скажем, Cost
и вы хотите объединить его, как указано выше. Именно благодаря объединению ответов unutbu и этого я узнал, как это сделать, и подумал, что поделюсь этим для будущих пользователей.
Создайте столбец DataFrame with Cost
:
In[1]:
import pandas as pd
import numpy as np
times = pd.to_datetime([
"2014-08-25 21:00:00", "2014-08-25 21:04:00",
"2014-08-25 22:07:00", "2014-08-25 22:09:00"
])
df = pd.DataFrame({
"Location": ["HK", "LDN", "LDN", "LDN"],
"Event": ["foo", "bar", "baz", "qux"],
"Cost": [20, 24, 34, 52]
}, index = times)
df
Out[1]:
Location Event Cost
2014-08-25 21:00:00 HK foo 20
2014-08-25 21:04:00 LDN bar 24
2014-08-25 22:07:00 LDN baz 34
2014-08-25 22:09:00 LDN qux 52
Теперь мы группируем с помощью функции agg
чтобы указать метод агрегации каждого столбца, например, count, mean, sum и т.д.
In[2]:
grp = df.groupby([pd.Grouper(freq = "1H"), "Location"]) \
.agg({"Event": np.size, "Cost": np.mean})
grp
Out[2]:
Event Cost
Location
2014-08-25 21:00:00 HK 1 20
LDN 1 24
2014-08-25 22:00:00 LDN 2 43
Затем окончательный unstack
с заполнением NaN
нулями и отображать как int
потому что это приятно.
In[3]:
grp.unstack().fillna(0).astype(int)
Out[3]:
Event Cost
Location HK LDN HK LDN
2014-08-25 21:00:00 1 1 20 24
2014-08-25 22:00:00 0 2 0 43
Ответ 4
Это можно сделать без использования resample
или Grouper
следующим образом:
df.groupby([df.index.floor("1H"), "Location"]).count()