【问题标题】:Computing diffs within groups of a dataframe计算数据帧组内的差异
【发布时间】:2014-01-06 01:16:12
【问题描述】:

假设我有一个包含 3 列的数据框:Date、Ticker、Value(没有索引,至少开始时)。我有很多日期和很多代码,但每个 (ticker, date) 元组都是独一无二的。 (但很明显,相同的日期会出现在很多行中,因为它会出现在多个代码中,而同一个代码会出现在多个行中,因为它会出现很多日期。)

最初,我的行按特定顺序排列,但不按任何列排序。

我想计算每个代码(按日期排序)的第一个差异(每日变化),并将它们放在我的数据框中的一个新列中。鉴于这种情况,我不能干脆做

df['diffs'] = df['value'].diff()

因为相邻的行不是来自同一个代码。排序如下:

df = df.sort(['ticker', 'date'])
df['diffs'] = df['value'].diff()

没有解决问题,因为会有“边界”。 IE。排序后,一个代码的最后一个值将高于下一个代码的第一个值。然后计算差异将在两个股票代码之间产生差异。我不想要这个。我希望每个代码的最早日期在其差异列中以NaN 结束。

这似乎是使用groupby 的明显时机,但无论出于何种原因,我似乎都无法让它正常工作。为了清楚起见,我想执行以下过程:

  1. 根据ticker 对行进行分组
  2. 在每个组中,按其date 对行进行排序
  3. 在每个排序组内,计算value 列的差异
  4. 将这些差异放入新的 diffs 列中的原始数据框中(最好保留原始数据框的顺序。)

我不得不想象这是一个单线。但我错过了什么?


在 2013 年 12 月 17 日晚上 9:00 编辑

好的……有些进展。我可以执行以下操作来获取新的数据框:

result = df.set_index(['ticker', 'date'])\
    .groupby(level='ticker')\
    .transform(lambda x: x.sort_index().diff())\
    .reset_index()

但如果我了解 groupby 的机制,我的行现在将首先按 ticker 排序,然后按 date 排序。那是对的吗?如果是这样,我是否需要进行合并以将差异列(当前位于 result['current'] 中)附加到原始数据框 df

