在Pandas MultiIndex中重新采样

Sna*_*Gee 42 python time-series hierarchical-data pandas

我有一些分层数据,最终到时间序列数据,看起来像这样:

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
Run Code Online (Sandbox Code Playgroud)

我想对每个城市进行时间重新采样,所以就像这样

df.resample("2D", how="sum")
Run Code Online (Sandbox Code Playgroud)

会输出

                             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
Run Code Online (Sandbox Code Playgroud)

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

TypeError: Only valid with DatetimeIndex or PeriodIndex
Run Code Online (Sandbox Code Playgroud)

很公平,但我希望这可行:

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

在这一点上,我真的没有想法......有没有什么方法堆叠和拆散可能能够帮助我?

unu*_*tbu 39

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

df.groupby(pd.Grouper(freq='2D', level=-1))
Run Code Online (Sandbox Code Playgroud)

level=-1讲述pd.Grouper寻找在多指标的最后一个级别的日期.此外,您可以将其与索引中的其他级别值结合使用:

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())
Run Code Online (Sandbox Code Playgroud)

它看起来有点尴尬,但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))
Run Code Online (Sandbox Code Playgroud)

产量

                               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
Run Code Online (Sandbox Code Playgroud)

这里是一个标杆比较using_Grouper,using_reset_index,using_stack在一个有5000行数据帧:

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
Run Code Online (Sandbox Code Playgroud)

  • 抱歉,我对 Pandas 的经验不足,无法说。以上更像是一种解决方法而不是解决方案。`df.reset_index` 可能是一个缓慢的操作,如果没有它就可以完成它会更好。 (2认同)
  • 我认为这里真正的答案是"如果你正在进行这些类型的计算,你应该使用groupby对象,而不是分层索引" (2认同)
  • @unutbu"对不起,我对Pandas说的经验不足." 有趣的是,以后有1000多个答案...... (2认同)
  • @Def_Os:令人高兴的是,1000 多个答案之后,我可以说有一个更快的解决方案——使用 `pd.Grouper`。 (2认同)

use*_*356 14

使用堆栈/取消堆栈的替代方案

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
Run Code Online (Sandbox Code Playgroud)

笔记:

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


ksi*_*ndi 10

这有效:

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
Run Code Online (Sandbox Code Playgroud)

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

df['Date'] = pd.to_datetime(df['Date'])
Run Code Online (Sandbox Code Playgroud)


fpe*_*syn 6

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

重新采样日期或时间戳的水平,你需要设置freq与选择的频率参数-使用类似的方法pd.TimeGrouper()赞成不赞成pd.Grouper()freq参数设置。

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

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

pandas文档中的“ 时间序列指南”描述resample()为:“基于时间的分组依据,然后对每个分组采用归约方法”。因此,在groupby()技术上,使用应.resample()与在具有单个索引的DataFrame上使用相同的操作。

该段落指向有关重新采样食谱部分,以获取更高级的示例,其中“ 使用MultiIndex分组 ”条目与此问题高度相关。希望能有所帮助。

  • 最好的答案。 (4认同)
  • 与许多复杂的解决方案不同,这是一个易于理解的解决方案,可以使代码易于阅读。 (2认同)

Jos*_*h D 5

我遇到了同样的问题,让我头疼了一段时间,但后来我阅读了0.19.2 文档.resample中该函数的文档,我看到有一个名为“级别”的新功能,您可以使用它来指定多重索引。kwarg

编辑:“新增内容”部分中有更多详细信息。

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