最低值减去组内第二低的值(熊猫,python,groupby)

lowest value subtract second lowest value within a group (panda, python,groupby)

我有一个 df,我可以得到最低单圈时间减法之间的单圈时间差
以及组内的剩余单圈时间

df['diff'] = df['lap_time'] - df.groupby('lap_ref')['lap_time '].transform('min')

原版df

╔═════════╦═════════╦══════════╦══════╗
║ lap_ref ║ swimmer ║ lap_time ║ diff ║
╠═════════╬═════════╬══════════╬══════╣
║     151 ║ steve   ║ 82.64    ║ 0    ║
║     151 ║ timoy   ║ 82.77    ║ 0.13 ║
║     151 ║ audi    ║ 82.83    ║ 0.19 ║
║     151 ║ gimmi   ║ 82.98    ║ 0.34 ║
║     151 ║ pete    ║ 83.08    ║ 0.44 ║
║     151 ║ manson  ║ 83.24    ║ 0.6  ║
║     151 ║ fuller  ║ 83.4     ║ 0.76 ║
║     151 ║ ron     ║ 83.56    ║ 0.92 ║
║     151 ║ limin   ║ 83.62    ║ 0.98 ║
║     151 ║ octank  ║ 83.92    ║ 1.28 ║
║     151 ║ frank   ║ 83.94    ║ 1.3  ║
║     151 ║ mose    ║ 84.15    ║ 1.51 ║
║     151 ║ preta   ║ 84.2     ║ 1.56 ║
║     151 ║ landra  ║ 92.2     ║ 9.56 ║
╚═════════╩═════════╩══════════╩══════╝

我需要用组内最低-第二低的值填充值 0。但是,我找不到解决方案。

尝试获得的结果

╔═════════╦═════════╦══════════╦══════╗
║ lap_ref ║ swimmer ║ lap_time ║ diff ║
╠═════════╬═════════╬══════════╬══════╣
║     151 ║ steve   ║ 82.64    ║-0.13 ║
║     151 ║ timoy   ║ 82.77    ║ 0.13 ║
║     151 ║ audi    ║ 82.83    ║ 0.19 ║
║     151 ║ gimmi   ║ 82.98    ║ 0.34 ║
║     151 ║ pete    ║ 83.08    ║ 0.44 ║
║     151 ║ manson  ║ 83.24    ║ 0.6  ║
║     151 ║ fuller  ║ 83.4     ║ 0.76 ║
║     151 ║ ron     ║ 83.56    ║ 0.92 ║
║     151 ║ limin   ║ 83.62    ║ 0.98 ║
║     151 ║ octank  ║ 83.92    ║ 1.28 ║
║     151 ║ frank   ║ 83.94    ║ 1.3  ║
║     151 ║ mose    ║ 84.15    ║ 1.51 ║
║     151 ║ preta   ║ 84.2     ║ 1.56 ║
║     151 ║ landra  ║ 92.2     ║ 9.56 ║
╚═════════╩═════════╩══════════╩══════╝
data = {'lap_ref':[151,151,151,151,151,151,151,151,151,151,151,151,151,151],
        'swimmer':['steve','timoy','audi','gimmi','pete','manson','fuller',
                    'ron','limin','octank','frank','mose','preta','landra'],
        'lap_time':[82.64,82.77,82.83,82.98,83.08,83.24,83.4,83.56,83.62,83.92,83.94,84.15,84.2,92.2]}

请指教。谢谢

我们可以用 pd.Series.nsmallest 然后将值映射回来

s = df.groupby('lap_ref ')['lap_time'].apply(pd.Series.nsmallest,n=2).groupby(level=0).agg(np.ptp).reindex(df['lap_ref ']).values
df['new'] = np.where(df['diff'].eq(0),-s,df['diff'])
df['diff'] = df['lap_time'].diff().cumsum().fillna(-df['lap_time'].diff()[1])

输出:

>>> df
0    -0.13
1     0.13
2     0.19
3     0.34
4     0.44
5     0.60
6     0.76
7     0.92
8     0.98
9     1.28
10    1.30
11    1.51
12    1.56
13    9.56
Name: lap_time, dtype: float64

您可以使用nsmallest方法。首先使用 .nsmallest 方法找到 2 个最小值,然后使用 .diff 方法找到两者之间的差异。这会产生每个 lap_ref 的第二小和最小之间的差异。由于我们想要第二小和最小之间的差异,所以我们取 max(因为另一个值是 NaN)。然后将其转换为字典并使用字典,map 将其转换为 df['lap_ref'] 并使用 np.where,将此值分配给 diff,其中 df['diff']==0:

g = df.groupby('lap_ref')['lap_time']
df['diff'] = df['lap_time'] - g.transform('min')

mapper = g.nsmallest(2).diff().droplevel(1).groupby('lap_ref').max().to_dict()
df['diff'] = np.where(df['diff']==0, -df['lap_ref'].map(mapper), df['diff'])

输出:

    lap_ref swimmer  lap_time  diff
0       151   steve     82.64 -0.13
1       151   timoy     82.77  0.13
2       151    audi     82.83  0.19
3       151   gimmi     82.98  0.34
4       151    pete     83.08  0.44
5       151  manson     83.24  0.60
6       151  fuller     83.40  0.76
7       151     ron     83.56  0.92
8       151   limin     83.62  0.98
9       151  octank     83.92  1.28
10      151   frank     83.94  1.30
11      151    mose     84.15  1.51
12      151   preta     84.20  1.56
13      151  landra     92.20  9.56

假设数据已经按降序排序(与当前在共享数据中一样):

 grp = df.groupby('lap_ref').lap_time
 difference = grp.nth(0) - grp.nth(1)
(df.assign(lap_time = df.lap_time.sub(grp.transform('min')), 
           diff = lambda df: np.where(df.lap_time.eq(0), 
                                      difference.item(), 
                                      df.lap_time)
           )
)
    lap_ref swimmer  lap_time  diff
0       151   steve      0.00 -0.13
1       151   timoy      0.13  0.13
2       151    audi      0.19  0.19
3       151   gimmi      0.34  0.34
4       151    pete      0.44  0.44
5       151  manson      0.60  0.60
6       151  fuller      0.76  0.76
7       151     ron      0.92  0.92
8       151   limin      0.98  0.98
9       151  octank      1.28  1.28
10      151   frank      1.30  1.30
11      151    mose      1.51  1.51
12      151   preta      1.56  1.56
13      151  landra      9.56  9.56