Список итераций массива 3d numpy по всем элементам с помощью nditer () из numpy 1.15.3

У меня есть список 3D-массивов Numpy, и я хочу перебрать каждый элемент этой структуры и внести некоторые изменения с помощью состояний if. Приведенный ниже код выполняет то, что я хочу сделать:

for counter1, entry in enumerate(all_frames_flow):
    for counter2, entry2 in enumerate(entry):
        for counter3, entry3 in enumerate(entry2):
            for counter4, entry4 in enumerate(entry3):
                if entry4 < -20.0:
                    all_frames_flow[counter1][counter2][counter3][counter4]=-20.0
                if entry4 > 20.0:
                    all_frames_flow[counter1][counter2][counter3][counter4]=20.0
                all_frames_flow[counter1][counter2][counter3][counter4]/=20

Но мне было интересно, есть ли более питонический способ. В numpy >=1.15.0 я попробовал этот новый код из документации, но ничего не вышло, он не дает желаемых результатов, я вижу значения больше, чем abs(20), и мне интересно, почему это так:

for counteref, _ in enumerate(backup2):                    
    with np.nditer(backup2[counteref], op_flags=['readwrite'], order = 'K') as it:
            for x in it:
                #print x
                if (x < -20.0):
                    x=-20.0
                if (x > 20.0):
                    x = 20.0
                x/=20.0
nditer сложно использовать, и обычно он не быстрее или (это трудно определить) питонический.
hpaulj 31.10.2018 14:46
1
1
96
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Я предпочитаю numpy.ndindex, чтобы сэкономить несколько вложенных циклов и сохранить разумную плоскую структуру функции. У меня такое ощущение, что numpy.nditer более полезен для случаев с большим количеством массивов для перебора:

mat = np.random.rand(2, 3, 4) * 100 - 50
for i in np.ndindex(*mat.shape):
    if mat[i] < -20:
        mat[i] = -20
    if mat[i] > 20:
        mat[i] = 20  
mat /= 20.0

Альтернативой является использование numpy.where для условий и операций, которые не зависят от конкретного индекса, но применяются ко всему массиву:

mat = np.random.rand(2, 3, 4) * 100 - 50
#              Condition, if True, if False
mat = np.where(mat < -20,     -20,     mat)
mat = np.where(mat > +20,      20,     mat)
mat /= 20.0

И для вашего конкретного случая обрезки значений массива за пределами определенного диапазона numpy.clip (arr, a_min, a_max), вероятно, самый простой и быстрый способ.

ndindex - один из немногих фрагментов интерпретируемого кода numpy, который использует np.nditer.
hpaulj 31.10.2018 18:36
Ответ принят как подходящий

Прежде чем пробовать лучшие / альтернативные итераторы, вы должны попытаться выполнить задачу без итерации (то есть делать что-то с помощью скомпилированного кода numpy)

In [347]: arr = np.random.randint(-40,40,(2,3,4))

Например, есть метод clip:

In [348]: arr.clip(-20, 20)
Out[348]: 
array([[[-20, -20,  20,  -6],
        [-15, -17,  -8, -20],
        [  2, -20, -16,  20]],

       [[-20,   3, -20,  17],
        [ 20,  20,  20, -17],
        [ 11, -20,  20,   0]]])

и разделение всего на 20 в numpy тривиально:

In [349]: _/20
Out[349]: 
array([[[-1.  , -1.  ,  1.  , -0.3 ],
        [-0.75, -0.85, -0.4 , -1.  ],
        [ 0.1 , -1.  , -0.8 ,  1.  ]],

       [[-1.  ,  0.15, -1.  ,  0.85],
        [ 1.  ,  1.  ,  1.  , -0.85],
        [ 0.55, -1.  ,  1.  ,  0.  ]]])

А еще лучше научитесь делать такие вещи с помощью логической маскировки:

In [351]: arr
Out[351]: 
array([[[-32, -30,  39,  -6],
        [-15, -17,  -8, -34],
        [  2, -31, -16,  35]],

       [[-39,   3, -37,  17],
        [ 31,  30,  28, -17],
        [ 11, -24,  26,   0]]])

In [354]: mask1 = arr<-20
In [355]: mask2 = arr>20
In [356]: mask1
Out[356]: 
array([[[ True,  True, False, False],
        [False, False, False,  True],
        [False,  True, False, False]],

       [[ True, False,  True, False],
        [False, False, False, False],
        [False,  True, False, False]]])
In [357]: mask2
Out[357]: 
array([[[False, False,  True, False],
        [False, False, False, False],
        [False, False, False,  True]],

       [[False, False, False, False],
        [ True,  True,  True, False],
        [False, False,  True, False]]])
In [358]: arr[mask1]=-20
In [359]: arr[mask2]=20
In [360]: arr
Out[360]: 
array([[[-20, -20,  20,  -6],
        [-15, -17,  -8, -20],
        [  2, -20, -16,  20]],

       [[-20,   3, -20,  17],
        [ 20,  20,  20, -17],
        [ 11, -20,  20,   0]]])

Что касается вашей итерации, важно помнить, что в любой итерации Python вы не можете использовать

для x в ...: х = -20,0

для изменения источника. Это назначение x=... присваивает новое значение переменной x и разрывает ее связь с итерацией. Попробуйте сделать это с помощью простого списка, если не понимаете почему. Вы должны изменить переменную на месте. Если x - простое целое число, это невозможно.

На первой итерации вы индексируете и изменяете all_frames_flow, поэтому он работает:

all_frames_flow [counter1] [counter2, counter3, counter4] = - 20,0

nditer предоставляет изменяемую итерационную переменную, поэтому вы можете:

In [364]: with np.nditer(arr, op_flags=['readwrite'], order = 'K') as it:
     ...:    for x in it:
     ...:        #print x
     ...:        if (x < -20.0):
     ...:            x[...]=-20.0      # change x in-place
     ...:        if (x > 20.0):
     ...:            x[...] = 20.0
     ...:            

Все примеры nditer, которые изменяют значения, должны использовать эту нотацию [...]=.

Я не поощряю использование nditer, по крайней мере, кода Python. В коде Python это наиболее полезно как способ тестирования идей, которые будут реализованы в вашем собственном скомпилированном коде (с cython). Он не дает никаких преимуществ в скорости.

но что из всего быстрее выполняется?

GGEv 01.11.2018 12:00

Есть инструменты для определения времени казни.

hpaulj 01.11.2018 12:12

последняя строка должна быть x [...] / = 20.0, верно? или x / = 20?

GGEv 07.11.2018 15:07

Если поделить все x /= 20 правильно. x[...] /= работает, но не нужен.

hpaulj 08.11.2018 05:56

Другие вопросы по теме