高效的四元数进行Euler变换

时间:2019-05-19 11:44:24

标签: python pandas performance

我正在使用以下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而不是整数。

2 个答案:

答案 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'])