Замена элементов Numpy, если условие выполнено

У меня есть большой массив numpy, которым мне нужно манипулировать, чтобы каждый элемент изменялся либо на 1, либо на 0, если выполняется условие (позже будет использоваться как пиксельная маска). В массиве около 8 миллионов элементов, и мой текущий метод занимает слишком много времени для конвейера сокращения:

for (y,x), value in numpy.ndenumerate(mask_data): 

    if mask_data[y,x]<3: #Good Pixel
        mask_data[y,x]=1
    elif mask_data[y,x]>3: #Bad Pixel
        mask_data[y,x]=0

Есть ли функция numpy, которая ускорит это?


person ChrisFro    schedule 04.11.2013    source источник
comment
Что вы хотите, чтобы произошло, если mask_data[y,x]==3?   -  person DSM    schedule 04.11.2013
comment
Хороший момент, это все равно будет плохой пиксель. Я изменю условие на if mask_data[y,x]>=3:   -  person ChrisFro    schedule 04.11.2013


Ответы (5)


>>> import numpy as np
>>> a = np.random.randint(0, 5, size=(5, 4))
>>> a
array([[4, 2, 1, 1],
       [3, 0, 1, 2],
       [2, 0, 1, 1],
       [4, 0, 2, 3],
       [0, 0, 0, 2]])
>>> b = a < 3
>>> b
array([[False,  True,  True,  True],
       [False,  True,  True,  True],
       [ True,  True,  True,  True],
       [False,  True,  True, False],
       [ True,  True,  True,  True]], dtype=bool)
>>> 
>>> c = b.astype(int)
>>> c
array([[0, 1, 1, 1],
       [0, 1, 1, 1],
       [1, 1, 1, 1],
       [0, 1, 1, 0],
       [1, 1, 1, 1]])

Вы можете сократить это с помощью:

>>> c = (a < 3).astype(int)
person Steve Barnes    schedule 04.11.2013
comment
как сделать это с определенными столбцами, не вырезая некоторые столбцы, а затем снова назначая их? например, только элементы в столбцах [2, 3] должны изменять значение при выполнении условий, в то время как другие столбцы не изменятся независимо от того, выполнены условия или нет. - person kuixiong; 20.07.2019
comment
Верно, но только для случая нулей и единиц. См. более общий ответ ниже (по стоимости эффективности) - person borgr; 17.05.2020

>>> a = np.random.randint(0, 5, size=(5, 4))
>>> a
array([[0, 3, 3, 2],
       [4, 1, 1, 2],
       [3, 4, 2, 4],
       [2, 4, 3, 0],
       [1, 2, 3, 4]])
>>> 
>>> a[a > 3] = -101
>>> a
array([[   0,    3,    3,    2],
       [-101,    1,    1,    2],
       [   3, -101,    2, -101],
       [   2, -101,    3,    0],
       [   1,    2,    3, -101]])
>>>

См., например, Индексирование с помощью логических массивов.

person ev-br    schedule 04.11.2013
comment
отличный материал, спасибо! Если вы хотите сослаться на значение, которое вы меняете, вы можете использовать что-то вроде a[a > 3] = -101+a[a > 3]. - person pexmar; 07.07.2017
comment
@pexmar Хотя, если вы сделаете a[a > 3] = -101+a[a > 3] вместо a[a > 3] += -101, вы, скорее всего, столкнетесь с утечкой памяти. - person Samuel Prevost; 14.12.2018
comment
как вы относитесь к значению, которое вы меняете, как спросил pexmar ?? - person Juan; 16.08.2019

Самый быстрый (и наиболее гибкий) способ — использовать np.where, который выбирает между двумя массивами в соответствии с маской (массив истинных и ложных значений):

import numpy as np
a = np.random.randint(0, 5, size=(5, 4))
b = np.where(a<3,0,1)
print('a:',a)
print()
print('b:',b)

который будет производить:

a: [[1 4 0 1]
 [1 3 2 4]
 [1 0 2 1]
 [3 1 0 0]
 [1 4 0 1]]

b: [[0 1 0 0]
 [0 1 0 1]
 [0 0 0 0]
 [1 0 0 0]
 [0 1 0 0]]
person Markus Dutschke    schedule 04.05.2017
comment
что будет лучшим способом, если я не хочу ничего заменять, если условие не выполнено? Замените предоставленным значением только при выполнении условия, если не оставьте исходный номер как есть.... - person Abhishek Sengupta; 29.07.2020
comment
чтобы заменить все значения в a, которые меньше 3, и оставить все как есть, используйте a[a<3] = 0 - person Markus Dutschke; 29.07.2020

Вы можете создать свой массив масок за один шаг, как это

mask_data = input_mask_data < 3

Это создает логический массив, который затем можно использовать в качестве пиксельной маски. Обратите внимание, что мы не изменили входной массив (как в вашем коде), а создали новый массив для хранения данных маски — я бы рекомендовал сделать это именно так.

>>> input_mask_data = np.random.randint(0, 5, (3, 4))
>>> input_mask_data
array([[1, 3, 4, 0],
       [4, 1, 2, 2],
       [1, 2, 3, 0]])
>>> mask_data = input_mask_data < 3
>>> mask_data
array([[ True, False, False,  True],
       [False,  True,  True,  True],
       [ True,  True, False,  True]], dtype=bool)
>>> 
person YXD    schedule 04.11.2013
comment
Ага. Если ОП действительно хочет 0 и 1, он может использовать .astype(int) или *1, но массив True и False так же хорош, как и он. - person DSM; 04.11.2013

Я не уверен, что понял ваш вопрос, но если вы напишете:

mask_data[:3, :3] = 1
mask_data[3:, 3:] = 0

Это сделает все значения данных маски, чьи индексы x и y меньше 3, равными 1, а все остальные равными 0.

person mamalos    schedule 04.11.2013