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?

Pion
źródło

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]),)
deinonychusaur
źródło
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]),)
tiago
źródło
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])
Bi Rico
źródło
13
a = np.array([1,2,3,4,5,6,7,8,9])
b = a[(a>2) & (a<8)]
Abhishek
źródło
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.

GM
źródło
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”.

Nathan
źródło
1
a = np.array([1, 3, 5, 6, 9, 10, 14, 15, 56])
np.argwhere((a>=6) & (a<=10))
Abhishek
źródło
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.

k1m190r
źródło
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.

Jorge Crvz
źródło
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]  
Vigneshwaran Narayanan
źródło
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)
cvanelteren
źródło
-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.

Deba Pratim Saha
źródło