У меня есть такой массив: [1 2 2 0 0 1 3 5]
Можно ли получить индекс элементов в виде 2d массива? Например, ответ на приведенный выше ввод будет[[3 4], [0 5], [1 2], [6], [], [7]]
В настоящее время я должен зациклить различные значения и вызывать numpy.where(input == i)
для каждого значения, которое имеет ужасную производительность с достаточно большим вводом.
python
numpy
numpy-ndarray
Фредерико Шардонг
источник
источник
np.argsort([1, 2, 2, 0, 0, 1, 3, 5])
даетarray([3, 4, 0, 5, 1, 2, 6, 7], dtype=int64)
. тогда вы можете просто сравнить следующие элементы.Ответы:
Вот подход O (max (x) + len (x)) с использованием
scipy.sparse
:Это работает путем создания разреженной матрицы с записями в позициях (x [0], 0), (x [1], 1), ... Используя
CSC
формат (сжатый разреженный столбец), это довольно просто. Затем матрица преобразуется вLIL
(связанный список) формат. Этот формат хранит индексы столбцов для каждой строки в виде списка в своемrows
атрибуте, поэтому все, что нам нужно сделать, это взять и преобразовать его в список.Обратите внимание, что для небольших массивов
argsort
решения, вероятно, быстрее, но при некоторых не слишком больших размерах это будет пересекаться.РЕДАКТИРОВАТЬ:
argsort
толькоnumpy
решение:Если порядок индексов внутри групп не имеет значения, вы также можете попробовать
argpartition
(это не имеет значения в этом небольшом примере, но это не гарантируется в целом):РЕДАКТИРОВАТЬ:
@Divakar рекомендует против использования
np.split
. Вместо этого цикл, вероятно, быстрее:Или вы можете использовать совершенно новый (Python3.8 +) оператор моржа:
EDIT (отредактированный):
(Не чисто NumPy): В качестве альтернативы Numba (см. Пост @ Senderle) мы также можем использовать Pythran.
Компилировать с
pythran -O3 <filename.py>
Здесь
numba
выигрывает усиком по производительности:Старые вещи:
Таймс против Нумба (старый)
источник
np.split
.Один из возможных вариантов, в зависимости от размера ваших данных, это просто отказаться от
numpy
использования и использоватьcollections.defaultdict
:Тогда вы получите словарь
{value1: [index1, index2, ...], value2: [index3, index4, ...]}
. Масштабирование времени довольно близко к линейному с размером массива, поэтому 10 000 000 занимает ~ 2,7 с на моей машине, что кажется достаточно разумным.источник
Хотя запрос на
numpy
решение, я решил посмотреть, есть ли интересноеnumba
решение. И действительно, есть! Вот подход, который представляет разделенный список как рваный массив, хранящийся в одном предварительно выделенном буфере. Это черпает вдохновение изargsort
подхода, предложенного Полом Панцером . (Для более старой версии, которая не так хорошо, но была проще, см. Ниже.)Это обрабатывает список из десяти миллионов элементов за 75 мс, что почти в 50 раз быстрее по сравнению со списочной версией, написанной на чистом Python.
Для более медленной, но несколько более читаемой версии, вот что я имел раньше, основываясь на недавно добавленной экспериментальной поддержке «типизированных списков» с динамическим размером, которые позволяют нам гораздо быстрее заполнять каждую ячейку не по порядку.
Это
numba
немного борется с механизмом вывода типа, и я уверен, что есть лучший способ справиться с этой частью. Это также оказывается почти в 10 раз медленнее, чем выше.Я проверил это в отношении следующего:
Я также проверил их на предварительно скомпилированной версии Cython, аналогичной
enum_bins_numba_buffer
(подробно описанной ниже).В списке из десяти миллионов случайных чисел (
ints = np.random.randint(0, 100, 10000000)
) я получаю следующие результаты:Впечатляет, что этот способ работы с
numba
опережаетcython
версию той же функции, даже с отключенной проверкой границ. У меня пока нет достаточных знаний,pythran
чтобы протестировать этот подход, используя его, но мне было бы интересно увидеть сравнение. Вероятно, исходя из этого ускорения,pythran
версия также может быть немного быстрее при таком подходе.Вот
cython
версия для справки, с некоторыми инструкциями по сборке. Послеcython
установки вам понадобится простойsetup.py
файл, подобный следующему:И модуль Cython
enum_bins_cython.pyx
:С этими двумя файлами в вашем рабочем каталоге выполните эту команду:
Затем вы можете импортировать функцию, используя
from enum_bins_cython import enum_bins_cython
.источник
Вот действительно очень странный способ сделать это, это ужасно, но я нашел это слишком смешным, чтобы не делиться - и все
numpy
!РЕДАКТИРОВАТЬ: это лучший метод, который я мог найти на этом пути. Это все еще в 10 раз медленнее, чем решение @PaulPanzer
argsort
:источник
Вы можете сделать это, составив словарь чисел, ключи - это числа, а значения - это индексы, которые видели числа, это один из самых быстрых способов сделать это, вы можете увидеть код ниже:
источник
псевдокод:
получите «количество 1d массивов в 2d массиве», вычитая минимальное значение вашего массива numpy из максимального значения, а затем плюс один. В вашем случае это будет 5-0 + 1 = 6
инициализировать 2d массив с количеством 1d массивов в нем. В вашем случае инициализируйте 2d массив с 6 1d массивом в нем. Каждый 1d массив соответствует уникальному элементу в вашем массиве numpy, например, первый 1d массив будет соответствовать '0', второй 1d массив будет соответствовать '1', ...
переберите ваш массивный массив, поместите индекс элемента в соответствующий соответствующий 1d массив. В вашем случае индекс первого элемента в вашем массиве numpy будет помещен во второй массив 1d, индекс второго элемента в вашем массиве numpy будет помещен в третий массив 1d, ....
Этот псевдокод будет работать линейно, так как он зависит от длины вашего массива.
источник
Это дает вам именно то, что вы хотите, и заняло бы около 2,5 секунд для 10 000 000 на моей машине:
источник
Итак, учитывая список элементов, вы хотите составить (элемент, индекс) пары. В линейное время это можно сделать так:
Это должно занять O (N) время. Я не могу придумать более быстрого решения на данный момент, но обновлю здесь, если я сделаю.
источник