pandas 对多列多行应用函数

9 投票
1 回答
12873 浏览
提问于 2025-04-18 09:39

我有一个数据框,里面有连续像素的坐标,分别在 'xpos' 和 'ypos' 这两列中。我想计算每两个连续像素之间的角度,单位是度。目前我有下面这个解决方案,它运行得还不错,对于我文件的大小来说速度也够快,但我觉得逐行遍历所有数据行似乎不是 pandas 的最佳做法。我知道怎么对不同的列应用函数,也知道怎么对不同列的行应用函数,但就是不知道怎么把这两者结合起来。

这是我的代码:

fix_df = pd.read_csv('fixations_out.csv')

# wyliczanie kąta sakady
temp_list=[]
for count, row in df.iterrows():
    x1 = row['xpos']
    y1 = row['ypos']
    try:
        x2 = df['xpos'].ix[count-1]
        y2 = df['ypos'].ix[count-1]
        a = abs(180/math.pi * math.atan((y2-y1)/(x2-x1)))
        temp_list.append(a)
    except KeyError:
        temp_list.append(np.nan)

然后我把临时列表插入到数据框中。

编辑:在参考评论中的建议后,我得到了:

df['diff_x'] = df['xpos'].shift() - df['xpos']
df['diff_y'] = df['ypos'].shift() - df['ypos']

def calc_angle(x):
    try:
        a = abs(180/math.pi * math.atan((x.diff_y)/(x.diff_x)))
        return a
    except ZeroDivisionError:
        return 0

df['angle_degrees'] = df.apply(calc_angle, axis=1)

我比较了三种解决方案在我的数据框上的执行时间(数据框大约有6000行),发现逐行遍历的速度几乎比使用 apply 慢了9倍,而比不使用 apply 的方法慢了大约1500倍:

使用逐行遍历的解决方案的执行时间,包括将新列插回数据框:1.51秒

不使用遍历,使用 apply 的解决方案的执行时间:0.17秒

EdChum 提出的使用 diff() 的被接受答案,既不使用遍历也不使用 apply 的执行时间:0.001秒

建议:不要使用遍历或 apply,尽量使用向量化计算;这不仅更快,而且更易读。

1 个回答

13

你可以通过以下方法来实现这个功能。我把用pandas的方法和你用的方法进行了比较,发现pandas的方法快了超过1000倍,而且这还不包括把列表作为新列添加回去!这个测试是在一个有10000行的数据表上进行的。

In [108]:

%%timeit
import numpy as np
df['angle'] = np.abs(180/math.pi * np.arctan(df['xpos'].shift() - df['xpos']/df['ypos'].shift() - df['ypos']))

1000 loops, best of 3: 1.27 ms per loop

In [100]:

%%timeit
temp_list=[]
for count, row in df.iterrows():
    x1 = row['xpos']
    y1 = row['ypos']
    try:
        x2 = df['xpos'].ix[count-1]
        y2 = df['ypos'].ix[count-1]
        a = abs(180/math.pi * math.atan((y2-y1)/(x2-x1)))
        temp_list.append(a)
    except KeyError:
        temp_list.append(np.nan)
1 loops, best of 3: 1.29 s per loop

另外,如果可以的话,尽量避免使用apply,因为它是逐行操作的。如果你能找到一种可以在整个系列或数据表上工作的矢量化方法,那就优先使用这种方法。

更新

既然你只是从前一行中减去一个值,其实有一个内置的方法diff可以做到这一点,这样代码会更快:

In [117]:

%%timeit
import numpy as np
df['angle'] = np.abs(180/math.pi * np.arctan(df['xpos'].diff(1)/df['ypos'].diff(1)))

1000 loops, best of 3: 1.01 ms per loop

另一个更新

对于系列和数据表的除法操作,也有一个内置的方法,这样可以节省更多时间,我的代码执行时间现在低于1毫秒:

In [9]:

%%timeit
import numpy as np
df['angle'] = np.abs(180/math.pi * np.arctan(df['xpos'].diff(1).div(df['ypos'].diff(1))))

1000 loops, best of 3: 951 µs per loop

撰写回答