代码之家  ›  专栏  ›  技术社区  ›  BobC

更快的numpy笛卡尔到球面坐标转换?

  •  28
  • BobC  · 技术社区  · 14 年前

    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)))
    
    4 回复  |  直到 11 年前
        1
  •  32
  •   Community Ian Goodfellow    7 年前

    这与 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,所以无法将时间与解决方案进行比较。

        2
  •  11
  •   Justin Peel    14 年前

    下面是我为这个写的一个简单的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
    

    我花了300万分从62.4秒降到1.22秒。那不算太寒酸。我相信还有其他的改进可以做。

        3
  •  8
  •   Vincent    7 年前

    TLDR编号: 我已经用VPython测试过了,用atan2表示theta(elev)是错误的,用 我推荐sympy1.0acos函数(它甚至不抱怨r=0的acos(z/r))。

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

    如果我们把它转换成物理系统(r,θ,phi)=(r,elev,方位角),我们得到:

    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]]
    

    test   = v.arrow(pos = (0,0,0), axis = vis_ori_ALA , shaftwidth=0.05, color=v.color.red)
    
        4
  •  7
  •   rth    9 年前

    为了完成前面的答案,这里有一个 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 appendSpherical_np @mtrw 对于大型阵列,

    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
    

    尽管尺寸较小, 实际上更快,

    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