Python 从列表构建点的三维立方体

Python 从列表构建点的三维立方体,python,numpy,Python,Numpy,我有一个列表pts包含N点(Python浮动)。我希望构造一个维度为N*N*N*3的NumPy数组,使该数组等价于: for i in xrange(0, N): for j in xrange(0, N): for k in xrange(0, N): arr[i,j,k,0] = pts[i] arr[i,j,k,1] = pts[j] arr[i,j,k,2] = pts[k] 我想知道如何利

我有一个列表
pts
包含
N
点(Python浮动)。我希望构造一个维度为
N*N*N*3
的NumPy数组,使该数组等价于:

for i in xrange(0, N):
    for j in xrange(0, N):
        for k in xrange(0, N):
            arr[i,j,k,0] = pts[i]
            arr[i,j,k,1] = pts[j]
            arr[i,j,k,2] = pts[k]

我想知道如何利用NumPy的阵列广播规则和功能(如
tile
)来简化这一过程。

我认为以下方法应该有效:

pts = np.array(pts)  #Skip if pts is a numpy array already
lp = len(pts)
arr = np.zeros((lp,lp,lp,3))
arr[:,:,:,0] = pts[:,None,None]  #None is the same as np.newaxis
arr[:,:,:,1] = pts[None,:,None]
arr[:,:,:,2] = pts[None,None,:]
快速测试:

import numpy as np
import timeit

def meth1(pts):
   pts = np.array(pts)  #Skip if pts is a numpy array already
   lp = len(pts)
   arr = np.zeros((lp,lp,lp,3))
   arr[:,:,:,0] = pts[:,None,None]  #None is the same as np.newaxis
   arr[:,:,:,1] = pts[None,:,None]
   arr[:,:,:,2] = pts[None,None,:]
   return arr

def meth2(pts):
   lp = len(pts)
   N = lp
   arr = np.zeros((lp,lp,lp,3))
   for i in xrange(0, N):
      for j in xrange(0, N):
         for k in xrange(0, N):
            arr[i,j,k,0] = pts[i]
            arr[i,j,k,1] = pts[j]
            arr[i,j,k,2] = pts[k]

   return arr

pts = range(10)
a1 = meth1(pts)
a2 = meth2(pts)

print np.all(a1 == a2)

NREPEAT = 10000
print timeit.timeit('meth1(pts)','from __main__ import meth1,pts',number=NREPEAT)
print timeit.timeit('meth2(pts)','from __main__ import meth2,pts',number=NREPEAT)
结果:

True
0.873255968094   #my way
11.4249279499    #original

因此,这种新方法也快了一个数量级。

我认为以下方法应该有效:

pts = np.array(pts)  #Skip if pts is a numpy array already
lp = len(pts)
arr = np.zeros((lp,lp,lp,3))
arr[:,:,:,0] = pts[:,None,None]  #None is the same as np.newaxis
arr[:,:,:,1] = pts[None,:,None]
arr[:,:,:,2] = pts[None,None,:]
import numpy as np
N = 10
pts = xrange(0,N)
l = [ [ [ [ pts[i],pts[j],pts[k] ]  for k in xrange(0,N) ] for j in xrange(0,N) ] for i in xrange(0,N) ]
x = np.array(l, np.int32)
print x.shape # (10,10,10,3)
快速测试:

import numpy as np
import timeit

def meth1(pts):
   pts = np.array(pts)  #Skip if pts is a numpy array already
   lp = len(pts)
   arr = np.zeros((lp,lp,lp,3))
   arr[:,:,:,0] = pts[:,None,None]  #None is the same as np.newaxis
   arr[:,:,:,1] = pts[None,:,None]
   arr[:,:,:,2] = pts[None,None,:]
   return arr

def meth2(pts):
   lp = len(pts)
   N = lp
   arr = np.zeros((lp,lp,lp,3))
   for i in xrange(0, N):
      for j in xrange(0, N):
         for k in xrange(0, N):
            arr[i,j,k,0] = pts[i]
            arr[i,j,k,1] = pts[j]
            arr[i,j,k,2] = pts[k]

   return arr

pts = range(10)
a1 = meth1(pts)
a2 = meth2(pts)

print np.all(a1 == a2)

NREPEAT = 10000
print timeit.timeit('meth1(pts)','from __main__ import meth1,pts',number=NREPEAT)
print timeit.timeit('meth2(pts)','from __main__ import meth2,pts',number=NREPEAT)
结果:

True
0.873255968094   #my way
11.4249279499    #original

因此,这种新方法的速度也快了一个数量级。

这可以通过两行完成:

import numpy as np
N = 10
pts = xrange(0,N)
l = [ [ [ [ pts[i],pts[j],pts[k] ]  for k in xrange(0,N) ] for j in xrange(0,N) ] for i in xrange(0,N) ]
x = np.array(l, np.int32)
print x.shape # (10,10,10,3)
def meth3(pts):
    arrs = np.broadcast_arrays(*np.ix_(pts, pts, pts))
    return np.concatenate([a[...,None] for a in arrs], axis=3)
然而,这种方法的速度不如的答案快,因为
concatenate
的速度非常慢。不过,他的答案的广义版本的性能也大致相同,并且可以为任何数组集生成您想要的结果(即,包含在n维网格中的n维笛卡尔积)

def meth4(arrs):     # or meth4(*arrs) for a simplified interface
    arr = np.empty([len(a) for a in arrs] + [len(arrs)])
    for i, a in enumerate(np.ix_(*arrs)):
        arr[...,i] = a
    return arr
