【问题标题】:Resampling Within a Pandas MultiIndexPandas MultiIndex 中的重采样
【发布时间】:2013-03-25 19:35:34
【问题描述】:

我有一些分层数据,这些数据从底部变成时间序列数据,看起来像这样:

df = pandas.DataFrame(
    {'value_a': values_a, 'value_b': values_b},
    index=[states, cities, dates])
df.index.names = ['State', 'City', 'Date']
df

                               value_a  value_b
State   City       Date                        
Georgia Atlanta    2012-01-01        0       10
                   2012-01-02        1       11
                   2012-01-03        2       12
                   2012-01-04        3       13
        Savanna    2012-01-01        4       14
                   2012-01-02        5       15
                   2012-01-03        6       16
                   2012-01-04        7       17
Alabama Mobile     2012-01-01        8       18
                   2012-01-02        9       19
                   2012-01-03       10       20
                   2012-01-04       11       21
        Montgomery 2012-01-01       12       22
                   2012-01-02       13       23
                   2012-01-03       14       24
                   2012-01-04       15       25

我想对每个城市进行时间重采样,比如

df.resample("2D", how="sum")

会输出

                             value_a  value_b
State   City       Date                        
Georgia Atlanta    2012-01-01        1       21
                   2012-01-03        5       25
        Savanna    2012-01-01        9       29
                   2012-01-03       13       33
Alabama Mobile     2012-01-01       17       37
                   2012-01-03       21       41
        Montgomery 2012-01-01       25       45
                   2012-01-03       29       49

照原样,df.resample('2D', how='sum') 得到我

TypeError: Only valid with DatetimeIndex or PeriodIndex

很公平,但我有点希望这能奏效:

>>> df.swaplevel('Date', 'State').resample('2D', how='sum')
TypeError: Only valid with DatetimeIndex or PeriodIndex

在这一点上我真的没有想法了......有什么方法可以帮助我吗?

