Numpy: Index der Elemente innerhalb des Bereichs finden

84

Ich habe eine Reihe von Zahlen, zum Beispiel

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

Ich möchte alle Indizes der Elemente innerhalb eines bestimmten Bereichs finden. Wenn der Bereich beispielsweise (6, 10) ist, sollte die Antwort (3, 4, 5) sein. Gibt es eine eingebaute Funktion, um dies zu tun?

Bob
quelle

Antworten:

137

Sie können verwenden np.where, um Indizes abzurufen und np.logical_andzwei Bedingungen festzulegen:

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
quelle
6
Übrigens wird das gleiche erreicht durch np.nonzero(np.logical_and(a>=6, a<=10)).
3lectrologos
8
Auch np.where((a > 6) & (a <= 10))
ELinda
scheint nicht gut mit mehrdimensionalen Arrays zu tun
Monica Heddneck
1
@ ELinda np.logical_andist ein bisschen schneller als &obwohl. Und np.whereist schneller als np.nonzero.
Skillmon mag topanswers.xyz
Es hat eine sehr schlechte Leistung für größere Arrays
EZLearner
62

Wie in der Antwort von @ deinonychusaur, aber noch kompakter:

In [7]: np.where((a >= 6) & (a <=10))
Out[7]: (array([3, 4, 5]),)
tiago
quelle
18
Nett. Sie können auch tun, a[(a >= 6) & (a <= 10)]wenn aes sich um ein Numpy-Array handelt.
ws_e_c421
1
Nur für den Fall, dass jemand wie ich mit dem Wortlaut des Kommentars verwechselt wird: Dies funktioniert nicht für gewöhnliche Listen, es ist nur, wenn aes sich um ein numpy Array handelt
Prof
14

Ich dachte, ich würde dies hinzufügen, weil das ain dem von Ihnen angegebenen Beispiel sortiert ist:

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
quelle
12
a = np.array([1,2,3,4,5,6,7,8,9])
b = a[(a>2) & (a<8)]
Abhishek
quelle
5

Zusammenfassung der Antworten

Um zu verstehen, was die beste Antwort ist, können wir mit der anderen Lösung ein Timing durchführen. Leider war die Frage nicht gut gestellt, so dass es Antworten auf verschiedene Fragen gibt. Hier versuche ich, die Antwort auf dieselbe Frage zu verweisen. Angesichts des Arrays:

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

Die Antwort sollten die Indizes der Elemente zwischen einem bestimmten Bereich sein, wir nehmen an, einschließlich, in diesem Fall 6 und 10.

answer = (3, 4, 5)

Entspricht den Werten 6,9,10.

Um die beste Antwort zu testen, können wir diesen Code verwenden.

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)")]',
]

Ergebnisse

Die Ergebnisse sind in der folgenden Darstellung angegeben. Oben die schnellsten Lösungen. Geben Sie hier die Bildbeschreibung ein Wenn Sie anstelle der Indizes die Werte extrahieren möchten, können Sie die Tests mit functions2 durchführen, aber die Ergebnisse sind fast gleich.

GM
quelle
Diese Ergebnisse gelten nur für ein Array mit einer bestimmten Länge (hier haben Sie ein sehr kleines Array ausgewählt). Diese Ergebnisse werden für größere Arrays schnell geändert
EZLearner
4

Dieses Code-Snippet gibt alle Zahlen in einem Numpy-Array zwischen zwei Werten zurück:

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

Es funktioniert wie folgt: (a> 6) gibt ein numpy-Array mit True (1) und False (0) zurück, ebenso (a <10). Durch Multiplizieren dieser beiden Werte erhalten Sie ein Array mit entweder True, wenn beide Anweisungen True (weil 1x1 = 1) oder False (weil 0x0 = 0 und 1x0 = 0) sind.

Der Teil a [...] gibt alle Werte von Array a zurück, wobei das Array in Klammern eine True-Anweisung zurückgibt.

Natürlich können Sie dies komplizierter machen, indem Sie zum Beispiel sagen

...*(1-a<10) 

Dies ähnelt einer "und nicht" -Anweisung.

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

Wollte der Mischung numexpr hinzufügen :

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)

Wäre nur für größere Arrays mit Millionen sinnvoll ... oder wenn Sie an Speichergrenzen stoßen.

k1m190r
quelle
1

Ein anderer Weg ist mit:

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

was zurückgibt:

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

Es ist manchmal nützlich, um Zeitreihen, Vektoren usw. zu maskieren.

Jorge Crvz
quelle
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)

Ausgabe:

(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
quelle
0

Dies ist vielleicht nicht die schönste, funktioniert aber für jede Dimension

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
quelle
-3

Sie können verwenden np.clip(), um das gleiche zu erreichen:

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

Es enthält jedoch Werte kleiner und größer als 6 bzw. 10.

Deba Pratim Saha
quelle