Как скопировать / повторить массив N раз в новый массив? [Дубликат]

1

Я имею:

test = np.random.randn(40,40,3)

И я хочу сделать:

result = Repeat(test, 10)

Таким образом, result содержит test массива, повторяемый 10 раз, с формой:

(10, 40, 40, 3)

Поэтому создайте тензор с новой осью, чтобы провести 10 копий test. Я также хочу сделать это максимально эффективно. Как я могу это сделать с помощью Numpy?

Спасибо!

Теги:
arrays
numpy

3 ответа

4

Вы можете использовать методы 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)
2

Предполагая, что вы хотите скопировать значения 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 предупреждает, что вы, вероятно, не должны ее использовать, а другая половина предупреждает, что определенно не следует использовать ее для записи, доступной для записи, поэтому... убедитесь, что это именно то, что вы хотите, прежде чем делать это.

  • 1
    broadcast_to вероятно, также может управлять сценарием без копирования, np.broadcast_to(arr, (count,)+arr.shape) .
  • 0
    @miradulo Хороший вопрос. На самом деле, я думаю, что broadcast_to - это в основном удобная функция, разработанная для того, чтобы никто не использовал as_strided для этого as_strided использования…
Показать ещё 3 комментария
0

Из многих способов создания надлежащей копии предварительное распределение + вещание кажется самым быстрым.

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 происходит медленнее, если вообще.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню