как реализовать несколько ifelse в numpy - PullRequest
2 голосов
/ 22 января 2020

У меня есть такой массив, и мне нужно заменить каждые 1 на 2, каждые 3 на 4, каждые 4 на 1. Есть ли способ сделать это только с помощью np, а не циклов?

import numpy as np
np.random.seed(2)
arr=np.random.randint(1,5,(3,3),int)
arr

array([[1, 4, 2],
       [1, 3, 4],
       [3, 4, 1]])

Если я последовательно использую маску массива, она не даст ожидаемого результата:

array([[2, 1, 2], 
       [2, 4, 1],
       [4, 1, 2]]) 

Она основана на условных логах c, а не на математической формуле

Ответы [ 5 ]

2 голосов
/ 22 января 2020

Вот один с np.searchsorted для повышения производительности -

def map_values(arr, old_val, new_val):
    sidx = old_val.argsort()
    idx = np.searchsorted(old_val,arr,sorter=sidx)
    return np.where(old_val[idx]==arr, new_val[sidx[idx]], arr)

Пробный прогон -

In [40]: arr
Out[40]: 
array([[1, 4, 2],
       [1, 3, 4],
       [3, 4, 1]])

In [41]: old_val = np.array([1,3,4])
    ...: new_val = np.array([2,4,1])

In [42]: map_values(arr, old_val, new_val)
Out[42]: 
array([[2, 1, 2],
       [2, 4, 1],
       [4, 1, 2]])
2 голосов
/ 22 января 2020

Если значения массива необязательно находятся в диапазоне от 1 до 4, вы можете использовать np.select:

import numpy as np

a = np.random.randint(1,5, (3,3))


condlist = [np.logical_or(a==1, a==2),  a==3, a==4]
choicelist= [2, 4, 1]
b = np.select(condlist, choicelist) 

, который не заботится о порядке условий

1 голос
/ 22 января 2020

Может сделать это с помощью лямбда-функции и np.vectorize():

import numpy as np
np.random.seed(2)
arr=np.random.randint(1,5,(3,3),int)

f = lambda x: x%4 + 1 if x in [1,3,4] else x
vfunc = np.vectorize(f)

Использование:

>>> vfunc(arr)
array([[2, 1, 2],
       [2, 4, 1],
       [4, 1, 2]])
0 голосов
/ 22 января 2020

Вы хотите arr % 4 + 1, за исключением случая 2, который остается прежним. Так что используйте np.where, чтобы найти все 2. Затем выполните arr % 4 + 1, затем сбросьте все 2 с.

import numpy as np

np.random.seed(2)
arr=np.random.randint(1,5,(3,3),int)

twos = np.where(arr == 2)
arr = arr % 4 + 1
arr[twos] = 2
print(arr)
0 голосов
/ 22 января 2020

Вы должны быть осторожны с порядком назначений. Например, если вы выполните

arr[arr == 4] = 1
arr[arr == 1] = 2

Теперь все элементы, которые изначально были 4, будут 2, а не 1, как вы и предполагали.

Одно из решений состоит в том, чтобы тщательно продумайте порядок назначений:

arr[arr == 1] = 2
arr[arr == 4] = 1

Однако, это очень хрупко и развалится, когда вы представите больше из них. Было бы лучше создать маски заранее из исходного массива:

ones = arr == 1
fours = arr == 4
arr[ones] = 2
arr[fours] = 1

Теперь порядок назначений не будет иметь значения, потому что маски определяются до изменения массива.

...