【问题讨论】:

    标签: python pandas


    【解决方案1】:

    按照你自己的描述去做会更容易,即

    df.sort(['ticker', 'date'], inplace=True)
    df['diffs'] = df['value'].diff()
    

    然后修正边框:

    mask = df.ticker != df.ticker.shift(1)
    df['diffs'][mask] = np.nan
    

    要保持原来的索引,可以在开头做idx = df.index,最后做df.reindex(idx),或者如果是一个巨大的dataframe,在上面进行操作

    df.filter(['ticker', 'date', 'value'])
    

    然后join最后的两个数据框。

    编辑:或者,(虽然仍然不使用groupby

    df.set_index(['ticker','date'], inplace=True)
    df.sort_index(inplace=True)
    df['diffs'] = np.nan 
    
    for idx in df.index.levels[0]:
        df.diffs[idx] = df.value[idx].diff()
    

       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
    

    这将产生:

                 value  diffs
    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
    

    【讨论】:

    • 这当然是一个简洁的解决方案。我在对原始帖子的编辑中提出了上述一种替代方案。你的干净多了。也就是说,如果保留行的原始顺序(由它们的(ticker, date) 元组定义)很重要怎么办?您是否只需在原始数据帧的副本上使用您的解决方案,然后合并(键入 tickerdate)?
    • 我正在寻找一种更通用的方法的另一个原因是,一旦我掌握了日期排序、股票同质组,我可能想做一些比第一次差异更有趣的事情。例如,我可能想在原始数据帧中添加一个名为“滚动平均值”的列,其中每一行的值是该行中指定的 ticker 的前 N ​​个样本的平均值。
    • @DJ_8one6 如果date 值(大部分)在tickers 中是通用的,您可以使用df.pivot_table( cols='ticker', rows='date', values='value' )
    • 好点。但数据漏洞在股票代码中并不是特别常见。 IE。假设仅在奇数天支持代码 1,仅在偶数天支持代码 2。如果您按照您的建议进行旋转,然后计算列下的差异,您将在任何地方看到nan。有没有办法将我的一般方法(set_indexgroupbytransformreset_index)与您提到的重新索引相结合,以便在流程结束时进行“重新调整”?我认为groupby 构造具有很多价值,可以强制计算不会“跨组”。
    • @DJ_8one6 我添加了另一种方式;虽然它没有使用groupby,但很容易使用这种方法来计算滚动平均值等
    【解决方案2】:

    您可以使用pivot 将数据框转换为日期代码表,这是一个示例:

    首先创建测试数据:

    import pandas as pd
    import numpy as np
    import random
    from itertools import product
    
    dates = pd.date_range(start="2013-12-01",  periods=10).to_native_types()
    ticks = "ABCDEF"
    pairs = list(product(dates, ticks))
    random.shuffle(pairs)
    pairs = pairs[:-5]
    values = np.random.rand(len(pairs))
    
    dates, ticks = zip(*pairs)
    df = pd.DataFrame({"date":dates, "tick":ticks, "value":values})
    

    通过pivot格式转换数据框:

    df2 = df.pivot(index="date", columns="tick", values="value")
    

    填充 NaN:

    df2 = df2.fillna(method="ffill")
    

    调用diff()方法:

    df2.diff()
    

    这是df2 的样子:

    tick               A         B         C         D         E         F
    date                                                                  
    2013-12-01  0.077260  0.084008  0.711626  0.071267  0.811979  0.429552
    2013-12-02  0.106349  0.141972  0.457850  0.338869  0.721703  0.217295
    2013-12-03  0.330300  0.893997  0.648687  0.628502  0.543710  0.217295
    2013-12-04  0.640902  0.827559  0.243816  0.819218  0.543710  0.190338
    2013-12-05  0.263300  0.604084  0.655723  0.299913  0.756980  0.135087
    2013-12-06  0.278123  0.243264  0.907513  0.723819  0.506553  0.717509
    2013-12-07  0.960452  0.243264  0.357450  0.160799  0.506553  0.194619
    2013-12-08  0.670322  0.256874  0.637153  0.582727  0.628581  0.159636
    2013-12-09  0.226519  0.284157  0.388755  0.325461  0.957234  0.810376
    2013-12-10  0.958412  0.852611  0.472012  0.832173  0.957234  0.723234
    

    【讨论】:

    • 感谢您的回答。对于我的应用程序,重要的是要“独立”考虑每个代码。 IE。不适合“填充”数据(向后或向前)以强制每个股票代码具有数据集中所有日期的值。相反,对于每个股票代码,只考虑为其提供数据的日期更为合适。我很好奇你对我在上面对 behzad.nouri 回答的评论中描述的一般set_indexgroupbytransformreset_index 过程的看法。
    【解决方案3】:

    好的。对此进行了很多思考,我认为这是我最喜欢的上述解决方案的组合以及一些玩弄。原始数据位于df

    df.sort(['ticker', 'date'], inplace=True)
    
    # for this example, with diff, I think this syntax is a bit clunky
    # but for more general examples, this should be good.  But can we do better?
    df['diffs'] = df.groupby(['ticker'])['value'].transform(lambda x: x.diff()) 
    
    df.sort_index(inplace=True)
    

    这将完成我想要的一切。我真正喜欢的是它可以推广到您想要应用比diff 更复杂的功能的情况。特别是,您可以执行lambda x: pd.rolling_mean(x, 20, 20) 之类的操作来制作滚动方式列,您无需担心每个代码的数据会被任何其他代码的数据损坏(groupby 会为您处理这些问题。 .).

    所以这是我留下的问题...为什么以下行不适用于以 df['diffs'] 开头的行:

    df['diffs'] = df.groupby[('ticker')]['value'].transform(np.diff)
    

    当我这样做时,我得到一个充满 0 的 diffs 列。对此有什么想法吗?

    【讨论】:

    • sort 已被 pandas 弃用。所以那些被'DataFrame'对象卡住的人没有属性'sort'警告你可以将上面的行更改为df.sort_values(['ticker', 'date'], inplace=True)
    • 关于你最后一个问题 - 这应该是df['diffs'] = df_temp.groupby('event_press')['event_impressions'].transform('diff')
    【解决方案4】:

    这是一个基于@behzad.nouri 编写的解决方案,但使用pd.IndexSlice

    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
    

    【讨论】:

      【解决方案5】:
      # Make sure your data is sorted properly
      df = df.sort_values(by=['group_var', 'value'])
      
      # only take diffs where next row is of the same group
      df['diffs'] = np.where(df.group_var == df.group_var.shift(1), df.value.diff(), 0)
      

      说明:

      【讨论】:

      • 请解释您的代码如何帮助回答问题。
      【解决方案6】:

      我知道这是一个老问题,所以我假设当时不存在此功能。但是对于现在有这个问题的人来说,这个解决方案效果很好:

      df.sort_values(['ticker', 'date'], inplace=True)
      df['diffs'] = df.groupby('ticker')['value'].diff()
      

      为了恢复原订单,可以使用

      df.sort_index(inplace=True)
      

      【讨论】:

        猜你喜欢
        • 2019-02-10
        • 1970-01-01
        • 2022-11-29
        • 1970-01-01
        • 2018-05-22
        • 2015-01-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多