【问题标题】:Speeding up group-wise differencing in Pandas加快 Pandas 中的分组差异
【发布时间】:2022-03-25 22:43:38
【问题描述】:

考虑以下solution 在 Pandas 中计算组内差异:

df =  df.set_index(['ticker', 'date']).sort_index()[['value']]
df['diff'] = np.nan
idx = pd.IndexSlice

for ix in df.index.levels[0]:
    df.loc[ idx[ix,:], 'diff'] = df.loc[idx[ix,:], 'value' ].diff()

为:

> df
   date ticker  value
0    63      C   1.65
1    88      C  -1.93
2    22      C  -1.29
3    76      A  -0.79
4    72      B  -1.24
5    34      A  -0.23
6    92      B   2.43
7    22      A   0.55
8    32      A  -2.50
9    59      B  -1.01

返回:

> df
             value  diff
ticker date             
A      22     0.55   NaN
       32    -2.50 -3.05
       34    -0.23  2.27
       76    -0.79 -0.56
B      59    -1.01   NaN
       72    -1.24 -0.23
       92     2.43  3.67
C      22    -1.29   NaN
       63     1.65  2.94
       88    -1.93 -3.58

该解决方案不适用于大型数据帧。形状为 (405344,2) 的数据框需要几分钟时间。大概是这种情况,因为我正在迭代主循环中第一级的每个值。

有没有办法在 Pandas 中加快速度?遍历索引值是解决这个问题的好方法吗? numba 可以用来做这个吗?

【问题讨论】:

    标签: python pandas numpy numba bodo


    【解决方案1】:

    这是另一种方式,应该会快很多。

    首先,根据代码和日期排序:

    In [11]: df = df.set_index(['ticker', 'date']).sort_index()
    
    In [12]: df
    Out[12]:
                 value
    ticker date
    A      22     0.55
           32    -2.50
           34    -0.23
           76    -0.79
    B      59    -1.01
           72    -1.24
           92     2.43
    C      22    -1.29
           63     1.65
           88    -1.93
    

    添加差异列:

    In [13]: df['diff'] = df['value'].diff()
    

    要填写NaN,我们可以找到第一行如下(可能有更好的方法):

    In [14]: s = pd.Series(df.index.labels[0])
    
    In [15]: s != s.shift()
    Out[15]:
    0     True
    1    False
    2    False
    3    False
    4     True
    5    False
    6    False
    7     True
    8    False
    9    False
    dtype: bool
    
    In [16]: df.loc[(s != s.shift()).values 'diff'] = np.nan
    
    In [17]: df
    Out[17]:
                 value  diff
    ticker date
    A      22     0.55   NaN
           32    -2.50 -3.05
           34    -0.23  2.27
           76    -0.79 -0.56
    B      59    -1.01   NaN
           72    -1.24 -0.23
           92     2.43  3.67
    C      22    -1.29   NaN
           63     1.65  2.94
           88    -1.93 -3.58
    

    【讨论】:

    • 在我对 10,000 个 DataFrame(具有与 OP 相同的特征)的计时中,这大约需要 40 毫秒。
    【解决方案2】:

    使用 groupby/apply 简单而优雅,但在 Pandas 中可能会很慢。 Bodo JIT 编译器(基于 Numba)在很多情况下都能让它变得更快:

    pip install bodo
    
    import pandas as pd
    import numpy as np
    import bodo
    
    def value_and_diff(subdf):
        subdf = subdf.set_index('date').sort_index()
        return pd.DataFrame({'value': subdf['value'],
                            'diff': subdf['value'].diff()})
    
    @bodo.jit(distributed=False)
    def f(df):
        df2 = df.groupby('ticker').apply(value_and_diff)
        return df2
    
    np.random.seed(0)
    df = pd.DataFrame({'ticker': ["A", "B", "C", "D"] * 25_000,
      'date': pd.date_range('1/1/2000', periods=100_000, freq='T'),
      'value': np.random.randn(100_000)})
    print(f(df))
    

    【讨论】:

      【解决方案3】:

      作为替代方案,您可以在每个组内进行排序和索引。虽然还没有经过时间考验:

      In [11]: def value_and_diff(subdf):
                   subdf = subdf.set_index('date').sort_index()
                   return pd.DataFrame({'value': subdf['value'],
                                        'diff': subdf['value'].diff()})
      
      In [12]: df.groupby('ticker').apply(value_and_diff)
      Out[12]:
                   diff  value
      ticker date
      A      22     NaN   0.55
             32   -3.05  -2.50
             34    2.27  -0.23
             76   -0.56  -0.79
      B      59     NaN  -1.01
             72   -0.23  -1.24
             92    3.67   2.43
      C      22     NaN  -1.29
             63    2.94   1.65
             88   -3.58  -1.93
      

      【讨论】:

      • 谢谢@Andy。有趣的是,您在 apply 中对条目进行排序(例如,相对于在运行 groupby 和 apply 之前对它们进行排序之前)。这是因为groupby 不能保证保留原始顺序吗?
      • 另外,查看 Jeff 的 this answer,我发现他应用了 transform(Series.diff) 而不是您的代码中的 diff。您知道何时使用一种与另一种来进行组内差异吗?
      • @AmelioVazquez-Reina 在这种情况下(当函数不“减少”时)然后转换和应用是相同的。回想起来,我认为全局排序可能会更快......我错误地认为这是导致最慢的问题。我想我有更好的解决方案。
      猜你喜欢
      • 2015-09-03
      • 2016-09-08
      • 2022-08-17
      • 2017-02-18
      • 2018-10-02
      • 1970-01-01
      • 2011-09-14
      • 1970-01-01
      • 2018-10-01
      相关资源
      最近更新 更多