我在熊猫中有一个小组,我正在尝试计算一个人在每个阶段花费的时间。为了更好地理解这一点,我的数据集如下:
group date stage
A 2014-01-01 one
A 2014-01-03 one
A 2014-01-04 one
A 2014-01-05 two
B 2014-01-02 four
B 2014-01-06 five
B 2014-01-10 five
C 2014-01-03 two
C 2014-01-05 two
我正在计算阶段持续时间给出:
group date stage dur
A 2014-01-01 one 0
A 2014-01-03 one 2
A 2014-01-04 one 3
A 2014-01-05 two 0
B 2014-01-02 four 0
B 2014-01-06 five 0
B 2014-01-10 five 4
C 2014-01-03 two 0
C 2014-01-05 two 2
我在下面使用的方法非常慢。有关更快方法的任何想法吗?
df['stage_duration'] = df.groupby(['group', 'stage']).date.apply(lambda y: (y - y.iloc[0])).apply(lambda y:y / np.timedelta64(1, 'D')))
答案 0 :(得分:6)
基于你的代码(你的groupby/apply
),它看起来像(尽管你的例子...但也许我误解了你想要的东西,然后安迪所做的最好的想法)你正在使用'date'列是实际数据中的datetime64
dtype而不是integer
dtype。此外,您似乎想要计算从给定group/stage
的第一次观察开始测量的天数变化。我认为这是一组更好的示例数据(如果我正确理解你的目标):
>>> df
group date stage dur
0 A 2014-01-01 one 0
1 A 2014-01-03 one 2
2 A 2014-01-04 one 3
3 A 2014-01-05 two 0
4 B 2014-01-02 four 0
5 B 2014-01-06 five 0
6 B 2014-01-10 five 4
7 C 2014-01-03 two 0
8 C 2014-01-05 two 2
考虑到你应该通过修改你的申请(正如杰夫在他的评论中所建议的那样)通过在申请后以矢量化方式除以timedelta64
来获得一些加速(或者你可以在适用):
>>> df['dur'] = df.groupby(['group','stage']).date.apply(lambda x: x - x.iloc[0])
>>> df['dur'] /= np.timedelta64(1,'D')
>>> df
group date stage dur
0 A 2014-01-01 one 0
1 A 2014-01-03 one 2
2 A 2014-01-04 one 3
3 A 2014-01-05 two 0
4 B 2014-01-02 four 0
5 B 2014-01-06 five 0
6 B 2014-01-10 five 4
7 C 2014-01-03 two 0
8 C 2014-01-05 two 2
但是,如果您的数据属于组,阶段,日期顺序,您也可以避免groupby/apply
。每个['group','stage']
分组的第一个日期都会在组更改或阶段更改时发生。所以我认为你可以做以下事情:
>>> beg = (df.group != df.group.shift(1)) | (df.stage != df.stage.shift(1))
>>> df['dur'] = (df['date'] - df['date'].where(beg).ffill())/np.timedelta64(1,'D')
>>> df
group date stage dur
0 A 2014-01-01 one 0
1 A 2014-01-03 one 2
2 A 2014-01-04 one 3
3 A 2014-01-05 two 0
4 B 2014-01-02 four 0
5 B 2014-01-06 five 0
6 B 2014-01-10 five 4
7 C 2014-01-03 two 0
8 C 2014-01-05 two 2
说明:注意df['date'].where(beg)
创建的内容:
>>> beg = (df.group != df.group.shift(1)) | (df.stage != df.stage.shift(1))
>>> df['date'].where(beg)
0 2014-01-01
1 NaT
2 NaT
3 2014-01-05
4 2014-01-02
5 2014-01-06
6 NaT
7 2014-01-03
8 NaT
然后我ffill
将值与“日期”列区分开来。
修改:Andy指出您也可以使用transform
:
>>> df['dur'] = df.date - df.groupby(['group','stage']).date.transform(lambda x: x.iloc[0])
>>> df['dur'] /= np.timedelta64(1,'D')
group date stage dur
0 A 2014-01-01 one 0
1 A 2014-01-03 one 2
2 A 2014-01-04 one 3
3 A 2014-01-05 two 0
4 B 2014-01-02 four 0
5 B 2014-01-06 five 0
6 B 2014-01-10 five 4
7 C 2014-01-03 two 0
8 C 2014-01-05 two 2
速度:我使用类似的数据帧和400,000个观察结果来计算两种方法:
申请方法:
1 loops, best of 3: 18.3 s per loop
非申请方法:
1 loops, best of 3: 1.64 s per loop
所以我认为避免申请会带来一些显着的加速
答案 1 :(得分:5)
我想我在这里使用diff
:
In [11]: df.groupby('stage')['date'].diff().fillna(0)
Out[11]:
0 0
1 2
2 0
3 0
4 0
5 4
dtype: float64
(假设这些阶段是连续的。)
如果您只是减去每组中的第一个,请使用transform:
In [21]: df['date'] - df.groupby('stage')['date'].transform(lambda x: x.iloc[0])
Out[21]:
0 0
1 2
2 0
3 0
4 0
5 4
Name: date, dtype: int64
注意:这可能要快得多......