Выбрать элементы в одном массиве на основе элементов другого массива? - PullRequest
1 голос
/ 17 марта 2020

У меня есть 2 numpy массивов: массив 1 имеет элементы 1 ... 100, обозначающие диапазоны для проверки (наименее значимый di git опущен), массив 2 имеет значения 1 ... 1000 для проверки каждого из них диапазоны.

import numpy
o = numpy.array([3, 7, 20, 47, 60, 72, 76, 83, 94, 94])
p = numpy.array([22, 54, 77, 83, 246, 285, 813, 828, 950, 998])

Цель состоит в том, чтобы получить индексы элементов в массиве 2, которые находятся в любом диапазоне массива 1. Например, для 22 нам нужно проверить:

30 < 22 < 40 => false
70 < 22 < 80 => false
etc. (all are false, so index 0 will not be in the result)

и 77 будут истинными, поскольку они лежат в исключительном диапазоне <70;80>

Каков правильный синтаксис?

In [238]: o
Out[238]: array([ 3,  7, 20, 47, 60, 72, 76, 83, 94, 94])

In [239]: p
Out[239]: array([ 22,  54,  77,  83, 246, 285, 813, 828, 950, 998])

In [240]: p[o*10 < p < (o+1)*10 ]

 ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()

Кажется, это работает, но очень сложно: (абсолютное вычитание с основанием. Верхние значения с сравниваемым значением должны равняться 10)

например, abs (77 -60) + абс (77-70) == 10

In [329]: oo = numpy.vstack(((o*10), (o+1)*10 ) )

In [324]: ten = numpy.abs(numpy.subtract(oo[0,:],p.reshape(-1,1))) + numpy.abs(numpy.subtract(oo[1,:],p.reshape(-1,1)))

array([[  26,  106,  366,  906, 1166, 1406, 1486, 1626, 1846, 1846],
       [  38,   42,  302,  842, 1102, 1342, 1422, 1562, 1782, 1782],
       [  84,   10,  256,  796, 1056, 1296, 1376, 1516, 1736, 1736],
       [  96,   16,  244,  784, 1044, 1284, 1364, 1504, 1724, 1724],
       [ 422,  342,   82,  458,  718,  958, 1038, 1178, 1398, 1398],
       [ 500,  420,  160,  380,  640,  880,  960, 1100, 1320, 1320],
       [1556, 1476, 1216,  676,  416,  176,   96,   44,  264,  264],
       [1586, 1506, 1246,  706,  446,  206,  126,   14,  234,  234],
       [1830, 1750, 1490,  950,  690,  450,  370,  230,   10,   10],
       [1926, 1846, 1586, 1046,  786,  546,  466,  326,  106,  106]])

In [330]: numpy.where(ten == 10)
Out[330]: (array([2, 8, 8]), array([1, 8, 9]))

In [331]: p[ numpy.where(ten == 10)[0] ]
Out[331]: array([ 77, 950, 950])

Ответы [ 2 ]

0 голосов
/ 31 марта 2020

tl; dr

Используйте any и map для выполнения реальной работы, а не where, который делает некоторые волхвы c, чтобы определить, что итерировать, и завершается неудачно, когда есть более одного итерируемого в вашем выражении.

>>> numpy.where( map( lambda pval : numpy.any( (o*10 < pval) & (pval < (o+1)*10) ), p) )
(array([2]),)

длинный ответ

Сравнение массива дает массив результатов. На данный момент, примените только одну проверку диапазона:

>>> o*10 < 77
array([ True,  True, False, False, False, False, False, False, False, False], dtype=bool)

@ ypnos правильно отмечает, как это можно объединить с другим сравнением:

>>> (o*10 < 77) & (77 < (o+1)*10)
array([False,  True, False, False, False, False, False, False, False, False], dtype=bool)

Это выполняет операцию И на каждом элементе списков попарно (и, как вы можете видеть, 77 лежит в диапазоне второго элемента o, 7, который обозначает 70 < 77 < 80. Однако нас не интересует, какое сравнение, только то, что один из них был успешным Итак, давайте свернем результат с помощью any():

>>> numpy.any( (o*10 < 77) & (77 < (o+1)*10) )
True
>>> numpy.any( (o*10 < 22) & (22 < (o+1)*10) )
False

Задача состоит в том, как применить эту операцию к каждому элементу p, а не только к проверке 22, как описано выше. Документация для numpy .where не объясняет, как массив выбирается из заданного условия, но даже оборачивает наше существующее условие в функцию и просто вызывает его как условие для одной переменной по-прежнему не работает:

>>> def foo(x):
...     return numpy.any( (o*10 < x) & (x < (o+1)*10) )
...
>>> numpy.where( foo(p) )
(array([], dtype=int64),)

В этом случае нам действительно нужно map, а не where, которое принимает предикат и итерацию и применяет предикат (булева функция) к итерируемому (numpy массиву результата наших предыдущих сравнений и любого вызова ()). В результате получается:

>>> map(foo,p)
[False, False, True, False, False, False, False, False, False, False]

Или напрямую, используя лямбду вместо функции foo:

>>> map( lambda pval : numpy.any( (o*10 < pval) & (pval < (o+1)*10) ), p)
[False, False, True, False, False, False, False, False, False, False]

Затем вам нужны индексы, поэтому примените, где к этому:

>>> numpy.where( map( lambda pval : numpy.any( (o*10 < pval) & (pval < (o+1)*10) ), p) )
(array([2]),)
0 голосов
/ 17 марта 2020

Вы можете использовать пересечение двух масок следующим образом. Однако в вашем примере их пересечение кажется пустым:

x = p[(o*10 < p) & (p < (o+1)*10)]
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...