Какой самый эффективный способ проверить, существует ли значение в массиве NumPy?

У меня очень большой массив NumPy

1 40 3
4 50 4
5 60 7
5 49 6
6 70 8
8 80 9
8 72 1
9 90 7
.... 

Я хочу проверить, существует ли значение в 1-м столбце массива. У меня есть куча доморощенных способов (например, итерации по каждой строке и проверка), но, учитывая размер массива, я бы хотел найти наиболее эффективный метод.

Спасибо!

9 ответов

Решение

Как насчет

if value in my_array[:, col_num]:
    do_whatever

Редактировать: я думаю __contains__ реализован таким образом, что это то же самое, что и версия @detly

Наиболее очевидным для меня будет:

np.any(my_array[:, 0] == value)

Чтобы проверить несколько значений, вы можете использовать numpy.in1d ​​(), который представляет собой поэлементную версию функции ключевого слова python. Если ваши данные отсортированы, вы можете использовать numpy.searchsorted():

import numpy as np
data = np.array([1,4,5,5,6,8,8,9])
values = [2,3,4,6,7]
print np.in1d(values, data)

index = np.searchsorted(data, values)
print data[index] == values

Захватывающий. Мне нужно было повысить скорость серии циклов, которые должны выполнять сопоставление индекса таким же образом. Поэтому я решил примерить здесь все решения вместе с некоторыми риффами.

Вот мои тесты скорости для Python 2.7.10:

import timeit
timeit.timeit('N.any(N.in1d(sids, val))', setup = 'import numpy as N; val = 20010401020091; sids = N.array([20010401010101+x for x in range(1000)])')

+18,86137104034424

timeit.timeit('val in sids', setup = 'import numpy as N; val = 20010401020091; sids = [20010401010101+x for x in range(1000)]')

+15,061666011810303

timeit.timeit('N.in1d(sids, val)', setup = 'import numpy as N; val = 20010401020091; sids = N.array([20010401010101+x for x in range(1000)])')

+11,613027095794678

timeit.timeit('N.any(val == sids)', setup = 'import numpy as N; val = 20010401020091; sids = N.array([20010401010101+x for x in range(1000)])')

+7,670552015304565

timeit.timeit('val in sids', setup = 'import numpy as N; val = 20010401020091; sids = N.array([20010401010101+x for x in range(1000)])')

+5,610057830810547

timeit.timeit('val == sids', setup = 'import numpy as N; val = 20010401020091; sids = N.array([20010401010101+x for x in range(1000)])')

1,6632978916168213

timeit.timeit('val in sids', setup = 'import numpy as N; val = 20010401020091; sids = set([20010401010101+x for x in range(1000)])')

0,0548710823059082

timeit.timeit('val in sids', setup = 'import numpy as N; val = 20010401020091; sids = dict(zip([20010401010101+x for x in range(1000)],[True,]*1000))')

+0,054754018783569336

Очень удивительно! Разница в порядках!

Подводя итог, если вы просто хотите узнать, есть ли что-то в одномерном списке или нет:

  • 19s N.any(N.in1d ​​(массив numpy))
  • 15 с х в (список)
  • 8s N.any (x == массив numpy)
  • 6s x in (массив numpy)
  • .1s x in (набор или словарь)

Если вы хотите знать, где что-то есть в списке (порядок важен):

  • 12s N.in1d ​​(x, массив numpy)
  • 2s x == (массив numpy)

Добавление ответа @HYRY in1d кажется самым быстрым для numpy. Это использует numpy 1.8 и python 2.7.6.

В этом тесте in1d был самым быстрым:

a = arange(0,99999,3)
%timeit 10 in a
%timeit in1d(a, 10)

10000 loops, best of 3: 150 µs per loop
10000 loops, best of 3: 61.9 µs per loop

Использование набора Python кажется самым быстрым:

s = set(range(0, 99999, 3))
%timeit 10 in s

10000000 loops, best of 3: 47 ns per loop

Я рекомендую использовать np.isin.
Руководство предлагает эту функцию для маскировки значений, но вы можете просто вызватьanyилиallпо этим маскам самостоятельно проверить членство. Рекомендую проверить скорость с помощьюtimeitкак предложено выше.
Не используйте циклы for, это в первую очередь противоречит идее использования numpy.
Вы можете либо проверить, находится ли какой-либо элемент списка в массиве, поместив массив первым, либо поместить массив вторым, чтобы проверить, охватывает ли он все элементы списка.

      import numpy
a = np.arange(9).reshape((3,3))
any_lookup = [2,6,10,10002,34543,45]
all_lookup = [2,3,4,5]
none_lookup = [-10,435344,-255,557755]

res_any = np.isin(a,any_lookup)
res_all = np.isin(a,all_lookup)
res_none = np.isin(a,none_lookup)

print(res_any)
print(res_all)
print(res_none)

print(res_any.any())
print(res_all.any())
print(res_none.any())

print(np.isin(any_lookup,a).all())
print(np.isin(all_lookup,a).all())

Полученные результаты:

      [[False False  True]
 [False False False]
 [ True False False]]
[[False False  True]
 [ True  True  True]
 [False False False]]
[[False False False]
 [False False False]
 [False False False]]
True
True
False
False
True

Если вы ищете список целых чисел, вы можете использовать индексацию для выполнения работы. Это также работает с nd-массивами, но кажется медленнее. Возможно, будет лучше, если вы сделаете это более одного раза.

      def valuesInArray(values, array):
    values = np.asanyarray(values)
    array = np.asanyarray(array)
    assert array.dtype == np.int and values.dtype == np.int
    
    matches = np.zeros(array.max()+1, dtype=np.bool_)
    matches[values] = True
    
    res = matches[array]
    
    return np.any(res), res
    
    
array = np.random.randint(0, 1000, (10000,3))
values = np.array((1,6,23,543,222))

matched, matches = valuesInArray(values, array)

Используя numba и njit, я мог получить ускорение на ~x10.

На мой взгляд, наиболее удобный способ:

(Val in X[:, col_num])

где Val - значение, которое вы хотите проверить, а X - массив. В вашем примере предположим, что вы хотите проверить, существует ли значение 8 в третьем столбце. Просто пиши

(8 in X[:, 2])

Это вернет True, если 8 находится в третьем столбце, иначе False.

Если вы хотите проверить, есть ли список a находится в массиве NumPy b затем используйте следующий синтаксис:

np.any(np.equal(a, b).all(axis=1))

Ввод axis = 1 учитывая, что массив NumPy имеет форму n*2

Другие вопросы по тегам