Я ищу быстрый способ межпреобразовать между линейной и многомерной индексацией в Numpy.
Для создания моего бетона использования у меня есть большое количество частиц N, каждые присвоенные 5 плаваний значения (размеры), дающие массив Nx5. Я затем мусорное ведро каждый размер с помощью numpy.digitize с соответствующим выбором границ мусорного ведра присвоить каждой частице мусорное ведро в 5 размерных пространствах.
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) содержащий многомерный индекс в каждой строке и coverting это к 1d линейный индекс, при помощи его для разрезания линейного массива индексации linind.
Если у кого-либо есть один (или несколько) прием индексации строки для движения назад и вперед между многомерным индексом и линейным индексом способом, который векторизует операцию для всех частиц N, я ценил бы понимание.
Хотя мне очень нравится ответ 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
Вы можете просто вычислить индекс каждого бина:
box_indices = numpy.dot(ndims**numpy.arange(ndims), binassign)
Скалярное произведение просто равно 1 * x0 + 5 * x1 + 5 * 5 * x2 +… Это очень эффективно выполняется с помощью NumPy's точка ()
.