2010-11-07 251 views
26

我有一個來自3-axiz加速度計(XYZ)的300萬個數據點的數組,我想向包含等效球面座標(r,theta,phi)的數組添加3列。以下代碼有效,但似乎太慢了。我該如何做得更好?更快的numpy笛卡兒到球面座標轉換?

import numpy as np 
import math as m 

def cart2sph(x,y,z): 
    XsqPlusYsq = x**2 + y**2 
    r = m.sqrt(XsqPlusYsq + z**2)    # r 
    elev = m.atan2(z,m.sqrt(XsqPlusYsq))  # theta 
    az = m.atan2(y,x)       # phi 
    return r, elev, az 

def cart2sphA(pts): 
    return np.array([cart2sph(x,y,z) for x,y,z in pts]) 

def appendSpherical(xyz): 
    np.hstack((xyz, cart2sphA(xyz))) 

回答

27

這類似於Justin Peel的答案,但只使用numpy,並利用其內置的矢量:

import numpy as np 

def appendSpherical_np(xyz): 
    ptsnew = np.hstack((xyz, np.zeros(xyz.shape))) 
    xy = xyz[:,0]**2 + xyz[:,1]**2 
    ptsnew[:,3] = np.sqrt(xy + xyz[:,2]**2) 
    ptsnew[:,4] = np.arctan2(np.sqrt(xy), xyz[:,2]) # for elevation angle defined from Z-axis down 
    #ptsnew[:,4] = np.arctan2(xyz[:,2], np.sqrt(xy)) # for elevation angle defined from XY-plane up 
    ptsnew[:,5] = np.arctan2(xyz[:,1], xyz[:,0]) 
    return ptsnew 

需要注意的是,如評論所說,我已經改變了定義仰角從您的原始功能。在我的機器上,使用pts = np.random.rand(3000000, 3)進行測試,時間從76秒變爲3.3秒。我沒有Cython,因此我無法比較該解決方案的時間。

+0

偉大的工作,我的Cython解決方案只有一點點快(我的機器上1.23秒比1.54秒)。出於某種原因,當我尋找直接用numpy做的時候,我沒有看到矢量化的arctan2函數。 +1 – 2010-11-07 15:41:29

+0

Anon建議'ptsnew [:,4] = np.arctan2(np.sqrt(xy),xyz [:,2])' – 2011-01-27 22:27:43

+0

參見:http://stackoverflow.com/edit-suggestions/756 – 2011-01-27 22:28:56

11

下面就讓我們用Cython代碼,我寫了這個:

cdef extern from "math.h": 
    long double sqrt(long double xx) 
    long double atan2(long double a, double b) 

import numpy as np 
cimport numpy as np 
cimport cython 

ctypedef np.float64_t DTYPE_t 

@cython.boundscheck(False) 
@cython.wraparound(False) 
def appendSpherical(np.ndarray[DTYPE_t,ndim=2] xyz): 
    cdef np.ndarray[DTYPE_t,ndim=2] pts = np.empty((xyz.shape[0],6)) 
    cdef long double XsqPlusYsq 
    for i in xrange(xyz.shape[0]): 
     pts[i,0] = xyz[i,0] 
     pts[i,1] = xyz[i,1] 
     pts[i,2] = xyz[i,2] 
     XsqPlusYsq = xyz[i,0]**2 + xyz[i,1]**2 
     pts[i,3] = sqrt(XsqPlusYsq + xyz[i,2]**2) 
     pts[i,4] = atan2(xyz[i,2],sqrt(XsqPlusYsq)) 
     pts[i,5] = atan2(xyz[i,1],xyz[i,0]) 
    return pts 

花時間下降62.4秒,採用300萬點我1.22秒。這並不太破舊。我相信還有其他一些可以改進的地方。

+0

是我的原代碼(問題)錯了嗎?或者你在談論其他答案? – BobC 2017-05-11 17:02:46

4

要完成以前的答案,這裏是一個Numexpr實現(具有可能的回退到numpy的),

import numpy as np 
from numpy import arctan2, sqrt 
import numexpr as ne 

