Numpy: знайти індекс елементів у межах діапазону


85

У мене є масив чисел Numpy, наприклад,

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

Я хотів би знайти всі індекси елементів у певному діапазоні. Наприклад, якщо діапазон (6, 10), відповідь повинна бути (3, 4, 5). Чи є для цього вбудована функція?

Відповіді:


140

Ви можете використовувати np.whereдля отримання індексів та np.logical_andвстановлення двох умов:

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
До речі, того ж досягається np.nonzero(np.logical_and(a>=6, a<=10)).
3lectrologos

10
Також np.where((a > 6) & (a <= 10))
Елінда

здається, не вдається з багатовимірними масивами
Моніка Хеднек,

1
@ELinda np.logical_and- це трохи швидше, ніж &хоча. І np.whereшвидше ніж np.nonzero.
Skillmon любить topanswers.xyz

Він має дуже погану продуктивність для більших масивів
EZLearner

62

Як у відповіді @ deinonychusaur, але ще більш компактним:

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

19
Приємно. Ви також можете зробити, a[(a >= 6) & (a <= 10)]якщо aце масив numpy.
ws_e_c421

1
Про всяк випадок, якщо хтось заплутається, як я з формулюванням коментаря: це не працює для звичайних списків, це лише якщо aце масив numpy
Prof

14

Я думав, що додам це, тому що aу наведеному вами прикладі сортується:

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

Короткий зміст відповідей

Щоб зрозуміти, що є найкращою відповіддю, ми можемо визначити час, використовуючи інше рішення. На жаль, питання було неправильно поставлене, тому є відповіді на різні запитання, тут я намагаюся вказати відповідь на те саме питання. Враховуючи масив:

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

Відповіддю повинні бути індекси елементів між певним діапазоном, ми припускаємо включно, в даному випадку 6 і 10.

answer = (3, 4, 5)

Відповідні значення 6,9,10.

Щоб перевірити найкращу відповідь, ми можемо використовувати цей код.

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

Результати

Результати представлені в наступному графіку. Зверху - найшвидші рішення. введіть тут опис зображення Якщо замість індексів ви хочете витягти значення, ви можете виконати тести, використовуючи functions2, але результати майже однакові.


Ці результати застосовуються лише до масиву конкретної довжини (тут ви вибрали дуже малий масив). Ці результати швидко змінюються для більших масивів
EZLearner

4

Цей фрагмент коду повертає всі числа в масиві numpy між двома значеннями:

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

Він працює наступним чином: (a> 6) повертає масив numpy з True (1) і False (0), так само робить (a <10). Помноживши ці два разом, ви отримаєте масив або з True, якщо обидва твердження є True (тому що 1x1 = 1) або False (оскільки 0x0 = 0 і 1x0 = 0).

Частина a [...] повертає всі значення масиву a, де масив між дужками повертає оператор True.

Звичайно, ви можете ускладнити це, сказавши, наприклад

...*(1-a<10) 

що схоже на твердження "та ні".



1

Хотіли додати numexpr до суміші:

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)

Це має сенс лише для більших масивів з мільйонами ... або якщо ви досягаєте обмежень пам'яті.


1

Інший спосіб - це:

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

який повертає:

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

Це іноді корисно для маскування часових рядів, векторів тощо.


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)

Вихід:

(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

Це може бути не найкрасивіше, але працює для будь-якого виміру

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

Ви можете використовувати np.clip()те ж саме:

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

Однак він має значення менше і більше, ніж 6 і 10 відповідно.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.