如何在pandas中获取相对另一行的最后N行(向量解)?

2 投票
4 回答
966 浏览
提问于 2025-04-20 10:58

我之前在一个更长的问题中提过这个问题,但我觉得我一次问了太多东西。所以,为了简单明了:

我有一个数据框,每次试验都会按下一个键。我想添加一列,显示最后 N 行的数据。所以如果我的数据看起来像这样:

trial sid  key_pressed        RT  
1     S04            x  0.502242        
2     S04            m  0.348620      
3     S04            m  0.312491       
4     S04            x  0.342541      
5     S04            n  0.419384       
6     S04            n  0.348211      
7     S04            z  0.376369   

那么每个单独的 sid 之后会变成这样:

trial sid  key_pressed        RT           last_3
1     S04            x  0.502242        NaN
2     S04            m  0.348620        NaN
3     S04            m  0.312491        [x, m, m]
4     S04            x  0.342541        [m, m, x]
5     S04            n  0.419384        [m, x, n]
6     S04            n  0.348211        [x, n, n]
7     S04            z  0.376369        [n, n, z]

有没有什么快速的方法可以做到这一点?我似乎无法弄清楚如何选择相对的行。(我刚接触 pandas,还不太习惯这样思考)

更新:根据下面贡献者的建议,我最终做了这个:

df['shifted'] = pd.concat([df.groupby('sid')['key_pressed'].shift(2) + df.groupby('sid')['key_pressed'].shift(1) + df.groupby('sid')['key_pressed'].shift(0)])

这创建了一个字符串 mxm,比如说。这样更好。

4 个回答

0

哦,也许这就是最好的解决办法。我们可以把数据“移动”一个特定的数量:

df['shifted'] = df.groupby('sid')['key_pressed'].shift(2)

然后我就可以根据这个移动后的数据来创建列表。

0

这个解决方案避免了循环,但我不太确定这是否真的算是“向量化”,因为一旦你开始使用 apply() 这个函数,我觉得你就会失去向量化带来的性能优势。

key_table = pd.concat(
    [df.key_pressed.shift(2), df.key_pressed.shift(1), df.key_pressed], 
    axis=1
)
 df['last_3'] = key_table.apply(
    lambda row: ', '.join(str(k) for k in row),
    axis=1
)

输出结果:

   trial  sid key_pressed        RT       last_3
0      1  S04           x  0.502242  nan, nan, x
1      2  S04           m  0.348620    nan, x, m
2      3  S04           m  0.312491      x, m, m
3      4  S04           x  0.342541      m, m, x
4      5  S04           n  0.419384      m, x, n
5      6  S04           n  0.348211      x, n, n
6      7  S04           z  0.376369      n, n, z
1

你想用这些列表做什么呢?把列表放在Series或DataFrame里面通常不是很方便。不过,这样做可以让你接近目标。你需要处理一下nans,然后就完成了。

In [6]: pd.concat([df.key_pressed.shift(i) for i in [0, 1, 2]], 1).apply(tuple, 1).map(list)
Out[6]: 
0    [x, nan, nan]
1      [m, x, nan]
2        [m, m, x]
3        [x, m, m]
4        [n, x, m]
5        [n, n, x]
6        [z, n, n]
dtype: object

注意,我们需要先把列表转换成元组,然后再转换成列表,这样才能避免pandas自动把我们的列表变回Series。试试这个,你就会明白为什么这样不行了:

pd.concat([df.key_pressed.shift(i) for i in [0, 1, 2]], 1).apply(list, 1)
2

一种方法是使用 shift 函数,把相关的列向下移动 n 行,然后把这些条目连接起来(因为它们是字符串,所以可以用 + 来连接):

df.last_3 = df.key_pressed.shift(1) + ', ' + df.key_pressed.shift(2) + ', ' + df.key_pressed.shift(3)

这样就会生成由前面三个条目组成的字符串,中间用逗号和空格隔开(而不是列表)。如果可以的话,我建议在数据框中尽量避免使用列表,因为那样会让事情变得有点复杂。

撰写回答