def cart2sph(x,y,z, ceval=ne.evaluate): 
    """ x, y, z : ndarray coordinates 
     ceval: backend to use: 
       - eval : pure Numpy 
       - numexpr.evaluate: Numexpr """ 
    azimuth = ceval('arctan2(y,x)') 
    xy2 = ceval('x**2 + y**2') 
    elevation = ceval('arctan2(z, sqrt(xy2))') 
    r = eval('sqrt(xy2 + z**2)') 
    return azimuth, elevation, r 

對於大型陣列的尺寸,這允許相對於純一個numpy的實施2速度的一個因素,並且可以與C或Cython速度相媲美。本numpy的溶液(與ceval=eval參數使用時)也比在@mtrw答案爲大陣列尺寸的appendSpherical_np功能快25%,

In [1]: xyz = np.random.rand(3000000,3) 
    ...: x,y,z = xyz.T 
In [2]: %timeit -n 1 appendSpherical_np(xyz) 
1 loops, best of 3: 397 ms per loop 
In [3]: %timeit -n 1 cart2sph(x,y,z, ceval=eval) 
1 loops, best of 3: 280 ms per loop 
In [4]: %timeit -n 1 cart2sph(x,y,z, ceval=ne.evaluate) 
1 loops, best of 3: 145 ms per loop 

儘管對於更小的尺寸,appendSpherical_np實際上更快,

In [5]: xyz = np.random.rand(3000,3) 
...: x,y,z = xyz.T 
In [6]: %timeit -n 1 appendSpherical_np(xyz) 
1 loops, best of 3: 206 µs per loop 
In [7]: %timeit -n 1 cart2sph(x,y,z, ceval=eval) 
1 loops, best of 3: 261 µs per loop 
In [8]: %timeit -n 1 cart2sph(x,y,z, ceval=ne.evaluate) 
1 loops, best of 3: 271 µs per loop 
+2

I不知道數字。我的長期希望是,當numpypy可以完成我所需要的一切時,最終切換到pypy,因此「純Python」解決方案是首選。雖然這比appendSpherical_np()快了2.7倍,但appendSpherical_np()本身提供了我想要的50倍提升,而不需要其他包。但是,你仍然遇到了挑戰,所以+1給你! – BobC 2015-05-14 08:12:28

3

!在上面的所有代碼中仍然有錯誤,並且這是Google的最高結果.. TLDR: 我已經用VPython測試了這個,使用atan2 for theta(升降)是錯誤的,請使用 acos! phi(azim)是正確的。 我推薦sympy1.0 acos函數(它甚至不會抱怨r = 0的acos(z/r))。

http://mathworld.wolfram.com/SphericalCoordinates.html

如果我們將其轉換成物理系統(R,θ,Φ)=(R,高程,方位角)我們有:

r = sqrt(x*x + y*y + z*z) 
phi = atan2(y,x) 
theta = acos(z,r) 

非優化,但正確碼右手物理系統:

from sympy import * 
def asCartesian(rthetaphi): 
    #takes list rthetaphi (single coord) 
    r  = rthetaphi[0] 
    theta = rthetaphi[1]* pi/180 # to radian 
    phi  = rthetaphi[2]* pi/180 
    x = r * sin(theta) * cos(phi) 
    y = r * sin(theta) * sin(phi) 
    z = r * cos(theta) 
    return [x,y,z] 

def asSpherical(xyz): 
    #takes list xyz (single coord) 
    x  = xyz[0] 
    y  = xyz[1] 
    z  = xyz[2] 
    r  = sqrt(x*x + y*y + z*z) 
    theta = acos(z/r)*180/ pi #to degrees 
    phi  = atan2(y,x)*180/ pi 
    return [r,theta,phi] 

你可以像函數測試它自己:

test = asCartesian(asSpherical([-2.13091326,-0.0058279,0.83697319])) 

其他一些測試數據對於一些象限:

[[ 0.   0.   0.  ] 
[-2.13091326 -0.0058279 0.83697319] 
[ 1.82172775 1.15959835 1.09232283] 
[ 1.47554111 -0.14483833 -1.80804324] 
[-1.13940573 -1.45129967 -1.30132008] 
[ 0.33530045 -1.47780466 1.6384716 ] 
[-0.51094007 1.80408573 -2.12652707]] 

我用VPython還可以輕鬆地可視化向量:

test = v.arrow(pos = (0,0,0), axis = vis_ori_ALA , shaftwidth=0.05, color=v.color.red)