【问题标题】:Creating a numpy array of 3D coordinates from three 1D arrays从三个 1D 数组创建一个 3D 坐标的 numpy 数组
【发布时间】:2013-08-17 16:09:39
【问题描述】:

假设我有三个任意一维数组,例如:

x_p = np.array((1.0, 2.0, 3.0, 4.0, 5.0))
y_p = np.array((2.0, 3.0, 4.0))
z_p = np.array((8.0, 9.0))

这三个数组表示 3D 网格中的采样间隔,我想为所有交叉点构造一个 3D 向量的 1D 数组,类似于

points = np.array([[1.0, 2.0, 8.0],
                   [1.0, 2.0, 9.0],
                   [1.0, 3.0, 8.0],
                   ...
                   [5.0, 4.0, 9.0]])

顺序实际上并不重要。生成它们的明显方法:

npoints = len(x_p) * len(y_p) * len(z_p)
points = np.zeros((npoints, 3))
i = 0
for x in x_p:
    for y in y_p:
        for z in z_p:
            points[i, :] = (x, y, z)
            i += 1

所以问题是......有没有更快的方法?我已查看但未找到(可能只是未能找到正确的 Google 关键字)。

我目前正在使用这个:

npoints = len(x_p) * len(y_p) * len(z_p)
points = np.zeros((npoints, 3))
i = 0
nz = len(z_p)
for x in x_p:
    for y in y_p:
        points[i:i+nz, 0] = x
        points[i:i+nz, 1] = y
        points[i:i+nz, 2] = z_p
        i += nz

但我觉得我错过了一些巧妙的 Numpy 方式?

【问题讨论】:

  • 此问题已被标记为重复;这是一个类似的问题,但是(显然我有偏见)我认为我的问题是对更普遍问题的更简单的措辞。我也认为这个问题的答案更好;使用 meshgrid 似乎是最简单、最快的解决方案。
  • 另外,在我看来,从 2D 到 3D 的扩展并不明显。看到答案具有相似的结构意味着直接扩展是一个好的开始,但是,先验,不清楚这些是否会起作用。

标签: python arrays performance numpy


【解决方案1】:

对于那些不得不留在 numpy

g_p=np.zeros((x_p.size, y_p.size, z_p.size))
array_you_want=array(zip(*[item.flatten() for item in \
                                 [g_p+x_p[...,np.newaxis,np.newaxis],\
                                  g_p+y_p[...,np.newaxis],\
                                  g_p+z_p]]))

也很容易扩展到更高的维度。 干杯!

