Эффективный итерация по индексам массива произвольного порядка

Скажем, у меня есть произвольный массив переменного порядка N. Например: A - это массив 2x3x3, это массив порядка 3 с 2,3 и 3 размерами вдоль его трех индексов.

Я хотел бы эффективно перебрать каждый элемент. Если бы я знал априори порядок, я мог бы сделать что-то вроде (на python),

#for order 3 
import numpy as np
shape = np.shape(A)
i = 0
while i < shape[0]:
  j = 0
  while j < shape[1]:
    k = 0
    while k < shape[2]:
      #code using i,j,k
      k += 1
    j += 1
  i += 1

Теперь предположим, что я не знаю порядок A, т.е. я не знаю априори длину shape. Как я могу быстро переставить все элементы массива?


person Josh Albert    schedule 11.09.2015    source источник
comment
Обязательно ли отформатирован массив как массив numpy?   -  person Alea Kootz    schedule 11.09.2015
comment
Чего вы на самом деле пытаетесь достичь? Вы могли бы сделать, например, for indices in itertools.product(*map(range, shape)):, но кажется маловероятным, что это лучший подход.   -  person jonrsharpe    schedule 11.09.2015
comment
Вам нужно сохранить результаты #code, используя i, j, k?   -  person Chad S.    schedule 11.09.2015
comment
@jonrsharpe: Альтернатива: for indices in range(a.size): indices = np.unravel_index(i, a.shape).   -  person Sven Marnach    schedule 11.09.2015
comment
Вы можете сделать это рекурсивно, ниже я написал ответ, показывающий, как это сделать.   -  person Alea Kootz    schedule 11.09.2015
comment
@AustinKootz: Все становится довольно сложно, тебе не кажется? Итеративная реализация приведена в docs.python.org/2/library/ itertools.html # itertools.product. Но поскольку у нас уже есть эта функция в стандартной библиотеке, мы могли бы с таким же успехом использовать ее, как было предложено выше jonrsharpe. Еще одно простое решение - использовать np.unravel_index() или, что еще проще, перебрать a.flat.   -  person Sven Marnach    schedule 11.09.2015
comment
@SvenMarnach Возможно, вы правы. Я просто хотел предоставить полезный и понятный метод для выполнения того, что хотел OP. OP, вероятно, должен использовать itertools.product() или np.unravel_index(). Однако использование этих методов оставило бы их в неведении относительно того, как на самом деле работает рекурсивный инструмент. Мой пример ниже должен помочь им понять. Тогда они смогут продуктивно использовать другие предложенные методы.   -  person Alea Kootz    schedule 11.09.2015
comment
OP, вы можете принять один из этих ответов   -  person coneyhelixlake    schedule 19.10.2015


Ответы (4)


Для этого есть много способов, например итерация по a.ravel() или a.flat. Однако перебор каждого отдельного элемента массива в цикле Python никогда не будет особенно эффективным.

person Sven Marnach    schedule 11.09.2015
comment
Итерация по a.flat не позволит выполнять операции на основе местоположения в исходном n-мерном массиве (местоположение элемента i, j, k). Тем не менее, он отлично подойдет для применения какой-либо функции к каждому термину. - person Alea Kootz; 11.09.2015
comment
@AustinKootz Этого можно легко достичь, перебирая enumerate(a.flat) вместо a.flat, так же, как и для простых списков Python. - person Sven Marnach; 11.09.2015

Я не думаю, что имеет значение, какой индекс вы выбираете для перестановки первым, какой индекс вы выбираете для перестановки над вторым и т. Д., Потому что ваш внутренний оператор while всегда будет выполняться один раз для каждой комбинации i, j и k.

person coneyhelixlake    schedule 11.09.2015

Если вам нужно сохранить результаты вашей операции (и если предположить, что это функция от A и i, j, k), вы захотите использовать что-то вроде этого:

import itertools
import numpy as np

results = ( (position, code(A,position))
              for indices in itertools.product(*(range(i) for i in np.shape(A))))

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

person Chad S.    schedule 11.09.2015

Если массив формата array = [[[1,2,3,4],[1,2]],[[1],[1,2,3]]]

Вы можете использовать следующую структуру:

array = [[[1,2,3,4],[1,2]],[[1],[1,2,3]]]
indices = []
def iter_array(array,indices):
    indices.append(0)
    for a in array:
        if isinstance(a[0],list):
            iter_array(a,indices)
        else:
            indices.append(0)
            for nonlist in a:
                #do something using each element in indices
                #print(indices)
                indices.append(indices.pop()+1)
            indices.pop()
        indices.append(indices.pop()+1)
    indices.pop()

iter_array(array,indices)

Это должно работать для обычных «массивов» вложенных списков. Я не знаю, можно ли имитировать это, используя структуру массива numpy.

person Alea Kootz    schedule 11.09.2015
comment
OP заявил, что они даже не знают количество измерений (называемое порядком в сообщении), поэтому вы не знаете, сколько петель вложить. - person Sven Marnach; 11.09.2015
comment
Если бы мы знали максимальный порядок, мы могли бы предварять каждый for оператор if isinstance(array[i][j],list):, чтобы он мог остановиться на соответствующем уровне. - person Alea Kootz; 11.09.2015
comment
Я не совсем уверен, как бы рекурсивно вызывать вложенные циклы for, но я считаю, что это будет лучший способ сделать это. - person Alea Kootz; 11.09.2015
comment
Я переписал его, чтобы он был рекурсивным. Я могу внести дополнительные незначительные правки для ясности - person Alea Kootz; 11.09.2015