2016-08-27 2 views
12

Используя NumPy, я хотел бы создать список всех линий и диагонали n-мерного массива с длиной k.Найти все n-мерные линии и диагонали с NumPy


Возьмите случай следующего трехмерного массива длиной 3.

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]]]) 

Для этого случая я хотел бы получить все следующие типы последовательностей. Для любого данного случая я хотел бы получить все возможные последовательности каждого типа. Примеры требуемых последовательностей приведены в скобках ниже, для каждого случая.

  • 1D линии
    • оси х (0, 1, 2)
    • оси Y (0, 3, 6)
    • ось г (0, 9, 18)
  • 2D диагоналей
    • х/у оси (), 2, 4, 6)
    • X/Z оси (0, 10, 20, 2, 10, 18) оси
    • Y/Z (0, 12, 24, 6, 12, 18)
  • 3D диагоналей
    • х/Y/Z оси (0, 13, 26, 2, 13, 24)

Решение должно быть обобщено, так что оно будет генерировать все линии и диагонали для массива, независимо от количества или длины массива (которое является постоянным во всех измерениях).

+0

Каково значение 2 выделенных блоков для двумерных диагоналей? Центральная диагональ будет иметь 3 элемента; вы включаете в себя антидиагональ в одном кортеже, но как-то разделены. Вы также заинтересованы в смежных диагоналях? – hpaulj

+0

Вы хотите сгенерировать '(0, 1, 2)' и '(2, 1, 0)'? – Eric

+0

