Я имею:
test = np.random.randn(40,40,3)
И я хочу сделать:
result = Repeat(test, 10)
Таким образом, result
содержит test
массива, повторяемый 10 раз, с формой:
(10, 40, 40, 3)
Поэтому создайте тензор с новой осью, чтобы провести 10 копий test
. Я также хочу сделать это максимально эффективно. Как я могу это сделать с помощью Numpy?
Спасибо!
Вы можете использовать методы np.repeat
вместе с np.newaxis
:
import numpy as np
test = np.random.randn(40,40,3)
result = np.repeat(test[np.newaxis,...], 10, axis=0)
print(result.shape)
>> (10, 40, 40, 3)
Предполагая, что вы хотите скопировать значения 10 раз, вы можете просто stack
10 из массива:
def repeat(arr, count):
return np.stack([arr for _ in range(count)], axis=0)
axis=0
на самом деле является значением по умолчанию, поэтому здесь это действительно не нужно, но я думаю, что он дает понять, что вы добавляете новую ось на передней панели.
Фактически, это в значительной степени идентично тому, что делают примеры для stack
:
>>> arrays = [np.random.randn(3, 4) for _ in range(10)]
>>> np.stack(arrays, axis=0).shape
(10, 3, 4)
На первый взгляд вы можете подумать, что repeat
или tile
будут лучше подходят.
Но repeat
повторяется по существующей оси (или выравниванию массива), поэтому вам нужно будет reshape
до или после. (Это так же эффективно, но я думаю не так просто.)
И tile
(если вы использовать массив типа reps
-са скалярных reps
это в основном repeat
) примерно заполнение многомерных спецификаций во всех направлениях, что является гораздо более сложным, чем то, что вы хотите для этого простого случая.
Все эти варианты будут одинаково эффективными. Все они копируют данные 10 раз, что является дорогостоящей частью; стоимость любой внутренней обработки, создание крошечных промежуточных объектов и т.д. не имеет значения. Единственный способ сделать это быстрее - избежать копирования. Которое вы, вероятно, не хотите делать.
Но если вы... Чтобы поделиться хранилищем строк по 10 копиям, вы, вероятно, хотите, чтобы broadcast_to
:
def repeat(arr, count):
return np.broadcast_to(arr, (count,)+arr.shape)
Обратите внимание, что broadcast_to
самом деле не гарантирует, что он избегает копирования, а именно, что он возвращает какое-то представление для чтения, где "более одного элемента широковещательного массива могут ссылаться на одну ячейку памяти". На практике это позволит избежать копирования. Если вы действительно нуждаетесь в этом, чтобы быть гарантированным по какой-либо причине (или если вы хотите, чтобы доступная для записи точка зрения, которая обычно будет ужасной идеей, но, возможно, у вас есть веская причина...), вам нужно спуститься к as_strided
:
def repeat(arr, count):
shape = (count,) + arr.shape
strides = (0,) + arr.strides
return np.lib.stride_tricks.as_strided(
arr, shape=shape, strides=strides, writeable=False)
Обратите внимание, что половина документов для as_strided
предупреждает, что вы, вероятно, не должны ее использовать, а другая половина предупреждает, что определенно не следует использовать ее для записи, доступной для записи, поэтому... убедитесь, что это именно то, что вы хотите, прежде чем делать это.
Из многих способов создания надлежащей копии предварительное распределение + вещание кажется самым быстрым.
import numpy as np
def f_pp_0():
out = np.empty((10, *a.shape), a.dtype)
out[...] = a
return out
def f_pp_1():
out = np.empty((10, *a.shape), a.dtype)
np.copyto(out, a)
return out
def f_oddn():
return np.repeat(a[np.newaxis,...], 10, axis=0)
def f_abar():
return np.stack([a for _ in range(10)], axis=0)
def f_arry():
return np.array(10*[a])
from timeit import timeit
a = np.random.random((40, 40, 3))
for f in list(locals().values()):
if callable(f) and f.__name__.startswith('f_'):
print(f.__name__, timeit(f, number=100000)/100, 'ms')
Пример прогона:
f_pp_0 0.019641224660445003 ms
f_pp_1 0.019557840081397444 ms
f_oddn 0.01983011547010392 ms
f_abar 0.03257150553865358 ms
f_arry 0.02305851033888757 ms
Но различия невелики, например, repeat
происходит медленнее, если вообще.
broadcast_to
вероятно, также может управлять сценарием без копирования,np.broadcast_to(arr, (count,)+arr.shape)
.broadcast_to
- это в основном удобная функция, разработанная для того, чтобы никто не использовалas_strided
для этогоas_strided
использования…