Numpy: znajdź indeks elementów w zasięgu


85

Mam numeryczną tablicę liczb, na przykład

a = np.array([1, 3, 5, 6, 9, 10, 14, 15, 56])  

Chciałbym znaleźć wszystkie indeksy elementów w określonym zakresie. Na przykład, jeśli zakres to (6, 10), odpowiedź powinna wynosić (3, 4, 5). Czy jest do tego wbudowana funkcja?

Odpowiedzi:


140

Możesz użyć, np.whereaby uzyskać indeksy i np.logical_andustawić dwa warunki:

import numpy as np
a = np.array([1, 3, 5, 6, 9, 10, 14, 15, 56])

np.where(np.logical_and(a>=6, a<=10))
# returns (array([3, 4, 5]),)

6
Przy okazji, to samo osiąga się przez np.nonzero(np.logical_and(a>=6, a<=10)).
3lectrologos

10
Również np.where((a > 6) & (a <= 10))
ELinda

wydaje się, że nie radzi sobie dobrze z wielowymiarowymi tablicami
Monica Heddneck

1
@ELinda np.logical_andjest odrobinę szybsza niż &chociaż. I np.wherejest szybszy niż np.nonzero.
Skillmon lubi topanswers.xyz

Ma bardzo złą wydajność dla większych tablic
EZLearner

62

Jak w odpowiedzi @ deinonychusaur, ale jeszcze bardziej zwięzłe:

In [7]: np.where((a >= 6) & (a <=10))
Out[7]: (array([3, 4, 5]),)

19
Ładny. Możesz także zrobić, a[(a >= 6) & (a <= 10)]jeśli ajest tablicą numpy.
ws_e_c421

1
Na wypadek, gdyby ktoś pomylił się, tak jak ja ze sformułowaniem komentarza: to nie działa w przypadku zwykłych list, to tylko wtedy, gdy ajest tablicą numpy
Prof.

14

Pomyślałem, że dodam to, ponieważ aw podanym przykładzie jest posortowany:

import numpy as np
a = [1, 3, 5, 6, 9, 10, 14, 15, 56] 
start = np.searchsorted(a, 6, 'left')
end = np.searchsorted(a, 10, 'right')
rng = np.arange(start, end)
rng
# array([3, 4, 5])


6

Podsumowanie odpowiedzi

Aby zrozumieć, jaka jest najlepsza odpowiedź, możemy określić czas, używając innego rozwiązania. Niestety pytanie nie było dobrze postawione, więc są odpowiedzi na różne pytania, tutaj staram się wskazać odpowiedź na to samo pytanie. Biorąc pod uwagę tablicę:

a = np.array([1, 3, 5, 6, 9, 10, 14, 15, 56])

Odpowiedzią powinny być indeksy elementów między pewnym przedziałem, który zakładamy włącznie, w tym przypadku 6 i 10.

answer = (3, 4, 5)

Odpowiadające wartościom 6,9,10.

Aby przetestować najlepszą odpowiedź, możemy użyć tego kodu.

import timeit
setup = """
import numpy as np
import numexpr as ne

a = np.array([1, 3, 5, 6, 9, 10, 14, 15, 56])
# we define the left and right limit
ll = 6
rl = 10

def sorted_slice(a,l,r):
    start = np.searchsorted(a, l, 'left')
    end = np.searchsorted(a, r, 'right')
    return np.arange(start,end)
"""

functions = ['sorted_slice(a,ll,rl)', # works only for sorted values
'np.where(np.logical_and(a>=ll, a<=rl))[0]',
'np.where((a >= ll) & (a <=rl))[0]',
'np.where((a>=ll)*(a<=rl))[0]',
'np.where(np.vectorize(lambda x: ll <= x <= rl)(a))[0]',
'np.argwhere((a>=ll) & (a<=rl)).T[0]', # we traspose for getting a single row
'np.where(ne.evaluate("(ll <= a) & (a <= rl)"))[0]',]

functions2 = [
   'a[np.logical_and(a>=ll, a<=rl)]',
   'a[(a>=ll) & (a<=rl)]',
   'a[(a>=ll)*(a<=rl)]',
   'a[np.vectorize(lambda x: ll <= x <= rl)(a)]',
   'a[ne.evaluate("(ll <= a) & (a <= rl)")]',
]

Wyniki

