【问题标题】:Pandas: resample timeseries with groupbyPandas:使用 groupby 重新采样时间序列
【发布时间】:2015-11-07 19:47:49
【问题描述】:

鉴于以下 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() 的各种组合,但没有运气。我该怎么办?

【问题讨论】:

  • 对于那些在 2017+ 年提出这个问题的人,pd.TimeGrouper 已被弃用。 See this answer 获取最新语法。

标签: python pandas group-by time-series


【解决方案1】:

在我原来的帖子中,我建议使用pd.TimeGrouper。 现在,使用pd.Grouper 而不是pd.TimeGrouper。语法大致相同,但TimeGrouper is now deprecated 支持pd.Grouper

此外,pd.TimeGrouper 只能按 DatetimeIndex 分组,pd.Grouper 可以按日期时间分组,您可以通过 key parameter 指定。


您可以使用 pd.Grouper 按小时对 DatetimeIndex 的 DataFrame 进行分组:

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

使用unstackLocation 索引级别移动到列级别:

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

【讨论】:

  • 如果您的DatetimeIndex 包含在MultiIndex 中,请使用pd.Grouper(level=datetime_level_name, freq='1H')
  • 在 0.24.1 版本中我不得不写参数 freq:grouper = df.groupby([pd.Grouper(freq='1H'), 'Location'])
【解决方案2】:

多列分组方式

untubu 对他的回答很满意,但我想补充一下,如果你有第三列,比如 Cost 并想像上面那样聚合它,你可以做什么。通过结合 unutbu 的回答和 this one,我发现了如何做到这一点,并认为我会为未来的用户分享。

使用Cost 列创建一个DataFrame:

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 函数来指定每列的聚合方法,例如计数、平均值、总和等。

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

【讨论】:

    【解决方案3】:

    熊猫 0.21 答案:TimeGrouper is getting deprecated

    执行此操作有两种选择。他们实际上可以根据您的数据给出不同的结果。第一个选项按位置分组,在位置组内按小时分组。第二个选项同时按位置和小时分组。

    选项 1:使用groupby + resample

    grouped = df.groupby('Location').resample('H')['Event'].count()
    

    选项 2:将 location 和 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
    

    【讨论】:

    • 对不起,我知道这是一个老问题。既然 TimeGrouper 已被弃用,您将如何为多列 groupby 执行此操作?另外,除了间隔之外,是否可以指定开始和结束日期?
    • @Pylander TimeGrouper 没有做任何 Grouper 做不到的事情。只需像选项 2 一样使用列表。您只能使用日期偏移别名定期指定日期。如果您有非常规日期,则必须进行一些预处理,并简单地将每个日期标记为它所属的每个组,然后将 groupby 与这些组一起使用。
    • 这是有道理的。所以只是为了澄清非常规日期。我的理想日期范围为 2004-01-01-12/31/2018,间隔 30 天。不能保证每个时间间隔的每个 groupby 键都会有值。这是一个“非常规日期”问题还是我会采用这种方法?
    • 30 天为常规日期。如果您的某些 groupby 键具有不同的日期,则此答案中的每个选项都会为您提供不同的结果。这将取决于你想要什么结果。如果您仍然感到困惑,请继续创建一个新问题。
    • 感谢您的提示。我最终提出了一个新问题:stackoverflow.com/questions/46611626/…
    【解决方案4】:

    这可以在不使用resampleGrouper 的情况下完成,如下所示:

    df.groupby([df.index.floor("1H"), "Location"]).count()

    【讨论】:

    • @exan 我不同意 - 其他解决方案使用 Grouper - 这个没有。你能指出这个重复的答案吗?
    • 我注意到并试图删除反对票,但系统不允许更改??‍♂️
    【解决方案5】:

    如果要保留所有列

    df = (df.groupby("Location")
          .resample("H", on="date")
          .last()
          .reset_index(drop=True))
    
    

    【讨论】:

      猜你喜欢
      • 2018-03-18
      • 1970-01-01
      • 2021-04-07
      • 2014-07-24
      • 1970-01-01
      • 2019-01-18
      • 1970-01-01
      • 1970-01-01
      • 2018-03-14
      相关资源
      最近更新 更多