计算数据帧组内的差异

时间:2013-12-18 01:57:15

标签: python pandas

假设我有一个包含3列的数据框:日期,代码,价值(没有索引,至少要开始)。我有很多日期和许多代码,但每个(ticker, date)元组都是唯一的。 (但显然相同的日期会出现在很多行中,因为它会出现在多个行中,并且同一个行列表会显示在多行中,因为它会存在很多日期。)

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

我想计算每个股票代码的第一个差异(每日更改)(按日期排序),并将它们放在我的数据框中的新列中。鉴于此背景,我不能简单地执行

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

因为相邻的行不是来自同一个自动收报机。排序如下:

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

解决问题,因为会有“边框”。即在那之后,一个股票代码的最后一个值将高于下一个股票代码的第一个值。然后计算差异会使两个代码之间产生差异。我不想要这个。我希望每个自动收报机的最早日期在差异列中以NaN结束。

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

  1. 根据ticker
  2. 对行进行分组
  3. 在每个组中,按date
  4. 对行进行排序
  5. 在每个已排序的组中,计算value
  6. 的差异
  7. 将这些差异放入新diffs列中的原始数据框中(理想情况下,保留原始数据框顺序。)
  8. 我不得不想象这是一个单行。但是我错过了什么?


    2013-12-17晚上9点编辑

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

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

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

6 个答案:

答案 0 :(得分:25)

不会更容易做自己描述的事情,即

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),或者如果它是一个巨大的数据帧,请执行

上的操作
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

答案 1 :(得分:13)

确定。很多人都在考虑这个问题,我认为这是我最喜欢的上述解决方案和一些游戏的组合。原始数据位于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列。有什么想法?

答案 2 :(得分:3)

# 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)

说明: enter image description here

答案 3 :(得分:2)

这是一个基于@ 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

答案 4 :(得分:1)

您可以使用pivot将数据框转换为date-ticker表,这是一个示例:

首先创建测试数据:

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

答案 5 :(得分:0)

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

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

为了返回到原始顺序,您可以使用

df.sort_index(inplace=True)