2017-02-06 8 views
2

(Edit: я написал решение, основываясь на ответ hpaulj, смотрите код в нижней части этого поста)Индексирования Numpy массива, используя Numpy массив ломтиков

Я написал функцию, которая подразделяет п-мерный массив в более мелкие, так что каждое из подразделений имеет всего max_chunk_size элементов.

Поскольку мне нужно разделить множество массивов одинаковых форм, а затем выполнить операции над соответствующими кусками, он фактически не работает с данными, а не создает массив «индексаторов», т.е. е. массив из (slice(x1, x2), slice(y1, y2), ...) объектов (см. код ниже). С помощью этих индексаторов я могу получить подразделения, вызвав the_array[indexer[i]] (см. Примеры ниже).

Кроме того, массив этих индексаторов имеет такое же количество измерений, что и вход и деления, выровнены вдоль соответствующих осей, i. е. блоки the_array[indexer[i,j,k]] и the_array[indexer[i+1,j,k]] являются adjusent вдоль 0 оси и т.д.

Я ожидал, что я должен быть в состоянии объединить эти блоки, вызвав the_array[indexer[i:i+2,j,k]] и the_array[indexer] вернется только the_array, однако такие вызовы приводят к ошибке:

IndexError: arrays used as indices must be of integer (or boolean) type

Есть ли простой способ обойти эту ошибку?

Вот код:

import numpy as np 
import itertools 

def subdivide(shape, max_chunk_size=500000): 
    shape = np.array(shape).astype(float) 
    total_size = shape.prod() 

    # calculate maximum slice shape: 
    slice_shape = np.floor(shape * min(max_chunk_size/total_size, 1.0)**(1./len(shape))).astype(int) 

    # create a list of slices for each dimension: 
    slices = [[slice(left, min(right, n)) \ 
     for left, right in zip(range(0, n, step_size), range(step_size, n + step_size, step_size))] \ 
     for n, step_size in zip(shape.astype(int), slice_shape)] 

    result = np.empty(reduce(lambda a,b:a*len(b), slices, 1), dtype=np.object) 
    for i, el in enumerate(itertools.product(*slices)): result[i] = el 
    result.shape = np.ceil(shape/slice_shape).astype(int) 
    return result 

Ниже приведен пример использования:

>>> ar = np.arange(90).reshape(6,15) 
>>> ar 
array([[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], 
     [15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], 
     [30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44], 
     [45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59], 
     [60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74], 
     [75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89]]) 

>>> slices = subdivide(ar.shape, 16) 
>>> slices 
array([[(slice(0, 2, None), slice(0, 6, None)), 
     (slice(0, 2, None), slice(6, 12, None)), 
     (slice(0, 2, None), slice(12, 15, None))], 
     [(slice(2, 4, None), slice(0, 6, None)), 
     (slice(2, 4, None), slice(6, 12, None)), 
     (slice(2, 4, None), slice(12, 15, None))], 
     [(slice(4, 6, None), slice(0, 6, None)), 
     (slice(4, 6, None), slice(6, 12, None)), 
     (slice(4, 6, None), slice(12, 15, None))]], dtype=object) 

>>> ar[slices[1,0]] 
array([[30, 31, 32, 33, 34, 35], 
     [45, 46, 47, 48, 49, 50]]) 
>>> ar[slices[0,2]] 
array([[12, 13, 14], 
     [27, 28, 29]]) 
>>> ar[slices[2,1]] 
array([[66, 67, 68, 69, 70, 71], 
     [81, 82, 83, 84, 85, 86]]) 

>>> ar[slices[:2,1:3]] 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
IndexError: arrays used as indices must be of integer (or boolean) type 

Вот решение, основанное на ответ hpaulj в:

import numpy as np 
import itertools 

class Subdivision(): 
    def __init__(self, shape, max_chunk_size=500000): 
     shape = np.array(shape).astype(float) 
     total_size = shape.prod() 

     # calculate maximum slice shape: 
     slice_shape = np.floor(shape * min(max_chunk_size/total_size, 1.0)**(1./len(shape))).astype(int) 

     # create a list of slices for each dimension: 
     slices = [[slice(left, min(right, n)) \ 
      for left, right in zip(range(0, n, step_size), range(step_size, n + step_size, step_size))] \ 
      for n, step_size in zip(shape.astype(int), slice_shape)] 

     self.slices = \ 
      np.array(list(itertools.product(*slices)), \ 
        dtype=np.object).reshape(tuple(np.ceil(shape/slice_shape).astype(int)) + (len(shape),)) 

    def __getitem__(self, args): 
     if type(args) != tuple: args = (args,) 

     # turn integer index into equivalent slice 
     args = tuple(slice(arg, arg + 1 if arg != -1 else None) if type(arg) == int else arg for arg in args) 

     # select the slices 
     # always select all elements from the last axis (which contains slices for each data dimension) 
     slices = self.slices[args + ((slice(None),) if Ellipsis in args else (Ellipsis, slice(None)))] 

     return np.ix_(*tuple(np.r_[tuple(slices[tuple([0] * i + [slice(None)] + \ 
                 [0] * (len(slices.shape) - 2 - i) + [i])])] \ 
           for i in range(len(slices.shape) - 1))) 

