我在熊猫中有一个小组,我试图计算一个人在每个阶段花费的时间.为了更好地理解这一点,我的数据集如下:
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
Run Code Online (Sandbox Code Playgroud)
我想计算阶段持续时间给出:
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
Run Code Online (Sandbox Code Playgroud)
我在下面使用的方法非常慢.关于更快方法的任何想法?
df['stage_duration'] = df.groupby(['group', 'stage']).date.apply(lambda y: (y - y.iloc[0])).apply(lambda y:y / np.timedelta64(1, 'D')))
Run Code Online (Sandbox Code Playgroud)
根据您的代码(您的groupby/apply),看起来(尽管您的示例...但是我可能误会了您想要什么,然后Andy做了什么才是最好的主意),您正在使用datetime64dtype 的'date'列而不是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
Run Code Online (Sandbox Code Playgroud)
鉴于您应该通过修改应用程序(如Jeff在他的评论中建议的)来提高速度,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
Run Code Online (Sandbox Code Playgroud)
但是您也可以避免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
Run Code Online (Sandbox Code Playgroud)
说明:请注意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
Run Code Online (Sandbox Code Playgroud)
然后,我ffill输入值并与“日期”列取不同。
编辑:正如安迪指出,您还可以使用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
Run Code Online (Sandbox Code Playgroud)
速度:我使用类似的数据帧对400,000个观察值进行计时,以计时两种方法:
申请方法:
1 loops, best of 3: 18.3 s per loop
Run Code Online (Sandbox Code Playgroud)
不适用方法:
1 loops, best of 3: 1.64 s per loop
Run Code Online (Sandbox Code Playgroud)
因此,我认为避免申请可能会大大提高速度
我想我会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
Run Code Online (Sandbox Code Playgroud)
(假设阶段是连续的。)
如果只是减去每个组中的第一个,请使用以下转换:
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
Run Code Online (Sandbox Code Playgroud)
注意:这可能快得多...