Devolva com eficiência o índice do primeiro valor que satisfaça a condição na matriz
Preciso encontrar o índice do primeiro valor em uma matriz NumPy 1d ou série numérica do Pandas, satisfazendo uma condição. A matriz é grande e o índice pode estar próximo do inícioo final da matriz,o a condição pode não ser atendida. Não sei dizer com antecedência o que é mais provável. Se a condição não for atendida, o valor de retorno deve ser-1
. Eu considerei algumas abordagens.
# func(arr) returns a Boolean array
idx = next(iter(np.where(func(arr))[0]), -1)
Mas isso geralmente é muito lento comofunc(arr)
aplica uma função vetorizada nointeir array em vez de parar quando a condição for atendida. Especificamente, é caro quando a condição é atendida perto docomeça da matriz.
np.argmax
é marginalmente mais rápido, mas falha ao identificar quando uma condição éNunc conheceu
np.random.seed(0)
arr = np.random.rand(10**7)
assert next(iter(np.where(arr > 0.999999)[0]), -1) == np.argmax(arr > 0.999999)
%timeit next(iter(np.where(arr > 0.999999)[0]), -1) # 21.2 ms
%timeit np.argmax(arr > 0.999999) # 17.7 ms
np.argmax(arr > 1.0)
retorna0
, ou seja, uma instância em que a condição énã satisfeito.
# func(arr) returns a Boolean scalar
idx = next((idx for idx, val in enumerate(arr) if func(arr)), -1)
Mas isso é muito lento quando a condição é atendida perto dofi da matriz. Presumivelmente, isso ocorre porque a expressão do gerador possui uma sobrecarga cara de um grande número de__next__
chamadas.
É istosempr um compromisso ou existe uma maneira, para @ genéricfunc
, para extrair o primeiro índice com eficiênci
Para comparações, assumafunc
localiza o índice quando um valor é maior que uma determinada constante:
# Python 3.6.5, NumPy 1.14.3, Numba 0.38.0
import numpy as np
np.random.seed(0)
arr = np.random.rand(10**7)
m = 0.9
n = 0.999999
# Start of array benchmark
%timeit next(iter(np.where(arr > m)[0]), -1) # 43.5 ms
%timeit next((idx for idx, val in enumerate(arr) if val > m), -1) # 2.5 µs
# End of array benchmark
%timeit next(iter(np.where(arr > n)[0]), -1) # 21.4 ms
%timeit next((idx for idx, val in enumerate(arr) if val > n), -1) # 39.2 ms