我正在使用以下Python函数将四元数转换为欧拉角:
import math
def quaternion_to_euler_angle(w, x, y, z):
ysqr = y * y
t0 = +2.0 * (w * x + y * z)
t1 = +1.0 - 2.0 * (x * x + ysqr)
X = math.degrees(math.atan2(t0, t1))
t2 = +2.0 * (w * y - z * x)
t2 = +1.0 if t2 > +1.0 else t2
t2 = -1.0 if t2 < -1.0 else t2
Y = math.degrees(math.asin(t2))
t3 = +2.0 * (w * z + x * y)
t4 = +1.0 - 2.0 * (ysqr + z * z)
Z = math.degrees(math.atan2(t3, t4))
return X, Y, Z
我想将具有列“ w”,“ quat_x”,“ quat_y”和“ quat_z”的Pandas DataFrame转换为Eueler角度。当前,我正在使用for循环遍历DataFrame的每一行,并在每一行上调用quaternion_to_euler_angle()
函数。这很慢,因为我有超过40万行。
有没有更有效的方法?例如,我可以将DataFrame(或单个系列)传递给quaternion_to_euler_angle()
,但是问题是更改quaternion_to_euler_angle()
,以便它可以处理DataFrame而不是整数。
答案 0 :(得分:2)
我们可以利用向量化的NumPy ufuncs
而不是它们的math
模块对应对象,这些模块对应对象可以处理整个数组数据,并且变化很小-
import numpy as np
def quaternion_to_euler_angle_vectorized1(w, x, y, z):
ysqr = y * y
t0 = +2.0 * (w * x + y * z)
t1 = +1.0 - 2.0 * (x * x + ysqr)
X = np.degrees(np.arctan2(t0, t1))
t2 = +2.0 * (w * y - z * x)
t2 = np.where(t2>+1.0,+1.0,t2)
#t2 = +1.0 if t2 > +1.0 else t2
t2 = np.where(t2<-1.0, -1.0, t2)
#t2 = -1.0 if t2 < -1.0 else t2
Y = np.degrees(np.arcsin(t2))
t3 = +2.0 * (w * z + x * y)
t4 = +1.0 - 2.0 * (ysqr + z * z)
Z = np.degrees(np.arctan2(t3, t4))
return X, Y, Z
因此,唯一的替代品是:
math.degrees <-> np.degrees
math.atan2 <-> np.arctan2
math.asin <-> np.arcsin
和np.where
用于矢量化检查和分配。
因此,我们得到了这样的矢量化解决方案-
# For df.columns = ['w', 'quat_x', 'quat_y', 'quat_z']
X,Y,Z = quaternion_to_euler_angle_vectorized1(*df.values.T)
# If needed as a dataframe output
df_out = pd.DataFrame({'X':X,'Y':Y,'Z':Z})
400,000
行上的时间-
In [55]: np.random.seed(0)
...: a = np.random.rand(400000,4)
...: df = pd.DataFrame(a)
...: df.columns = ["w", "quat_x", "quat_y" , "quat_z"]
In [56]: %timeit quaternion_to_euler_angle_vectorized1(*df.values.T)
1 loops, best of 3: 70.6 ms per loop
优化#1
使用np.clip
替换双np.where
-
def quaternion_to_euler_angle_vectorized2(w, x, y, z):
ysqr = y * y
t0 = +2.0 * (w * x + y * z)
t1 = +1.0 - 2.0 * (x * x + ysqr)
X = np.degrees(np.arctan2(t0, t1))
t2 = +2.0 * (w * y - z * x)
t2 = np.clip(t2, a_min=-1.0, a_max=1.0)
Y = np.degrees(np.arcsin(t2))
t3 = +2.0 * (w * z + x * y)
t4 = +1.0 - 2.0 * (ysqr + z * z)
Z = np.degrees(np.arctan2(t3, t4))
return X, Y, Z
对相同数据进行计时-
In [70]: %timeit quaternion_to_euler_angle_vectorized2(*df.values.T)
10 loops, best of 3: 65.2 ms per loop
答案 1 :(得分:2)
一种更紧凑的方法是使用Rotation
中的scipy.spatial.transform
:
import pandas as pd
from scipy.spatial.transform import Rotation
rot = Rotation.from_quat(quat_df)
rot_euler = rot.as_euler('xyz', degrees=True)
euler_df = pd.DataFrame(data=rot_euler, columns=['x', 'y', 'z'])