【讨论】:

    【解决方案2】:

    要在上面的示例中使用 numpy 网格网格,以下将起作用:

    np.vstack(np.meshgrid(x_p,y_p,z_p)).reshape(3,-1).T
    

    用于二维以上网格的 Numpy meshgrid 需要 numpy 1.7。为了规避这一点并从source code 中提取相关数据。

    def ndmesh(*xi,**kwargs):
        if len(xi) < 2:
            msg = 'meshgrid() takes 2 or more arguments (%d given)' % int(len(xi) > 0)
            raise ValueError(msg)
    
        args = np.atleast_1d(*xi)
        ndim = len(args)
        copy_ = kwargs.get('copy', True)
    
        s0 = (1,) * ndim
        output = [x.reshape(s0[:i] + (-1,) + s0[i + 1::]) for i, x in enumerate(args)]
    
        shape = [x.size for x in output]
    
        # Return the full N-D matrix (not only the 1-D vector)
        if copy_:
            mult_fact = np.ones(shape, dtype=int)
            return [x * mult_fact for x in output]
        else:
            return np.broadcast_arrays(*output)
    

    检查结果:

    print np.vstack((ndmesh(x_p,y_p,z_p))).reshape(3,-1).T
    
    [[ 1.  2.  8.]
     [ 1.  2.  9.]
     [ 1.  3.  8.]
     ....
     [ 5.  3.  9.]
     [ 5.  4.  8.]
     [ 5.  4.  9.]]
    

    对于上面的例子:

    %timeit sol2()
    10000 loops, best of 3: 56.1 us per loop
    
    %timeit np.vstack((ndmesh(x_p,y_p,z_p))).reshape(3,-1).T
    10000 loops, best of 3: 55.1 us per loop
    

    对于每个维度为 100 时:

    %timeit sol2()
    1 loops, best of 3: 655 ms per loop
    In [10]:
    
    %timeit points = np.vstack((ndmesh(x_p,y_p,z_p))).reshape(3,-1).T
    10 loops, best of 3: 21.8 ms per loop
    

    根据你想对数据做什么,你可以返回一个视图:

    %timeit np.vstack((ndmesh(x_p,y_p,z_p,copy=False))).reshape(3,-1).T
    100 loops, best of 3: 8.16 ms per loop
    

    【讨论】:

    • 幸运的是我有 Numpy 1.7。对于我的特殊情况,维度可能至少为 128,并且可能更多(大型数据集),mgrid/meshgrid 解决方案似乎效果最好,并且比我原来的双循环解决方案稍微干净。
    • 请注意,如果您不使用稀疏数组,ndmesh 等效于 np.meshgrid,因此所有时间和关键字都将相同。
    【解决方案3】:

    你可以使用itertools.product:

    def sol1():
        points = np.array( list(product(x_p, y_p, z_p)) )
    

    使用迭代器和np.take 的另一种解决方案显示,对于您的情况,速度大约快 3 倍:

    from itertools import izip, product
    
    def sol2():
        points = np.empty((len(x_p)*len(y_p)*len(z_p),3))
    
        xi,yi,zi = izip(*product( xrange(len(x_p)),
                                  xrange(len(y_p)),
                                  xrange(len(z_p))  ))
    
        points[:,0] = np.take(x_p,xi)
        points[:,1] = np.take(y_p,yi)
        points[:,2] = np.take(z_p,zi)
        return points
    

    计时结果:

    In [3]: timeit sol1()
    10000 loops, best of 3: 126 µs per loop
    
    In [4]: timeit sol2()
    10000 loops, best of 3: 42.9 µs per loop
    
    In [6]: timeit ops()
    10000 loops, best of 3: 59 µs per loop
    
    In [11]: timeit joekingtons() # with your permission Joe...
    10000 loops, best of 3: 56.2 µs per loop
    

    所以,只有我的第二个解决方案和 Joe Kington 的解决方案会给您带来一些性能提升...

    【讨论】:

    • 根据我的时间安排,您的第二个解决方案在预分配的 OP 解决方案的 66% 时间内运行,该解决方案的运行速度几乎与 mgrid 解决方案一样快。也许你应该显示时间?你也可以通过使用 np.empty 而不是 np.zeros 来节省一点时间
    • 这对于小型系统来说很好,但是它的扩展性很差。对于这样的系统,为更清晰的代码采取(14 微秒!)性能损失可能要好得多。 Nd meshgrid 在所有尺寸上都始终更快,并且可以通过复制/粘贴 numpy 源代码轻松使用。
    • @Ophion 问题是如果在x_p, y_p or z_p 中有一些不规则间隔的非整数值,例如np.array([0.1, 0.2, 0.6, 1., 2., 3.]) 等等......
    • @Saullo Castro meshgrid 考虑到了这一点。
    • 当我使用 timeit 尝试这个(虽然不是特别好,只重复几次),三个数组每个 256 时,花了一分钟多一点的时间;比简单的 for 循环方法更快(超过两分钟),但比只循环 x_p 和 y_p 的更快的 for 循环方法慢;这大约需要 20-30 秒。
    【解决方案4】:

    对于您的具体示例,mgrid 非常有用。:

    In [1]: import numpy as np
    In [2]: points = np.mgrid[1:6, 2:5, 8:10]
    In [3]: points.reshape(3, -1).T
    Out[3]:
    array([[1, 2, 8],
           [1, 2, 9],
           [1, 3, 8],
           [1, 3, 9],
           [1, 4, 8],
           [1, 4, 9],
           [2, 2, 8],
           [2, 2, 9],
           [2, 3, 8],
           [2, 3, 9],
           [2, 4, 8],
           [2, 4, 9],
           [3, 2, 8],
           [3, 2, 9],
           [3, 3, 8],
           [3, 3, 9],
           [3, 4, 8],
           [3, 4, 9],
           [4, 2, 8],
           [4, 2, 9],
           [4, 3, 8],
           [4, 3, 9],
           [4, 4, 8],
           [4, 4, 9],
           [5, 2, 8],
           [5, 2, 9],
           [5, 3, 8],
           [5, 3, 9],
           [5, 4, 8],
           [5, 4, 9]])
    

    更一般地说,如果您有特定的数组要执行此操作,您可以使用meshgrid 而不是mgrid。但是,您需要 numpy 1.7 或更高版本才能在两个以上的维度上工作。

    【讨论】:

    • 特别是在 numpy 1.7 中使用 meshgrid:np.vstack(np.meshgrid(x_p,y_p,z_p)).reshape(3,-1).T
    • 这对我很有效,即使是大网格。我本来打算写这个问题,不要假设间距是均匀的。实际上在我的情况下它们是,但我认为为了一般性,meshgrid 解决方案是这个问题的最佳答案。
    猜你喜欢
    • 2018-01-05
    • 1970-01-01
    • 2021-08-15
    • 2018-12-11
    • 1970-01-01
    • 1970-01-01
    • 2021-10-07
    • 1970-01-01
    相关资源
    最近更新 更多