pandas 对多列多行应用函数
我有一个数据框,里面有连续像素的坐标,分别在 '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 个回答
你可以通过以下方法来实现这个功能。我把用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