Как преобразовать отрицательные элементы в ноль без цикла? - PullRequest
41 голосов
/ 03 августа 2010

Если у меня есть массив вроде

a = np.array([2, 3, -1, -4, 3])

Я хочу установить все отрицательные элементы на ноль: [2, 3, 0, 0, 3]. Как это сделать с NumPy без явного для? Мне нужно использовать модифицированный a в вычислениях, например

c = a * b

, где b - другой массив с той же длиной, что и исходный a

Заключение

import numpy as np
from time import time

a = np.random.uniform(-1, 1, 20000000)
t = time(); b = np.where(a>0, a, 0); print ("1. ", time() - t)
a = np.random.uniform(-1, 1, 20000000)
t = time(); b = a.clip(min=0); print ("2. ", time() - t)
a = np.random.uniform(-1, 1, 20000000)
t = time(); a[a < 0] = 0; print ("3. ", time() - t)
a = np.random.uniform(-1, 1, 20000000)
t = time(); a[np.where(a<0)] = 0; print ("4. ", time() - t)
a = np.random.uniform(-1, 1, 20000000)
t = time(); b = [max(x, 0) for x in a]; print ("5. ", time() - t)
  1. +1,38629984856
  2. 0,516846179962 <- более быстрый клип (мин = 0); </li>
  3. 0,615426063538
  4. 0,944557905197
  5. +51,7364809513

Ответы [ 4 ]

78 голосов
/ 03 августа 2010
a = a.clip(min=0)
12 голосов
/ 03 августа 2010

Я бы сделал это:

a[a < 0] = 0

Если вы хотите сохранить оригинал a и установить только отрицательные элементы в ноль при копировании, вы можете сначала скопировать массив:

c = a.copy()
c[c < 0] = 0
9 голосов
/ 23 сентября 2015

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

b = a*(a>0) # copies data

или

a *= (a>0) # in-place zero-ing

Я запустил тесты со временем, предварительно рассчитав <и>, потому что некоторые из них модифицируют на месте, и это сильно повлияет на результаты. Во всех случаях a было np.random.uniform(-1, 1, 20000000), но с отрицаниями, уже установленными на 0, но L = a < 0 и G = a > 0 до того, как a было изменено. На clip относительно негативно влияют, поскольку он не использует L или G (однако для расчета этих значений на одной и той же машине потребовалось всего 17 мс каждый, поэтому это не является основной причиной разницы в скорости).

%timeit b = np.where(G, a, 0)  # 132ms  copies
%timeit b = a.clip(min=0)      # 165ms  copies
%timeit a[L] = 0               # 158ms  in-place
%timeit a[np.where(L)] = 0     # 122ms  in-place
%timeit b = a*G                # 87.4ms copies
%timeit np.multiply(a,G,a)     # 40.1ms in-place (normal code would use `a*=G`)

При выборе метода штрафования вместо методов вместо clip появляются следующие моменты времени:

%timeit b = np.where(a>0, a, 0)             # 152ms
%timeit b = a.clip(min=0)                   # 165ms
%timeit b = a.copy(); b[a<0] = 0            # 231ms
%timeit b = a.copy(); b[np.where(a<0)] = 0  # 205ms
%timeit b = a*(a>0)                         # 108ms
%timeit b = a.copy(); b*=a>0                # 121ms

Методы на месте штрафуются на 20 мс (время, необходимое для вычисления a>0 или a<0), а методы на месте штрафуются на 73-83 мс (поэтому для выполнения * 1024 требуется около 53-63 мс) *).

В целом методы умножения намного быстрее, чем clip. Если не на месте, это будет 1,5x быстрее. Если вы можете сделать это на месте, то это будет 2,75x быстрее.

4 голосов
/ 03 августа 2010

Использование , где

a[numpy.where(a<0)] = 0
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...