Вы пытаетесь реализовать [это] (http://ericwieser.me/games/4d/)? – Eric

ответ

5

Этот раствор, обобщенный на n

Позволяет перефразировать этот про blem как «найти список индексов».

Мы ищем все 2d индексных массивов вида

array[i[0], i[1], i[2], ..., i[n-1]] 

Пусть n = arr.ndim

Где i массив формы (n, k)

Каждый из i[j] может быть один из :

  • тот же индекс повторяется п раз, ri[j] = [j, ..., j]
  • Передняя последовательность, fi = [0, 1, ..., k-1]
  • Обратный последовательность, bi = [k-1, ..., 1, 0]

С требованиями, что каждая последовательность имеет вид ^(ri)*(fi)(fi|bi|ri)*$ (с использованием регулярных выражений, чтобы суммировать его).Это происходит потому, что:

  • должно быть по крайней мере один fi так что «линия» не является точкой выбран повторно
  • не bi не ы прийти до fi с, чтобы избежать попадания обращенных линий

def product_slices(n): 
    for i in range(n): 
     yield (
      np.index_exp[np.newaxis] * i + 
      np.index_exp[:] + 
      np.index_exp[np.newaxis] * (n - i - 1) 
     ) 

def get_lines(n, k): 
    """ 
    Returns: 
     index (tuple): an object suitable for advanced indexing to get all possible lines 
     mask (ndarray): a boolean mask to apply to the result of the above 
    """ 
    fi = np.arange(k) 
    bi = fi[::-1] 
    ri = fi[:,None].repeat(k, axis=1) 

    all_i = np.concatenate((fi[None], bi[None], ri), axis=0) 

    # inedx which look up every possible line, some of which are not valid 
    index = tuple(all_i[s] for s in product_slices(n)) 

    # We incrementally allow lines that start with some number of `ri`s, and an `fi` 
    # [0] here means we chose fi for that index 
    # [2:] here means we chose an ri for that index 
    mask = np.zeros((all_i.shape[0],)*n, dtype=np.bool) 
    sl = np.index_exp[0] 
    for i in range(n): 
     mask[sl] = True 
     sl = np.index_exp[2:] + sl 

    return index, mask 

Применительно к вашему примеру:

# construct your example array 
n = 3 
k = 3 
data = np.arange(k**n).reshape((k,)*n) 

# apply my index_creating function 
index, mask = get_lines(n, k) 

# apply the index to your array 
lines = data[index][mask] 
print(lines) 
array([[ 0, 13, 26], 
     [ 2, 13, 24], 
     [ 0, 12, 24], 
     [ 1, 13, 25], 
     [ 2, 14, 26], 
     [ 6, 13, 20], 
     [ 8, 13, 18], 
     [ 6, 12, 18], 
     [ 7, 13, 19], 
     [ 8, 14, 20], 
     [ 0, 10, 20], 
     [ 2, 10, 18], 
     [ 0, 9, 18], 
     [ 1, 10, 19], 
     [ 2, 11, 20], 
     [ 3, 13, 23], 
     [ 5, 13, 21], 
     [ 3, 12, 21], 
     [ 4, 13, 22], 
     [ 5, 14, 23], 
     [ 6, 16, 26], 
     [ 8, 16, 24], 
     [ 6, 15, 24], 
     [ 7, 16, 25], 
     [ 8, 17, 26], 
     [ 0, 4, 8], 
     [ 2, 4, 6], 
     [ 0, 3, 6], 
     [ 1, 4, 7], 
     [ 2, 5, 8], 
     [ 0, 1, 2], 
     [ 3, 4, 5], 
     [ 6, 7, 8], 
     [ 9, 13, 17], 
     [11, 13, 15], 
     [ 9, 12, 15], 
     [10, 13, 16], 
     [11, 14, 17], 
     [ 9, 10, 11], 
     [12, 13, 14], 
     [15, 16, 17], 
     [18, 22, 26], 
     [20, 22, 24], 
     [18, 21, 24], 
     [19, 22, 25], 
     [20, 23, 26], 
     [18, 19, 20], 
     [21, 22, 23], 
     [24, 25, 26]]) 

Еще один хороший набор тестовых данных является np.moveaxis(np.indices((k,)*n), 0, -1), что дает массив, где каждое значение является его собственный индекс


Я решил эту проблему, прежде чем Внедрение a higher dimensional tic-tac-toe

3
In [1]: x=np.arange(27).reshape(3,3,3) 

Выбор индивидуального rows легко:

In [2]: x[0,0,:] 
Out[2]: array([0, 1, 2]) 
In [3]: x[0,:,0] 
Out[3]: array([0, 3, 6]) 
In [4]: x[:,0,0] 
Out[4]: array([ 0, 9, 18]) 

Вы можете перебрать размеры с индексного списка:

In [10]: idx=[slice(None),0,0] 
In [11]: x[idx] 
Out[11]: array([ 0, 9, 18]) 
In [12]: idx[2]+=1 
In [13]: x[idx] 
Out[13]: array([ 1, 10, 19]) 

Посмотрите на код для np.apply_along_axis, чтобы увидеть, как он реализует этот вид итерации.

Реформирование и разделение также могут содержать список rows.Для некоторых размеров это может потребоваться transpose:

In [20]: np.split(x.reshape(x.shape[0],-1),9,axis=1) 
Out[20]: 
[array([[ 0], 
     [ 9], 
     [18]]), array([[ 1], 
     [10], 
     [19]]), array([[ 2], 
     [11], 
     ... 

np.diag может получить диагонали от 2d подмассивов

In [21]: np.diag(x[0,:,:]) 
Out[21]: array([0, 4, 8]) 
In [22]: np.diag(x[1,:,:]) 
Out[22]: array([ 9, 13, 17]) 
In [23]: np.diag? 
In [24]: np.diag(x[1,:,:],1) 
Out[24]: array([10, 14]) 
In [25]: np.diag(x[1,:,:],-1) 
Out[25]: array([12, 16]) 

и исследовать np.diagonal для непосредственного применения в 3D. Также легко индексировать массив напрямую, с range и arange, x[0,range(3),range(3)].

Насколько я знаю, нет никакой функции для преодоления всех этих альтернатив. Поскольку размеры возвращаемых массивов могут различаться, нет смысла создавать такую ​​функцию в скомпилированном numpy-коде. Поэтому, даже если бы существовала функция, она могла бы пройти через альтернативы, как я изложил.

==============

Все 1d линии

x.reshape(-1,3) 
x.transpose(0,2,1).reshape(-1,3) 
x.transpose(1,2,0).reshape(-1,3) 

г/г диагональные и анти-диагональные

In [154]: i=np.arange(3) 
In [155]: j=np.arange(2,-1,-1) 
In [156]: np.concatenate((x[:,i,i],x[:,i,j]),axis=1) 
Out[156]: 
array([[ 0, 4, 8, 2, 4, 6], 
     [ 9, 13, 17, 11, 13, 15], 
     [18, 22, 26, 20, 22, 24]]) 
+0

Эти функции не получают всех последовательностей для каждого случая. Например, 'x [0,0,:]' только извлекает первую строку, тогда как она должна получать '[0, 1, 2]', '[3, 4, 5]', ..., '[21 , 22, 23] ',' [24, 25, 26] '. Последовательности в скобках в моем вопросе являются просто примерами желаемого результата. – 2Cubed

+0

Я говорил об итерации по различным показателям, необходимым для получения всех фрагментов. – hpaulj

+1

Это работает только для 'n = 3', правильно? – Eric

2

np.einsum может использоваться для сборки всех этих ки nd выражений; например:

# 3d diagonals 
print(np.einsum('iii->i', a)) 
# 2d diagonals 
print(np.einsum('iij->ij', a)) 
print(np.einsum('iji->ij', a)) 
+0

Но это суммирование, а не просто нарезка – Eric

+1

, оно суммирует только индексы, которые появляются слева, но не справа. –

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