"Выполнение отображения (mapping) массива NumPy на месте"
Возможна ли карта NumPy массива на месте? Если да, то как?
У меня есть 2D массив a_values
, и сейчас я использую следующий код, чтобы выполнить преобразование элементов:
for row in range(len(a_values)):
for col in range(len(a_values[0])):
a_values[row][col] = dim(a_values[row][col])
Однако это выглядит довольно неаккуратно, и у меня есть подозрение, что в NumPy должна быть функция, которая позволяет сделать то же самое, что-то вроде:
a_values.map_in_place(dim)
Но если что-то подобное существует, я пока не смог его найти.
3 ответ(ов)
Вопрос: Возможно ли выполнить отображение массива NumPy на месте?
Ответ: Да, но не с помощью одного метода массива. Вам нужно будет написать собственный код.
Ниже представлен скрипт, который сравнивает различные реализации, обсуждаемые в теме:
import timeit
from numpy import array, arange, vectorize, rint
# ПОДГОТОВКА
get_array = lambda side: arange(side**2).reshape(side, side) * 30
dim = lambda x: int(round(x * 0.67328))
# ТАЙМЕР
def best(fname, reps, side):
global a
a = get_array(side)
t = timeit.Timer('%s(a)' % fname, setup='from __main__ import %s, a' % fname)
return min(t.repeat(reps, 3)) # малое число, так как на месте --> сходимость к 1
# ФУНКЦИИ
def mac(array_):
for row in range(len(array_)):
for col in range(len(array_[0])):
array_[row][col] = dim(array_[row][col])
def mac_two(array_):
li = range(len(array_[0]))
for row in range(len(array_)):
for col in li:
array_[row][col] = int(round(array_[row][col] * 0.67328))
def mac_three(array_):
for i, row in enumerate(array_):
array_[i][:] = [int(round(v * 0.67328)) for v in row]
def senderle(array_):
array_ = array_.reshape(-1)
for i, v in enumerate(array_):
array_[i] = dim(v)
def eryksun(array_):
array_[:] = vectorize(dim)(array_)
def ufunc_ed(array_):
multiplied = array_ * 0.67328
array_[:] = rint(multiplied)
# ОСНОВНОЕ
r = []
for fname in ('mac', 'mac_two', 'mac_three', 'senderle', 'eryksun', 'ufunc_ed'):
print('\nТестируем `%s`...' % fname)
r.append(best(fname, reps=50, side=50))
# Следующий код необходим для визуальной проверки, что функции возвращают одинаковые результаты
tmp = get_array(3)
eval('%s(tmp)' % fname)
print(tmp)
tmp = min(r) / 100
print('\n===== ...И ПОБЕДИТЕЛЬ... =========================')
print(' mac (как в вопросе) : %.4fms [%.0f%%]' % (r[0] * 1000, r[0] / tmp))
print(' mac (оптимизированный) : %.4fms [%.0f%%]' % (r[1] * 1000, r[1] / tmp))
print(' mac (срез-присвоение) : %.4fms [%.0f%%]' % (r[2] * 1000, r[2] / tmp))
print(' senderle : %.4fms [%.0f%%]' % (r[3] * 1000, r[3] / tmp))
print(' eryksun : %.4fms [%.0f%%]' % (r[4] * 1000, r[4] / tmp))
print(' срез-присвоение с ufunc : %.4fms [%.0f%%]' % (r[5] * 1000, r[5] / tmp))
print('=======================================================\n')
Вывод приведенного выше скрипта - по крайней мере, на моей системе - следующий:
mac (как в вопросе) : 88.7411ms [74591%]
mac (оптимизированный) : 86.4639ms [72677%]
mac (срез-присвоение) : 79.8671ms [67132%]
senderle : 85.4590ms [71832%]
eryksun : 13.8662ms [11655%]
срез-присвоение с ufunc : 0.1190ms [100%]
Как вы можете заметить, использование ufunc
в NumPy увеличивает скорость более чем на 2 и почти на 3 порядка по сравнению с вторым лучшим и худшим альтернативами соответственно.
Если использование ufunc
невозможно, вот сравнение других альтернатив:
mac (как в вопросе) : 91.5761ms [672%]
mac (оптимизированный) : 88.9449ms [653%]
mac (срез-присвоение) : 80.1032ms [588%]
senderle : 86.3919ms [634%]
eryksun : 13.6259ms [100%]
Надеюсь, это поможет!
Использование реализации на numpy
с применением трюка out_
может быть привлекательным, но как показывают ваши результаты, есть более быстрые способы достижения аналогичных результатов. Время выполнения функции fmilo
, которая, судя по всему, оптимизирована для вашей задачи, составляет всего 0.0620 мс, что гораздо быстрее, чем другие методы.
Давайте проанализируем:
Использование
np_round
сout=
— это действительно хороший способ избежать создания дополнительных массивов, так как вы перенаправляете результат непосредственно в входной массив. Однако использование этого подхода не всегда ведет к максимальной производительности, как показывает ваш тест.Оптимизация
fmilo
— видно, что данная функция значительно быстрее, чем другие варианты. Это может быть связано с тем, что в ней используется меньше накладных расходов, связанных с обработкой массивов и вызовом функций.Сравнение с другими методами — в вашем тесте функции с использованием срезов и векторизации показывают значительно худшие результаты, что указывает на то, что более прямолинейные реализации могут иметь преимущества в скорости.
Таким образом, хотя использование numpy
и трюков с out_
может быть оправдано в некоторых случаях, оптимизация конкретной функции, как fmilo
, может дать существенно лучшие результаты. Выбор подхода должен основываться на потребностях вашего приложения и требованиях к производительности.
Если использование ufunc не представляется возможным, возможно, стоит рассмотреть вариант с Cython. Он легко интегрируется и может привести к значительному ускорению в конкретных задачах, связанных с массивами NumPy.
Как вывести полный массив NumPy без обрезки?
Как получить доступ к i-му столбцу многомерного массива NumPy?
Преобразование DataFrame Pandas в массив NumPy
Как подсчитать количество вхождений определенного элемента в ndarray?
Индексация массива numpy с помощью списка кортежей