这可以接受任何序列,只要它可以转换为numpy数组序列:

>>> meth4([[0, 1], [2, 3]])
array([[[ 0.,  2.],
        [ 0.,  3.]],

       [[ 1.,  2.],
        [ 1.,  3.]]])
这种通用性的成本并不太高——对于小型
pts
阵列来说,它的速度只有原来的两倍:

>>> (meth4([pts, pts, pts]) == meth1(pts)).all()
True
>>> %timeit meth4([pts, pts, pts])
10000 loops, best of 3: 27.4 us per loop
>>> %timeit meth1(pts)
100000 loops, best of 3: 13.1 us per loop
对于较大的,它实际上要快一点(虽然速度的提高可能是因为我使用了
而不是
):


这可以通过两行完成:

def meth3(pts):
    arrs = np.broadcast_arrays(*np.ix_(pts, pts, pts))
    return np.concatenate([a[...,None] for a in arrs], axis=3)
然而,这种方法的速度不如的答案快,因为
concatenate
的速度非常慢。不过,他的答案的广义版本的性能也大致相同,并且可以为任何数组集生成您想要的结果(即,包含在n维网格中的n维笛卡尔积)

def meth4(arrs):     # or meth4(*arrs) for a simplified interface
    arr = np.empty([len(a) for a in arrs] + [len(arrs)])
    for i, a in enumerate(np.ix_(*arrs)):
        arr[...,i] = a
    return arr
这可以接受任何序列,只要它可以转换为numpy数组序列:

>>> meth4([[0, 1], [2, 3]])
array([[[ 0.,  2.],
        [ 0.,  3.]],

       [[ 1.,  2.],
        [ 1.,  3.]]])
这种通用性的成本并不太高——对于小型
pts
阵列来说,它的速度只有原来的两倍:

>>> (meth4([pts, pts, pts]) == meth1(pts)).all()
True
>>> %timeit meth4([pts, pts, pts])
10000 loops, best of 3: 27.4 us per loop
>>> %timeit meth1(pts)
100000 loops, best of 3: 13.1 us per loop
对于较大的,它实际上要快一点(虽然速度的提高可能是因为我使用了
而不是
):


我不知道
xrange
是可索引的。有点漂亮。我不知道
xrange
是可索引的。这是一个很好的回答!请注意,
numpy.ix
会更自动地执行上面的一些操作。+1——回答得好!请注意,
numpy.ix
可以更自动地执行上面的一些操作。非常好。我总是发现
numpy.ix
对我来说有点太密集了,以至于我无法真正地去摸索(尽管我并没有试着把我的脑袋绕得太紧)。一般来说,我会发现构造长度为N的元组更容易,除了“I”(即
slice()
)之外,每个元素都填充
None
)。我会用它来索引
pts
,然后像你一样在LHS上使用省略号。e、 g.
idx=tuple(如果i==j else slice()表示xrange(N)中的j,则无);arr[…,i]=pts[idx]
。我也花了一段时间摸索
numpy.ix,直到有一天我意外地重新发明了它。但这基本上就是你在上面所做的<代码>九(pts,pts,pts)
只返回元组
(pts[:,None,None],pts[None,:,None],pts[None,None,:])
。哦——那么,我想这也让我更容易记住,因为我基本上也重新发明了它。。。现在我更了解您的解决方案(谢谢)。从API的角度来看,我可能会将
meth4
定义为
defmeth4(*arrs)
。然后将其称为
meth4(pts,pts,pts)
,而不是
meth4((pts,pts,pts))
。对我来说,这似乎有点干净。是什么阻止了arr[…,:]=np.ix_(*arrs)代替显式for循环工作?(我试过了,但在分配数组元素时出现了一个错误。)@FreddieWitherden,基本答案是
np.ix(*arrs)
返回的值不是
numpy
数组。它是数组的元组,每个数组都有不同的形状,因此
numpy
无法直接使用它。此外,给定
ix=np.ix_u(*arrs)
arr[…,i]=ix[0]
arr[…,i]=ix[1]
有很大不同,因为
ix[0]
ix[1]
具有不同的形状,因此广播规则的应用也不同。最后,
arr[…,:]
arr[…,:,:,:]
是相同的
numpy
不能很好地区分椭圆和切片,所以它无法判断要在哪个轴上运行。非常好。我总是发现
numpy.ix
对我来说有点太密集了,以至于我无法真正地去摸索(尽管我并没有试着把我的脑袋绕得太紧)。一般来说,我会发现构造长度为N的元组更容易,除了“I”(即
slice()
)之外,每个元素都填充
None
)。我会用它来索引
pts
,然后像你一样在LHS上使用省略号。e、 g.
idx=tuple(如果i==j else slice()表示xrange(N)中的j,则无);arr[…,i]=pts[idx]
。我也花了一段时间摸索
numpy.ix,直到有一天我意外地重新发明了它。但这基本上就是你在上面所做的<代码>九(pts,pts,pts)
只返回元组
(pts[:,None,None],pts[None,:,None],pts[None,None,:])
。哦——那么,我想这也让我更容易记住,因为我基本上也重新发明了它。。。现在我更了解您的解决方案(谢谢)。从API的角度来看,我可能会将
meth4
定义为
defmeth4(*arrs)
。然后将其称为
meth4(pts,pts,pts)
,而不是
meth4((pts,pts,pts))
。对我来说,这似乎有点干净。是什么阻止了arr[…,:]=np.ix_(*arrs)代替显式for循环工作?(我试过了,但在分配数组元素时出错。)@FreddieWitherden,b