Пример использования:

>>> ar = np.arange(90).reshape(6,15) 
>>> ar 
array([[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], 
     [15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], 
     [30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44], 
     [45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59], 
     [60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74], 
     [75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89]]) 

>>> subdiv = Subdivision(ar.shape, 16) 
>>> ar[subdiv[...]] 
array([[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], 
     [15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], 
     [30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44], 
     [45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59], 
     [60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74], 
     [75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89]]) 

>>> ar[subdiv[0]] 
array([[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], 
     [15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29]]) 

>>> ar[subdiv[:2,1]] 
array([[ 6, 7, 8, 9, 10, 11], 
     [21, 22, 23, 24, 25, 26], 
     [36, 37, 38, 39, 40, 41], 
     [51, 52, 53, 54, 55, 56]]) 

>>> ar[subdiv[2,:3]] 
array([[60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74], 
     [75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89]]) 

>>> ar[subdiv[...,:2]] 
array([[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], 
     [15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26], 
     [30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41], 
     [45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56], 
     [60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71], 
     [75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86]]) 

ответ

3

Ваши фрагменты производят массивы 2x6 и 2x3.

In [36]: subslice=slices[:2,1:3] 
In [37]: subslice[0,0] 
Out[37]: array([slice(0, 2, None), slice(6, 12, None)], dtype=object) 

In [38]: ar[tuple(subslice[0,0])] 
Out[38]: 
array([[ 6, 7, 8, 9, 10, 11], 
     [21, 22, 23, 24, 25, 26]]) 

Моя NumPy версия ожидает меня, чтобы превратить subslice в кортеж. Это то же самое, что и

ar[slice(0,2), slice(6,12)] 
ar[:2, 6:12] 

Это просто основной синтаксис индексации и нарезки. ar - 2d, поэтому ar[(i,j)] требует 2 элемента кортежа - фрагментов, списков, массивов или целых чисел. Он не будет работать с массивом объектов среза.

Как можно объединить результаты в более крупный массив. Это можно сделать после индексирования или фрагменты можно преобразовать в списки индексирования.

np.bmat, например, объединяющее вместе 2d arangement массивов:

In [42]: np.bmat([[ar[tuple(subslice[0,0])], ar[tuple(subslice[0,1])]], 
        [ar[tuple(subslice[1,0])],ar[tuple(subslice[1,1])]]]) 
Out[42]: 
matrix([[ 6, 7, 8, 9, 10, 11, 12, 13, 14], 
     [21, 22, 23, 24, 25, 26, 27, 28, 29], 
     [36, 37, 38, 39, 40, 41, 42, 43, 44], 
     [51, 52, 53, 54, 55, 56, 57, 58, 59]]) 

Вы могли бы обобщать это. Он просто использует hstack и vstack во вложенных списках. Результат np.matrix, но может быть преобразован обратно в array.

Другой подход - использовать такие инструменты, как np.arange, np.r_, np.xi_ для создания массивов индексов. Для создания примера потребуется немного времени.

Чтобы объединить [0,0] и [0,1] subslices:

In [64]: j = np.r_[subslice[0,0,1],subslice[0,1,1]] 
In [65]: i = np.r_[subslice[0,0,0]] 

In [66]: i,j 
Out[66]: (array([0, 1]), array([ 6, 7, 8, 9, 10, 11, 12, 13, 14])) 
In [68]: ix = np.ix_(i,j) 
In [69]: ix 
Out[69]: 
(array([[0], 
     [1]]), array([[ 6, 7, 8, 9, 10, 11, 12, 13, 14]])) 

In [70]: ar[ix] 
Out[70]: 
array([[ 6, 7, 8, 9, 10, 11, 12, 13, 14], 
     [21, 22, 23, 24, 25, 26, 27, 28, 29]]) 

Или с i = np.r_[subslice[0,0,0], subslice[1,0,0]], ar[np.ix_(i,j)] производит массив 4x9.

+0

Спасибо за ответ! Я использовал ваше предложение с помощью 'np.r_' и' np.xi_' для создания класса и определения его метода '__getitem__' для возврата требуемого массива индексов (см. Обновленный OP). – SiLiKhon

 Смежные вопросы

  • Нет связанных вопросов^_^