【问题讨论】:

    标签: python pandas time-series multi-index hierarchical-data


    【解决方案1】:

    您需要groupby() 方法,并为您希望在结果DataFrame 中维护的MultiIndex 的每个级别提供pd.Grouper。然后,您可以应用选择的操作。

    要重新采样日期或时间戳级别,您需要使用选择的频率设置 freq 参数 — 使用 pd.TimeGrouper() 的类似方法已被弃用,而使用 pd.Grouper()freq 参数集。

    这应该会为您提供所需的 DataFrame:

    df.groupby([pd.Grouper(level='State'), 
                pd.Grouper(level='City'), 
                pd.Grouper(level='Date', freq='2D')]
              ).sum()
    

    pandas 文档中的Time Series Guideresample() 描述为:

    ... 一个基于时间的 groupby,然后是对其每个组的归约方法。

    因此,从技术上讲,使用 groupby() 与在具有单个索引的 DataFrame 上使用 .resample() 的操作相同。

    同一段落指向cookbook section on resampling 以获得更高级的示例,其中“Grouping using a MultiIndex”条目与此问题高度相关。希望对您有所帮助。

    【讨论】:

    • 与这个问题的许多复杂答案不同,这是一个易于理解的解决方案,代码易于阅读。
    • 最佳答案。
    【解决方案2】:

    我自己试过这个,很短也很简单(我只会使用 2 个索引,你会得到完整的想法):

    第 1 步:重新采样日期,但这会给您提供没有其他索引的日期:

    new=df.reset_index('City').groupby('crime', group_keys=False).resample('2d').sum().pad()
    

    这会给你日期和计数

    第二步:按照日期的顺序获取分类索引:

    col=df.reset_index('City').groupby('City', group_keys=False).resample('2D').pad()[['City']]
    

    这将为您提供一个新列,其中包含城市名称并且与日期的顺序相同。

    第 3 步:将数据框合并在一起

    new_df=pd.concat([new, col], axis=1)
    

    这很简单,你可以让它更短。

    【讨论】:

      【解决方案3】:

      我还没有检查过它的效率,但我本能地对多索引执行日期时间操作是通过一种使用字典理解的手动“拆分-应用-组合”过程。

      假设您的 DataFrame 未编入索引。 (你可以先.reset_index()),它的工作原理如下:

      1. 按非日期列分组
      2. 将“日期”设置为索引并对每个块重新采样
      3. 使用pd.concat重新组装

      最终代码如下:

      pd.concat({g: x.set_index("Date").resample("2D").mean()
                         for g, x in house.groupby(["State", "City"])})
      

      【讨论】:

        【解决方案4】:

        我遇到了同样的问题,有一阵子让我头晕目眩,但后来我阅读了 0.19.2 docs.resample 函数的文档,我看到有一个新的 kwarg 称为“级别”,你可用于指定 MultiIndex 中的级别。

        编辑:"What's New" 部分中的更多详细信息。

        【讨论】:

        • 并没有真正回答关于何时需要在保留多个索引的同时重新采样的问题。在文档中,级别 kwarg 必须是类似日期时间的参数,问题是围绕非日期时间辅助分组列
        【解决方案5】:

        pd.Grouper 允许您指定“目标对象的 groupby 指令”。在 特别是,即使df.index 不是DatetimeIndex,您也可以使用它按日期分组:

        df.groupby(pd.Grouper(freq='2D', level=-1))
        

        level=-1 告诉pd.Grouper 在 MultiIndex 的最后一级查找日期。 此外,您可以将其与索引中的其他级别值结合使用:

        level_values = df.index.get_level_values
        result = (df.groupby([level_values(i) for i in [0,1]]
                              +[pd.Grouper(freq='2D', level=-1)]).sum())
        

        看起来有点尴尬,但using_Grouper 比我原来的要快得多 建议,using_reset_index:

        import numpy as np
        import pandas as pd
        import datetime as DT
        
        def using_Grouper(df):
            level_values = df.index.get_level_values
            return (df.groupby([level_values(i) for i in [0,1]]
                               +[pd.Grouper(freq='2D', level=-1)]).sum())
        
        def using_reset_index(df):
            df = df.reset_index(level=[0, 1])
            return df.groupby(['State','City']).resample('2D').sum()
        
        def using_stack(df):
            # http://stackoverflow.com/a/15813787/190597
            return (df.unstack(level=[0,1])
                      .resample('2D').sum()
                      .stack(level=[2,1])
                      .swaplevel(2,0))
        
        def make_orig():
            values_a = range(16)
            values_b = range(10, 26)
            states = ['Georgia']*8 + ['Alabama']*8
            cities = ['Atlanta']*4 + ['Savanna']*4 + ['Mobile']*4 + ['Montgomery']*4
            dates = pd.DatetimeIndex([DT.date(2012,1,1)+DT.timedelta(days = i) for i in range(4)]*4)
            df = pd.DataFrame(
                {'value_a': values_a, 'value_b': values_b},
                index = [states, cities, dates])
            df.index.names = ['State', 'City', 'Date']
            return df
        
        def make_df(N):
            dates = pd.date_range('2000-1-1', periods=N)
            states = np.arange(50)
            cities = np.arange(10)
            index = pd.MultiIndex.from_product([states, cities, dates], 
                                               names=['State', 'City', 'Date'])
            df = pd.DataFrame(np.random.randint(10, size=(len(index),2)), index=index,
                              columns=['value_a', 'value_b'])
            return df
        
        df = make_orig()
        print(using_Grouper(df))
        

        产量

                                       value_a  value_b
        State   City       Date                        
        Alabama Mobile     2012-01-01       17       37
                           2012-01-03       21       41
                Montgomery 2012-01-01       25       45
                           2012-01-03       29       49
        Georgia Atlanta    2012-01-01        1       21
                           2012-01-03        5       25
                Savanna    2012-01-01        9       29
                           2012-01-03       13       33
        

        这是在 5000 行 DataFrame 上比较 using_Grouperusing_reset_indexusing_stack 的基准:

        In [30]: df = make_df(10)
        
        In [34]: len(df)
        Out[34]: 5000
        
        In [32]: %timeit using_Grouper(df)
        100 loops, best of 3: 6.03 ms per loop
        
        In [33]: %timeit using_stack(df)
        10 loops, best of 3: 22.3 ms per loop
        
        In [31]: %timeit using_reset_index(df)
        1 loop, best of 3: 659 ms per loop
        

        【讨论】:

        • 对不起,我对 Pandas 的经验还不够多。以上更多的是一种解决方法而不是解决方案。 df.reset_index 可能是一个缓慢的操作,如果没有它可以做到这一点会更好。
        • 有趣的是,这比堆叠和取消堆叠性能更高:在 [561] 中: timeit.timeit("from main import df; df.reset_index(level=[0, 1]).groupby(['State', 'City']).resample('2D', how='sum')", number=1000) Out[561]: 7.496185064315796 In [562]: timeit.timeit( "从 main 导入 df; df.unstack(level=[0,1]).resample('2D', how='sum').stack(level=[2,1])。 swaplevel(2,0)", number=1000) Out[562]: 10.618878841400146
        • 我认为这里真正的答案是“如果你正在做这些类型的计算,你应该使用 groupby 对象,而不是分层索引”
        • @unutbu “抱歉,我对 Pandas 的经验还不够多。”读起来很有趣,稍后会有 1000 多个答案......
        • @Def_Os:很高兴,1000 多个答案之后,我可以说有一个更快的解决方案——使用pd.Grouper
        【解决方案6】:

        这行得通:

        df.groupby(level=[0,1]).apply(lambda x: x.set_index('Date').resample('2D', how='sum'))
        
                                       value_a  value_b
        State   City       Date
        Alabama Mobile     2012-01-01       17       37
                           2012-01-03       21       41
                Montgomery 2012-01-01       25       45
                           2012-01-03       29       49
        Georgia Atlanta    2012-01-01        1       21
                           2012-01-03        5       25
                Savanna    2012-01-01        9       29
                           2012-01-03       13       33
        

        如果Date列是字符串,则预先转换为datetime:

        df['Date'] = pd.to_datetime(df['Date'])
        

        【讨论】:

        • @jimbasquiat - 不,这很慢。
        【解决方案7】:

        我知道这个问题已经有几年的历史了,但我遇到了同样的问题并找到了一个需要 1 行的更简单的解决方案:

        >>> import pandas as pd
        >>> ts = pd.read_pickle('time_series.pickle')
        >>> ts
        xxxxxx1  yyyyyyyyyyyyyyyyyyyyyy1  2012-07-01     1
                                          2012-07-02    13
                                          2012-07-03     1
                                          2012-07-04     1
                                          2012-07-05    10
                                          2012-07-06     4
                                          2012-07-07    47
                                          2012-07-08     0
                                          2012-07-09     3
                                          2012-07-10    22
                                          2012-07-11     3
                                          2012-07-12     0
                                          2012-07-13    22
                                          2012-07-14     1
                                          2012-07-15     2
                                          2012-07-16     2
                                          2012-07-17     8
                                          2012-07-18     0
                                          2012-07-19     1
                                          2012-07-20    10
                                          2012-07-21     0
                                          2012-07-22     3
                                          2012-07-23     0
                                          2012-07-24    35
                                          2012-07-25     6
                                          2012-07-26     1
                                          2012-07-27     0
                                          2012-07-28     6
                                          2012-07-29    23
                                          2012-07-30     0
                                                        ..
        xxxxxxN  yyyyyyyyyyyyyyyyyyyyyyN  2014-06-02     0
                                          2014-06-03     1
                                          2014-06-04     0
                                          2014-06-05     0
                                          2014-06-06     0
                                          2014-06-07     0
                                          2014-06-08     2
                                          2014-06-09     0
                                          2014-06-10     0
                                          2014-06-11     0
                                          2014-06-12     0
                                          2014-06-13     0
                                          2014-06-14     0
                                          2014-06-15     0
                                          2014-06-16     0
                                          2014-06-17     0
                                          2014-06-18     0
                                          2014-06-19     0
                                          2014-06-20     0
                                          2014-06-21     0
                                          2014-06-22     0
                                          2014-06-23     0
                                          2014-06-24     0
                                          2014-06-25     4
                                          2014-06-26     0
                                          2014-06-27     1
                                          2014-06-28     0
                                          2014-06-29     0
                                          2014-06-30     1
                                          2014-07-01     0
        dtype: int64
        >>> ts.unstack().T.resample('W', how='sum').T.stack()
        xxxxxx1  yyyyyyyyyyyyyyyyyyyyyy1  2012-06-25/2012-07-01      1
                                          2012-07-02/2012-07-08     76
                                          2012-07-09/2012-07-15     53
                                          2012-07-16/2012-07-22     24
                                          2012-07-23/2012-07-29     71
                                          2012-07-30/2012-08-05     38
                                          2012-08-06/2012-08-12    258
                                          2012-08-13/2012-08-19    144
                                          2012-08-20/2012-08-26    184
                                          2012-08-27/2012-09-02    323
                                          2012-09-03/2012-09-09    198
                                          2012-09-10/2012-09-16    348
                                          2012-09-17/2012-09-23    404
                                          2012-09-24/2012-09-30    380
                                          2012-10-01/2012-10-07    367
                                          2012-10-08/2012-10-14    163
                                          2012-10-15/2012-10-21    338
                                          2012-10-22/2012-10-28    252
                                          2012-10-29/2012-11-04    197
                                          2012-11-05/2012-11-11    336
                                          2012-11-12/2012-11-18    234
                                          2012-11-19/2012-11-25    143
                                          2012-11-26/2012-12-02    204
                                          2012-12-03/2012-12-09    296
                                          2012-12-10/2012-12-16    146
                                          2012-12-17/2012-12-23     85
                                          2012-12-24/2012-12-30    198
                                          2012-12-31/2013-01-06    214
                                          2013-01-07/2013-01-13    229
                                          2013-01-14/2013-01-20    192
                                                                  ...
        xxxxxxN  yyyyyyyyyyyyyyyyyyyyyyN  2013-12-09/2013-12-15      3
                                          2013-12-16/2013-12-22      0
                                          2013-12-23/2013-12-29      0
                                          2013-12-30/2014-01-05      1
                                          2014-01-06/2014-01-12      3
                                          2014-01-13/2014-01-19      6
                                          2014-01-20/2014-01-26     11
                                          2014-01-27/2014-02-02      0
                                          2014-02-03/2014-02-09      1
                                          2014-02-10/2014-02-16      4
                                          2014-02-17/2014-02-23      3
                                          2014-02-24/2014-03-02      1
                                          2014-03-03/2014-03-09      4
                                          2014-03-10/2014-03-16      0
                                          2014-03-17/2014-03-23      0
                                          2014-03-24/2014-03-30      9
                                          2014-03-31/2014-04-06      1
                                          2014-04-07/2014-04-13      1
                                          2014-04-14/2014-04-20      1
                                          2014-04-21/2014-04-27      2
                                          2014-04-28/2014-05-04      8
                                          2014-05-05/2014-05-11      7
                                          2014-05-12/2014-05-18      5
                                          2014-05-19/2014-05-25      2
                                          2014-05-26/2014-06-01      8
                                          2014-06-02/2014-06-08      3
                                          2014-06-09/2014-06-15      0
                                          2014-06-16/2014-06-22      0
                                          2014-06-23/2014-06-29      5
                                          2014-06-30/2014-07-06      1
        dtype: int64
        

        ts.unstack().T.resample('W', how='sum').T.stack() 就够了!非常简单,而且看起来非常高效。我正在阅读的泡菜是 331M,所以这是一个非常强大的数据结构;在我的 MacBook Pro 上重新采样只需几秒钟。

        【讨论】:

        • 很好,但这仅适用于您只有一列的情况。
        【解决方案8】:

        使用 stack/unstack 的替代方法

        df.unstack(level=[0,1]).resample('2D', how='sum').stack(level=[2,1]).swaplevel(2,0)
        
                                       value_a  value_b
        State   City       Date
        Georgia Atlanta    2012-01-01        1       21
        Alabama Mobile     2012-01-01       17       37
                Montgomery 2012-01-01       25       45
        Georgia Savanna    2012-01-01        9       29
                Atlanta    2012-01-03        5       25
        Alabama Mobile     2012-01-03       21       41
                Montgomery 2012-01-03       29       49
        Georgia Savanna    2012-01-03       13       33
        

        注意事项:

        1. 不知道性能比较
        2. 可能的 pandas 错误 - stack(level=[2,1]) 有效,但 stack(level=[1,2]) 失败

        【讨论】:

        • 这真的很有帮助!
        • 我在计算 pct_change 之后遇到了麻烦。与here 类似的问题。我最终做了以下事情:reset_index, sort_values, groupby, pct_change(在链接中)
        猜你喜欢
        • 2017-09-19
        • 2021-08-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-06-09
        • 2018-01-20
        • 2018-02-24
        • 1970-01-01
        相关资源
        最近更新 更多