Я реализую некоторый алгоритм поиска с использованием numpy, где один шаг - проверить, находится ли вектор в матрице (в виде строки). Раньше я использовал np.isin, но мне вдруг стало любопытно, будет ли работать ключевое слово in в python. Поэтому я протестировал его и обнаружил, что он работает.
Поскольку я не нашел никакого интерфейса python для in (например, __add__ для + или __abs__ для abs), я считаю, что in жестко встроен в python с использованием стандартной логики итератора, поэтому он должен быть медленнее по сравнению с numpy, предоставленным np.isin. Но после того, как я провел некоторое тестирование, невероятно:
>>> a = np.int8(1)
>>> A = np.zeros(2**24, 'b')
>>> %timeit a in A
>>> %timeit np.isin(a, A)
21.7 ms ± 1.58 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
310 ms ± 20.4 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
который sais np.isin в 10+ раз медленнее, чем python in для небольших типов данных. Я также сделал тест на большой тип данных
>>> a = np.ones(1, 'V256')
>>> A = np.zeros(2**22, 'V256')
>>> %timeit a in A
>>> %timeit np.isin(a, A)
129 ms ± 12.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
10.5 s ± 184 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
который говорит, что np.isin примерно в 100 раз медленнее.
Интересно, в чем может быть причина этого. Обратите внимание, поскольку a=1 и A=[0,0,...], сопоставление должно выполняться для всего массива. На стороне Python не существует такой вещи, как «ранний выход».
РЕДАКТИРОВАТЬ О, на самом деле существует интерфейс Python для in под названием __contains__. Но все же почему np.isin должен быть намного медленнее, чем np.ndarray.__contains__?
«Поскольку я не нашел никакого интерфейса Python для in» - Вы, вероятно, искали не в том месте.
@ user2357112 Я знаю, что это работает только со скаляром. Вы можете заставить его работать, рассматривая строки как скаляр. Поэтому он становится идентичным тестированию in на типе данных VsomeNumber.
isin - это код Python, который вы можете прочитать. Или прочтите весь arraysetops.py. Думаю, этот код написан скорее для удобства, чем для производительности. Массивы не оптимальны для поисковых задач.






Мой ответ не такой, как просили. Может дать вам некоторое представление. Как правило, большая идея для получения хорошей производительности от numpy - это амортизировать стоимость интерпретатора по нескольким элементам за раз. Другими словами, переместите циклы из кода Python (медленно) в циклы C / Fortran где-нибудь в numpy / BLAS / LAPACK / etc. внутренние (быстро). Если вам удастся выполнить эту операцию (называемую векторизацией), производительность обычно будет достаточно хорошей.
Конечно, очевидно, что вы можете получить даже лучшую производительность, отказавшись от интерпретатора Python и используя, скажем, C++. Будет ли этот подход успешным или нет, зависит от того, насколько хорошо вы умеете высокопроизводительное программирование на C++ по сравнению с numpy и какую именно операцию вы пытаетесь выполнить.
«высокопроизводительное программирование» - это концепция, о которой я никогда раньше не слышал.
Более высокая производительность - это машинный код или язык ассемблера (разные представления одного и того же). Вы не ограничены никакими ограничениями, кроме аппаратных возможностей устройства. (IOW, здесь нет инструкции «сделать тост».) После этого, какой язык имеет лучшую производительность, все равно что спросить, какой ручной инструмент имеет лучшую производительность. Сложно распилить дерево молотком или забить гвоздь пилой. Некоторые языки имеют лучшую производительность при выполнении некоторых задач, некоторые - при выполнении других задач.
numpy.ndarray.__contains__ - это просто (elem == arr).any() (даже если это не имеет смысла). Вы можете взглянуть на источник, который очень короткий и простой для подпрограммы NumPy C.
numpy.isin осуществляет вещание по своему левому операнду, и он оптимизирован для повышения эффективности в случае вещания. Для небольшого левого операнда он будет использовать подход, основанный на сортировке, что является излишним для скаляра. В настоящее время у него нет быстрого пути для левого операнда, являющегося скаляром, или для того, чтобы левая сторона была массивом, достаточно маленьким, чтобы сортировка была более дорогостоящей, чем наивный подход.
«Поэтому я протестировал его и обнаружил, что он действительно работает». - Неа.