У меня есть два массива, которые определяют оси X и Y сетки. Например:
x = numpy.array([1,2,3])
y = numpy.array([4,5])
Я хотел бы сгенерировать декартово произведение этих массивов для генерации:
array([[1,4],[2,4],[3,4],[1,5],[2,5],[3,5]])
В некотором смысле это не очень неэффективно, поскольку мне нужно делать это много раз в цикле. Я предполагаю, что преобразование их в список Python и использование itertools.product
массива и обратно в массив не является наиболее эффективной формой.
python
numpy
cartesian-product
Богатый
источник
источник
Ответы:
См. Использование numpy для создания массива всех комбинаций из двух массивов для общего решения для вычисления декартового произведения N массивов.
источник
meshgrid
+dstack
Подход, а быстрее в некоторых случаях может привести к ошибкам , если вы ожидаете , что декартово произведение будет построен в том же порядке , для массивов одного и того же размера.meshgrid
+dstack
. Не могли бы вы опубликовать пример?Канонический
cartesian_product
(почти)Есть много подходов к этой проблеме с различными свойствами. Некоторые из них быстрее, чем другие, а некоторые более общего назначения. После долгих испытаний и настроек я обнаружил, что следующая функция, которая вычисляет n-мерную
cartesian_product
, быстрее для большинства входных данных быстрее, чем большинство других. Для пары подходов, которые немного сложнее, но во многих случаях даже немного быстрее, см. Ответ Пола Панцера .Учитывая этот ответ, это больше не самая быстрая реализация декартового произведения,
numpy
о которой я знаю. Тем не менее, я думаю, что его простота будет продолжать делать его полезным ориентиром для будущих улучшений:Стоит отметить, что эта функция используется
ix_
необычным образом; в то время как документированное использованиеix_
состоит в том, чтобы генерировать индексы в массиве, просто так получается, что массивы с одинаковой формой могут использоваться для широковещательного присваивания. Большое спасибо mgilson , который вдохновил меня попробовать использоватьix_
этот способ, и unutbu , который предоставил несколько чрезвычайно полезных отзывов об этом ответе, включая предложение использоватьnumpy.result_type
.Известные альтернативы
Иногда быстрее записать непрерывные блоки памяти в порядке Фортрана. Это основа этой альтернативы,
cartesian_product_transpose
которая на некоторых аппаратных средствах оказалась быстрее, чемcartesian_product
(см. Ниже). Однако ответ Пола Панцера, использующий тот же принцип, еще быстрее. Тем не менее, я включаю это здесь для заинтересованных читателей:Поняв подход Panzer, я написал новую версию, которая почти такая же быстрая, как и его, и такая же простая, как
cartesian_product
:Похоже, что это приводит к некоторым накладным расходам в постоянном времени, что делает его медленным, чем Panzer, для небольших входов. Но для больших входных данных во всех тестах, которые я выполнял, он работает так же хорошо, как и его самая быстрая реализация (
cartesian_product_transpose_pp
).В следующих разделах я включаю некоторые тесты других альтернатив. Теперь они несколько устарели, но вместо дублирующих усилий я решил оставить их здесь вне исторического интереса. Актуальные тесты см. В ответе Panzer, а также в ответе Нико Шлёмера .
Тесты против альтернатив
Вот набор тестов, которые показывают повышение производительности, которое предоставляют некоторые из этих функций относительно ряда альтернатив. Все тесты, показанные здесь, были выполнены на четырехъядерном компьютере под управлением Mac OS 10.12.5, Python 3.6.1 и
numpy
1.12.1. Известно, что различия в аппаратном и программном обеспечении дают разные результаты, поэтому YMMV. Запустите эти тесты для себя, чтобы быть уверенным!Определения:
Результаты теста:
Во всех случаях,
cartesian_product
как определено в начале, этот ответ является самым быстрым.Для тех функций, которые принимают произвольное количество входных массивов, также стоит проверить производительность
len(arrays) > 2
. (Пока я не могу определить, почемуcartesian_product_recursive
выдает ошибку в этом случае, я удалил ее из этих тестов.)Как показывают эти тесты,
cartesian_product
остается конкурентоспособным, пока число входных массивов не превысит (примерно) четыре. После этого,cartesian_product_transpose
имеет небольшое преимущество.Стоит повторить, что пользователи с другим оборудованием и операционными системами могут видеть разные результаты. Например, unutbu сообщает о следующих результатах этих тестов с использованием Ubuntu 14.04, Python 3.4.3 и
numpy
1.14.0.dev0 + b7050a9:Ниже я подробно расскажу о предыдущих тестах, которые я проводил в этом направлении. Относительная производительность этих подходов со временем менялась для разных аппаратных средств и разных версий Python и
numpy
. Хотя это не сразу полезно для людей, использующих новейшие версииnumpy
, оно иллюстрирует, как все изменилось с первой версии этого ответа.Простая альтернатива:
meshgrid
+dstack
В настоящее время принятый ответ использует
tile
иrepeat
для трансляции двух массивов вместе. Ноmeshgrid
функция делает практически то же самое. Вот выводtile
иrepeat
перед передачей для транспонирования:И вот вывод
meshgrid
:Как видите, он практически идентичен. Нам нужно только изменить результат, чтобы получить точно такой же результат.
Вместо того,
meshgrid
чтобыdstack
изменить форму на этом этапе, мы могли бы передать выходные данные to и изменить форму впоследствии, что экономит некоторую работу:Вопреки утверждению в этом комментарии , я не видел никаких доказательств того, что разные входные данные будут давать выходные данные различной формы, и, как показано выше, они делают очень похожие вещи, поэтому было бы довольно странно, если бы они это сделали. Пожалуйста, дайте мне знать, если вы найдете контрпример.
Тестирование
meshgrid
+dstack
противrepeat
+transpose
Относительная эффективность этих двух подходов со временем изменилась. В более ранней версии Python (2.7) результат использования
meshgrid
+dstack
был заметно быстрее для небольших входных данных. (Обратите внимание, что эти тесты взяты из старой версии этого ответа.) Определения:Для ввода среднего размера я увидел значительное ускорение. Но я повторил эти тесты с более новыми версиями Python (3.6.1) и
numpy
(1.12.1) на более новой машине. Два подхода сейчас практически идентичны.Старый тест
Новый тест
Как всегда, YMMV, но это говорит о том, что в последних версиях Python и numpy они взаимозаменяемы.
Обобщенные функции продукта
В целом, мы можем ожидать, что использование встроенных функций будет быстрее для небольших входов, в то время как для больших входов встроенная функция может быть быстрее. Кроме того, для обобщенного n-мерного продукта,
tile
иrepeat
не поможет, потому что они не имеют четких аналогов многомерных. Так что стоит исследовать поведение специализированных функций.Большинство соответствующих тестов появятся в начале этого ответа, но вот несколько тестов, выполненных на более ранних версиях Python и
numpy
для сравнения.cartesian
Функция , определенная в другом ответе используется для выполнения довольно хорошо для больших входов. (Это то же самое, что и функция называетсяcartesian_product_recursive
выше.) Для того , чтобы сравнитьcartesian
сdstack_prodct
, мы используем только два измерения.Здесь опять старый тест показал значительную разницу, в то время как новый тест почти ничего не показал.
Старый тест
Новый тест
Как и прежде,
dstack_product
все еще бьетcartesian
в меньших масштабах.Новый тест ( старый тест не показан )
Эти различия, я думаю, интересны и заслуживают записи; но они академичны в конце концов. Как показали тесты в начале этого ответа, все эти версии почти всегда работают медленнее, чем
cartesian_product
определено в самом начале этого ответа, что само по себе немного медленнее, чем самые быстрые реализации среди ответов на этот вопрос.источник
dtype=object
вarr = np.empty( )
позволит использовать различные типы в продукте, напримерarrays = [np.array([1,2,3]), ['str1', 'str2']]
.cartesian_product_tranpose
быстрее, чем вcartesian_product
зависимости от ОС своего компьютера, Python или NumPy версии. Например, в Ubuntu 14.04, python3.4.3, numpy 1.14.0.dev0 + b7050a9,%timeit cartesian_product_transpose(x500,y500)
выдает, в1000 loops, best of 3: 682 µs per loop
то время как%timeit cartesian_product(x500,y500)
выдает1000 loops, best of 3: 1.55 ms per loop
. Я также нахожу,cartesian_product_transpose
может быть, быстрее, когдаlen(arrays) > 2
.cartesian_product
возвращает массив dtype с плавающей точкой, аcartesian_product_transpose
возвращает массив того же dtype, что и первый (широковещательный) массив. Возможность сохранения dtype при работе с целочисленными массивами может быть причиной для пользователейcartesian_product_transpose
.dtype = np.find_common_type([arr.dtype for arr in arrays], [])
можно использовать, чтобы найти общий dtype всех массивов, вместо того, чтобы заставлять пользователя сначала размещать массив, который управляет dtype.Вы можете просто сделать обычное понимание списка в Python
который должен дать вам
источник
Я также заинтересовался этим и провел небольшое сравнение производительности, возможно, несколько понятнее, чем в ответе @ senderle.
Для двух массивов (классический случай):
Для четырех массивов:
(Обратите внимание, что длина массивов здесь составляет всего несколько десятков записей.)
Код для воспроизведения сюжетов:
источник
Основываясь на образцовой фундаментальной работе @ senderle, я придумал две версии - одну для C и одну для разметки Fortran - которые часто бывают немного быстрее.
cartesian_product_transpose_pp
это - в отличие от @ senderle's,cartesian_product_transpose
который использует совсем другую стратегию - версияcartesion_product
которой используется более выгодная схема транспонирования памяти + некоторые очень незначительные оптимизации.cartesian_product_pp
придерживается оригинального расположения памяти. Что делает его быстрым, так это использование непрерывного копирования. Непрерывные копии оказываются намного быстрее, чем копирование полного блока памяти, хотя только часть его содержит действительные данные, предпочтительнее, чем копирование действительных битов.Несколько перфлотов. Я сделал отдельные для макетов C и Fortran, потому что это разные задачи IMO.
Имена, оканчивающиеся на 'pp' - мои подходы.
1) много крошечных факторов (2 элемента каждый)
2) много мелких факторов (4 элемента каждый)
3) три фактора равной длины
4) два фактора равной длины
Код (нужно делать отдельные прогоны для каждого сюжета, потому что я не могу понять, как выполнить сброс; также нужно правильно редактировать / комментировать / выводить):
источник
arrays
в cartesian_product_transpose_pp (массивы) превышает определенный размер,MemoryError
произойдет. В этой ситуации я хотел бы, чтобы эта функция давала меньшие фрагменты результатов. Я разместил вопрос по этому вопросу. Можете ли вы ответить на мой вопрос? Спасибо.По состоянию на октябрь 2017 года numpy теперь имеет обобщенную
np.stack
функцию, которая принимает параметр оси. Используя его, мы можем получить «обобщенное декартово произведение», используя технику «dstack and meshgrid»:Обратите внимание на
axis=-1
параметр. Это последняя (самая внутренняя) ось в результате. Это эквивалентно использованиюaxis=ndim
.Еще один комментарий: поскольку декартовы произведения взрываются очень быстро, если нам не нужно по какой-то причине реализовать массив в памяти, если продукт очень большой, мы можем захотеть использовать
itertools
и использовать значения на лету.источник
Некоторое время я использовал ответ @kennytm , но при попытке сделать то же самое в TensorFlow, но обнаружил, что TensorFlow не имеет эквивалента
numpy.repeat()
. После небольшого эксперимента, я думаю, я нашел более общее решение для произвольных векторов точек.Для NumPy:
и для TensorFlow:
источник
Пакет Scikit-learn имеет быструю реализацию именно этого:
Обратите внимание, что соглашение этой реализации отличается от того, что вы хотите, если вы заботитесь о порядке вывода. Для вашего точного заказа вы можете сделать
источник
В более общем случае, если у вас есть два двумерных числовых массива a и b, и вы хотите объединить каждую строку a с каждой строкой b (декартово произведение строк, вроде соединения в базе данных), вы можете использовать этот метод :
источник
Самое быстрое, что вы можете получить, это либо объединить выражение генератора с функцией map:
Выходы (фактически весь итоговый список печатается):
или с помощью выражения двойного генератора:
Выходы (весь список напечатан):
Учтите, что большая часть времени вычислений уходит на команду печати. Расчеты генератора в остальном прилично эффективны. Без печати время расчета составляет:
для выражения генератора + функция карты и:
для выражения двойного генератора.
Если на самом деле вы хотите вычислить фактическое произведение каждой из пар координат, самое быстрое - это решить его как произведение матрицы на пустые места:
Выходы:
и без печати (в данном случае это не сильно экономит, так как на самом деле распечатывается только крошечный кусочек матрицы):
источник
foo = a[:,None]*b
быстрее. Используя ваш метод синхронизации безprint(foo)
, это 0,001103 с против 0,002225 с. Используя timeit, оно составляет 304 мкс против 1,6 мс. Известно, что матрица работает медленнее, чем ndarray, поэтому я попробовал ваш код с помощью np.array, но он все еще медленнее (1,57 мс), чем трансляция.Это также легко сделать с помощью метода itertools.product.
Результат: массив ([
[1, 4],
[1, 5],
[2, 4],
[2, 5],
[3, 4],
[3, 5]], dtype = int32)
Время выполнения: 0,000155 с
источник
В конкретном случае, когда вам нужно выполнить простые операции, такие как сложение для каждой пары, вы можете ввести дополнительное измерение и позволить вещанию делать свою работу:
Я не уверен, есть ли подобный способ на самом деле получить сами пары.
источник
dtype
этоfloat
вы можете сделать ,(a[:, None, None] + 1j * b[None, :, None]).view(float)
что на удивление быстро.