Wyniki przedstawiono na poniższym wykresie. Na górze najszybsze rozwiązania. wprowadź opis obrazu tutaj Jeśli zamiast indeksów chcesz wyodrębnić wartości, możesz przeprowadzić testy przy użyciu funkcji functions2, ale wyniki są prawie takie same.


Te wyniki dotyczą tylko tablicy o określonej długości (tutaj wybrałeś bardzo małą tablicę). Te wyniki zmieniają się szybko w przypadku większych tablic
EZLearner

4

Ten fragment kodu zwraca wszystkie liczby w tablicy numpy między dwiema wartościami:

a = np.array([1, 3, 5, 6, 9, 10, 14, 15, 56] )
a[(a>6)*(a<10)]

Działa w następujący sposób: (a> 6) zwraca tablicę numpy z True (1) i False (0), tak samo jak (a <10). Mnożąc te dwa razem, otrzymujemy tablicę z wartością True, jeśli obie instrukcje są True (ponieważ 1x1 = 1) lub False (ponieważ 0x0 = 0 i 1x0 = 0).

Część a [...] zwraca wszystkie wartości tablicy a, gdzie tablica między nawiasami zwraca instrukcję True.

Oczywiście możesz to bardziej skomplikować, mówiąc na przykład

...*(1-a<10) 

co jest podobne do instrukcji „i nie”.


1
a = np.array([1, 3, 5, 6, 9, 10, 14, 15, 56])
np.argwhere((a>=6) & (a<=10))

1

Chciałem dodać numexpr do miksu:

import numpy as np
import numexpr as ne

a = np.array([1, 3, 5, 6, 9, 10, 14, 15, 56])  

np.where(ne.evaluate("(6 <= a) & (a <= 10)"))[0]
# array([3, 4, 5], dtype=int64)

Miałoby to sens tylko w przypadku większych tablic z milionami ... lub w przypadku przekroczenia limitu pamięci.


1

Innym sposobem jest:

np.vectorize(lambda x: 6 <= x <= 10)(a)

która zwraca:

array([False, False, False,  True,  True,  True, False, False, False])

Czasami jest przydatny do maskowania szeregów czasowych, wektorów itp.


0
s=[52, 33, 70, 39, 57, 59, 7, 2, 46, 69, 11, 74, 58, 60, 63, 43, 75, 92, 65, 19, 1, 79, 22, 38, 26, 3, 66, 88, 9, 15, 28, 44, 67, 87, 21, 49, 85, 32, 89, 77, 47, 93, 35, 12, 73, 76, 50, 45, 5, 29, 97, 94, 95, 56, 48, 71, 54, 55, 51, 23, 84, 80, 62, 30, 13, 34]

dic={}

for i in range(0,len(s),10):
    dic[i,i+10]=list(filter(lambda x:((x>=i)&(x<i+10)),s))
print(dic)

for keys,values in dic.items():
    print(keys)
    print(values)

Wynik:

(0, 10)
[7, 2, 1, 3, 9, 5]
(20, 30)
[22, 26, 28, 21, 29, 23]
(30, 40)
[33, 39, 38, 32, 35, 30, 34]
(10, 20)
[11, 19, 15, 12, 13]
(40, 50)
[46, 43, 44, 49, 47, 45, 48]
(60, 70)
[69, 60, 63, 65, 66, 67, 62]
(50, 60)
[52, 57, 59, 58, 50, 56, 54, 55, 51]  

0

To może nie być najładniejsze, ale działa w każdym wymiarze

a = np.array([[-1,2], [1,5], [6,7], [5,2], [3,4], [0, 0], [-1,-1]])
ranges = (0,4), (0,4) 

def conditionRange(X : np.ndarray, ranges : list) -> np.ndarray:
    idx = set()
    for column, r in enumerate(ranges):
        tmp = np.where(np.logical_and(X[:, column] >= r[0], X[:, column] <= r[1]))[0]
        if idx:
            idx = idx & set(tmp)
        else:
            idx = set(tmp)
    idx = np.array(list(idx))
    return X[idx, :]

b = conditionRange(a, ranges)
print(b)

-4

Możesz użyć, np.clip()aby osiągnąć to samo:

a = [1, 3, 5, 6, 9, 10, 14, 15, 56]  
np.clip(a,6,10)

Jednak przechowuje wartości mniejsze niż i większe niż odpowiednio 6 i 10.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.