多维和线性索引之间的Numpy互换

时间:2010-07-15 16:19:29

标签: python indexing numpy

我正在寻找一种在Numpy中线性和多维索引之间进行互换的快速方法。

为了使我的用法具体化,我有大量的N个粒子,每个粒子分配5个浮点值(维度),给出一个Nx5数组。然后,我使用numpy.digitize并使用适当选择的bin边界来对每个维度进行分区,以便为每个粒子分配5维空间中的bin。

N = 10
ndims = 5
p = numpy.random.normal(size=(N,ndims))
for idim in xrange(ndims):
    bbnds[idim] = numpy.array([-float('inf')]+[-2.,-1.,0.,1.,2.]+[float('inf')])

binassign = ndims*[None]
for idim in xrange(ndims):
    binassign[idim] = numpy.digitize(p[:,idim],bbnds[idim]) - 1
然后 binassign包含与多维索引对应的行。如果我想将多维索引转换为线性索引,我想我会想做类似的事情:

linind = numpy.arange(6**5).reshape(6,6,6,6,6)

这将查找每个多维索引以将其映射到线性索引。然后你可以回去使用:

mindx = numpy.unravel_index(x,linind.shape)

我遇到困难时,弄清楚如何在每行中包含多维索引的binassign(Nx5数组),并将其转换为1d线性索引,方法是使用它来切割线性索引数组linind。

如果有人有一个(或几个)行索引技巧在多维索引和线性索引之间来回传递,其方式是对所有N粒子的操作进行矢量化,我将非常感谢您的见解。

2 个答案:

答案 0 :(得分:4)

您可以简单地计算每个bin的索引:

box_indices = numpy.dot(ndims**numpy.arange(ndims), binassign)

标量积只做1 * x0 + 5 * x1 + 5 * 5 * x2 + ...这可以通过NumPy的dot()非常有效地完成。

答案 1 :(得分:3)

虽然我非常喜欢EOL的答案,但我想对每个方向的非均匀数量的二进制数进行概括,并强调C和F排序样式之间的差异。这是一个示例解决方案:

ndims = 5
N = 10

# Define bin boundaries 
binbnds = ndims*[None]
nbins = []
for idim in xrange(ndims):
    binbnds[idim] = numpy.linspace(-10.0,10.0,numpy.random.randint(2,15))
    binbnds[idim][0] = -float('inf')
    binbnds[idim][-1] = float('inf')
    nbins.append(binbnds[idim].shape[0]-1)

nstates = numpy.cumprod(nbins)[-1]

# Define variable values for N particles in ndims dimensions
p = numpy.random.normal(size=(N,ndims))

# Assign to bins along each dimension
binassign = ndims*[None]
for idim in xrange(ndims):
    binassign[idim] = numpy.digitize(p[:,idim],binbnds[idim]) - 1

binassign = numpy.array(binassign)

# multidimensional array with elements mapping from multidim to linear index
# Two different arrays for C vs F ordering
linind_C = numpy.arange(nstates).reshape(nbins,order='C')
linind_F = numpy.arange(nstates).reshape(nbins,order='F')

现在进行转换

# Fast conversion to linear index
b_F = numpy.cumprod([1] + nbins)[:-1]
b_C = numpy.cumprod([1] + nbins[::-1])[:-1][::-1]

box_index_F = numpy.dot(b_F,binassign)
box_index_C = numpy.dot(b_C,binassign)

并检查是否正确:

# Check
print 'Checking correct mapping for each particle F order'
for k in xrange(N):
    ii = box_index_F[k]
    jj = linind_F[tuple(binassign[:,k])]
    print 'particle %d %s (%d %d)' % (k,ii == jj,ii,jj)

print 'Checking correct mapping for each particle C order'
for k in xrange(N):
    ii = box_index_C[k]
    jj = linind_C[tuple(binassign[:,k])]
    print 'particle %d %s (%d %d)' % (k,ii == jj,ii,jj)

为了完整起见,如果你想以快速,矢量化的方式从1d索引返回到多维索引:

print 'Convert C-style from linear to multi'
x = box_index_C.reshape(-1,1)
bassign_rev_C = x / b_C % nbins 

print 'Convert F-style from linear to multi'
x = box_index_F.reshape(-1,1)
bassign_rev_F = x / b_F % nbins

再次检查:

print 'Check C-order'
for k in xrange(N):
    ii = tuple(binassign[:,k])
    jj = tuple(bassign_rev_C[k,:])
    print ii==jj,ii,jj

print 'Check F-order'
for k in xrange(N):
    ii = tuple(binassign[:,k])
    jj = tuple(bassign_rev_F[k,:])
    print ii==jj,ii,jj