Самый эффективный способ обратить вспять массив


276

Хотите верьте, хотите нет, но после профилирования моего текущего кода повторяющаяся операция возврата массивов клочков съела гигантский кусок времени выполнения. Сейчас у меня есть общий метод, основанный на представлении:

reversed_arr = arr[::-1]

Есть ли какой-то другой способ сделать это более эффективно, или это просто иллюзия из-за моей одержимости нереальным выступлением?


27
Э-э ... arr[::-1]просто возвращает перевернутое представление. Это так быстро, как вы можете получить, и не зависит от количества элементов в массиве, поскольку это просто меняет шаги. Является ли то, что вы изменили на самом деле массивом?
— Джо Кингтон

да, действительно, arrэто массив NumPy.
— nye17

12
Хммм ... Ну, на моем ноутбуке это занимает около 670 наносекунд, независимо от длины массива. Если это ваше узкое место, вам может потребоваться переключить языки ... Я уверен, что вы не найдете более быстрого способа обращения к массиву. В любом случае, удачи!
— Джо Кингтон

6
Ну, вы обязательно должны запустить его внутри цикла? В некоторых случаях лучше создать массив с миллионами элементов, а затем работать со всем массивом. Даже если вы используете метод конечных разностей или что-то подобное, где результат зависит от предыдущего результата, иногда вы можете сделать это. (Акцент иногда ...) Во всяком случае, если скорость является основной целью, Фортран по-прежнему король. f2pyтвой друг! Часто стоит написать критичные к производительности части алгоритма (особенно в научных вычислениях) на другом языке и вызвать его из python. Удачи!
— Джо Кингтон

1
@berto. Это медленнее, так как это обертка для arr[::-1]: github.com/numpy/numpy/blob/master/numpy/lib/twodim_base.py . Ищите def flipud. Функция буквально в четыре строки.
— Безумный физик

Ответы:


240

При создании reversed_arrвы создаете представление в исходном массиве. Затем вы можете изменить исходный массив, и представление обновится, чтобы отразить изменения.

Вы воссоздаете представление чаще, чем нужно? Вы должны быть в состоянии сделать что-то вроде этого:

arr = np.array(some_sequence)
reversed_arr = arr[::-1]

do_something(arr)
look_at(reversed_arr)
do_something_else(arr)
look_at(reversed_arr)

Я не эксперт в NumPy, но похоже, что это будет самый быстрый способ сделать все в NUMPY. Если это то, что вы уже делаете, я не думаю, что вы можете улучшить это.

PS Отличная дискуссия о numpy взглядах здесь:

Посмотреть на массив NumPy?


Помогает ли это создать объект среза, а затем повторно использовать его во многих массивах?
— эндолит

1
На самом деле я только что проверил это и не вижу никакой разницы с объектом слайса, созданным вне цикла. (Ой, подождите, это очень немного быстрее. Повторно 43,4 мс против 44,3 мс для цикла 1000000)
— эндолит

Что look_atдолжна делать функция?
— mrgloom

1
@mrgloom Предполагается, что представляет собой любую задачу, которая смотрит на данные. Целью примера было показать, что reversed_arrпредставление все еще можно использовать после изменения базовых данных. Запись новых значений в массив не лишает законной силы представление. На самом деле вы также можете использовать представление для записи новых значений в массив. reversed_arr[0] = 99установил бы последний элемент в массиве на 99, так же как arr[-1] = 99и.
— Steveha

60

Как упоминалось выше, на a[::-1]самом деле создается только представление, поэтому это операция с постоянным временем (и, как таковая, не занимает больше времени при увеличении массива). Если вам нужно, чтобы массив был смежным (например, потому что вы выполняете с ним много векторных операций), ascontiguousarrayэто примерно так же быстро, как flipup/ fliplr:

введите описание изображения здесь


Код для генерации сюжета:

import numpy
import perfplot


perfplot.show(
    setup=lambda n: numpy.random.randint(0, 1000, n),
    kernels=[
        lambda a: a[::-1],
        lambda a: numpy.ascontiguousarray(a[::-1]),
        lambda a: numpy.fliplr([a])[0],
    ],
    labels=["a[::-1]", "ascontiguousarray(a[::-1])", "fliplr"],
    n_range=[2 ** k for k in range(25)],
    xlabel="len(a)",
    logx=True,
    logy=True,
)

Для perfplot требуется как минимум Python 3.6, потому что он использует f-строки (Literal String Interpolation)
— пятое

42

Потому что это, кажется, еще не помечено как ответ ... Ответ Томаса Арилдсена должен быть правильным: просто используйте

np.flipud(your_array) 

если это массив 1d (массив столбцов).

С матрицами делаю

fliplr(matrix)

если вы хотите перевернуть строки и flipud(matrix)перевернуть столбцы. Нет необходимости превращать массив из 1d-столбца в двухмерный массив строк (матрица с одним None-слоем), а затем переворачивать его.


38

np.fliplr() переворачивает массив слева направо.

Обратите внимание, что для 1d-массивов вам нужно немного его обмануть:

arr1d = np.array(some_sequence)
reversed_arr = np.fliplr([arr1d])[0]

34
reversed_arr = np.flipud(arr1d)кажется, работает напрямую.
— Томас Арилдсен

3

Я остановлюсь на более раннем ответе о np.fliplr(). Вот некоторый код, который демонстрирует создание 1d-массива, преобразование его в 2-мерный массив, переворачивание и последующее преобразование обратно в 1-мерный массив. time.clock()будет использоваться для хранения времени, которое представлено в секундах.

import time
import numpy as np

start = time.clock()
x = np.array(range(3))
#transform to 2d
x = np.atleast_2d(x)
#flip array
x = np.fliplr(x)
#take first (and only) element
x = x[0]
#print x
end = time.clock()
print end-start

С заявлением на печать без комментариев:

[2 1 0]
0.00203907123594

С заявлением о печати закомментировано:

5.59799927506e-05

Так что, с точки зрения эффективности, я думаю, что это достойно. Для тех из вас, кто любит делать это в одной строке, вот эта форма.

np.fliplr(np.atleast_2d(np.array(range(3))))[0]

3
Сроки чего-то с таким маленьким массивом довольно бесполезны. Если вы хотите сравнить вещи, было бы лучше использовать что-то, что занимает некоторое время, например, 3000 или даже больше элементов.
— Барабас

0

Подробно о том, что сказали другие, приведу короткий пример.

Если у вас есть одномерный массив ...

>>> import numpy as np
>>> x = np.arange(4) # array([0, 1, 2, 3])
>>> x[::-1] # returns a view
Out[1]: 
array([3, 2, 1, 0])

Но если вы работаете с 2D-массивом ...

>>> x = np.arange(10).reshape(2, 5)
>>> x
Out[2]:
array([[0, 1, 2, 3, 4],
       [5, 6, 7, 8, 9]])

>>> x[::-1] # returns a view:
Out[3]: array([[5, 6, 7, 8, 9],
               [0, 1, 2, 3, 4]])

Это на самом деле не переворачивает Матрицу.

Следует использовать np.flip для реверсирования элементов

>>> np.flip(x)
Out[4]: array([[9, 8, 7, 6, 5],
               [4, 3, 2, 1, 0]])

Если вы хотите распечатать элементы матрицы по одному, используйте плоскую и флип

>>> for el in np.flip(x).flat:
>>>     print(el, end = ' ')
9 8 7 6 5 4 3 2 1 0

-1

Чтобы он работал с отрицательными числами и длинным списком, вы можете сделать следующее:

b = numpy.flipud(numpy.array(a.split(),float))

Где flipud для 1d arra

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.