【问题标题】:Elegant resample for groups in PandasPandas 中组的优雅重采样
【发布时间】:2018-02-24 10:38:27
【问题描述】:

对于一个名为 full_df 的给定 pandas 数据框,它看起来像

  index   id   timestamp    data  
 ------- ---- ------------ ------ 
      1    1   2017-01-01   10.0  
      2    1   2017-02-01   11.0  
      3    1   2017-04-01   13.0  
      4    2   2017-02-01    1.0  
      5    2   2017-03-01    2.0  
      6    2   2017-05-01    9.0  

开始和结束日期(以及开始和结束之间的时间差)是不同的。

但我需要一个 id 明智的重采样版本(添加了标有 * 的行)

  index   id   timestamp    data       
 ------- ---- ------------ ------ ---- 
      1    1   2017-01-01   10.0       
      2    1   2017-02-01   11.0       
      3    1   2017-03-01    NaN   *   
      4    1   2017-04-01   13.0       
      5    2   2017-02-01    1.0       
      6    2   2017-03-01    2.0       
      7    2   2017-04-01    NaN   *   
      8    2   2017-05-01    9.0  

因为数据集非常大,我想知道是否有比这样做更有效的方法

  1. full_df.groupby('id')
  2. 为每个组做df

    df.index = pd.DatetimeIndex(df['timestamp'])
    all_days = pd.date_range(df.index.min(), df.index.max(), freq='MS')
    df = df.reindex(all_days)
    
  3. 使用新索引再次组合所有组

这很耗时而且不是很优雅。有什么想法吗?

【问题讨论】:

  • 每个id的开始结束日期都不同。
  • 您在 id 2 的起始样本中有 2017-05-01
  • 对不起,复制粘贴错误。修复。谢谢指出!
  • 一个小注,all也是内置的。为避免进一步出现奇怪的麻烦,您可能需要为 DataFrame 选择一个不同的名称
  • 我将all重命名为full_df

标签: python pandas group-by


【解决方案1】:

使用resample

In [1175]: (df.set_index('timestamp').groupby('id').resample('MS').asfreq()
              .drop(['id', 'index'], 1).reset_index())
Out[1175]:
   id  timestamp  data
0   1 2017-01-01  10.0
1   1 2017-02-01  11.0
2   1 2017-03-01   NaN
3   1 2017-04-01  13.0
4   2 2017-02-01   1.0
5   2 2017-03-01   2.0
6   2 2017-04-01   NaN
7   2 2017-05-01   9.0

详情

In [1176]: df
Out[1176]:
   index  id  timestamp  data
0      1   1 2017-01-01  10.0
1      2   1 2017-02-01  11.0
2      3   1 2017-04-01  13.0
3      4   2 2017-02-01   1.0
4      5   2 2017-03-01   2.0
5      6   2 2017-05-01   9.0

In [1177]: df.dtypes
Out[1177]:
index                 int64
id                    int64
timestamp    datetime64[ns]
data                float64
dtype: object

【讨论】:

  • 请注意,具有 id 的 groupby 和基于 freq 的 Grouper 对大量组有效
  • 为了清楚起见,这里的“df”是 OP 所说的“全部”?我认为在 OP 中这是一个隐含的for df in all,对吧?
【解决方案2】:

编辑添加:这样可以计算full_df 的最小/最大日期,而不是df。如果 ID 之间的开始/结束日期有很大差异,那么不幸的是,这会使数据框膨胀,@JohnGalt 方法更好。不过,我将把它留在这里作为一种替代方法,因为在适当的情况下它应该比 groupby/resample 更快。


我认为最有效的方法可能是使用 stack/unstack 或 melt/pivot。

你可以这样做,例如:

full_df.set_index(['timestamp','id']).unstack('id').stack('id',dropna=False)

               index  data
timestamp  id             
2017-01-01 1     1.0  10.0
           2     NaN   NaN
2017-02-01 1     2.0  11.0
           2     4.0   1.0
2017-03-01 1     NaN   NaN
           2     5.0   2.0
2017-04-01 1     3.0  13.0
           2     NaN   NaN
2017-05-01 1     NaN   NaN
           2     6.0   9.0

如果您希望它显示得更像上面的样子,只需添加reset_index().set_index('id')。请特别注意将dropna=False 与保留NaN 占位符的堆栈一起使用。没有它,stack/unstack 方法只会让你回到你开始的地方。

此方法自动包含最小和最大日期,以及至少存在一个时间戳的所有日期。如果每个人都缺少内部时间戳,那么您需要像这样添加resample

full_df.set_index(['timestamp','id']).unstack('id')\
   .resample('MS').mean()\
   .stack('id',dropna=False)

【讨论】:

  • 但是就大小而言,我们得到了非常大的数据框,对吧?尤其是日期不相近时(例如 1978 年和 2017 年,各 5 个月)。
  • 哦,我想我误会了。您希望最小/最大日期为“df”,而不是“all”?抱歉,我会在我的回答中添加评论以澄清这一点。
猜你喜欢
  • 2013-03-25
  • 2013-06-09
  • 1970-01-01
  • 1970-01-01
  • 2019-04-15
  • 2018-07-14
  • 2014-07-24
  • 2016-09-06
  • 2022-06-13
相关资源
最近更新 更多