Как скопировать данные из массива numpy в другой

86

Каков самый быстрый способ скопировать данные из массива b в массив a, не изменяя адрес массива a. Мне это нужно, потому что внешняя библиотека (PyFFTW) использует указатель на мой массив, который не может измениться.

Например:

a = numpy.empty(n, dtype=complex)
for i in xrange(a.size):
  a[i] = b[i]

А можно без шлейфа?

Шарль Брюне
источник

Ответы:

86

я верю

a = numpy.empty_like (b)
a[:] = b

быстро сделает глубокую копию. Как упоминает Фунси, последние версии numpy также имеют copytoфункцию.

Брайан Хокинс
источник
4
+1. Но разве numpy.empty не будет существенно быстрее, чем numpy.zeros ?
mg007
9
@ M.ElSaka a = bпросто создает новую ссылку на b. a[:] = bозначает «установить все элементы aравными элементам из b». Разница важна, потому что массивы numpy являются изменяемыми типами.
Брайан Хокинс
14
@ mg007 Я провел несколько тестов, которые показали, что empty()он примерно на 10% быстрее, чем zeros(). На удивление empty_like()даже быстрее. copyto(a,b)быстрее, чем синтаксис массива a[:] = b. См. Gist.github.com/bhawkins/5095558
Брайан Хокинс,
2
@ Брайан Хокинс прав. Чтобы узнать, когда использовать, np.copyto(a, b)а когда a = b.astype(b.dtype)для повышения скорости, см. Ответ ниже: stackoverflow.com/a/33672015/3703716
mab
1
@michael_n Я был удивлен, empty_likeон намного быстрее empty, тем более что zeros_likeон медленнее, чем zeros. Кстати, я только что повторно запустил свой тест (теперь обновлен), и разница между copyto(a,b)и, a[:] = bпохоже, испарилась. gist.github.com/bhawkins/5095558
Брайан Хокинс,
26

NumPy версии 1.7 имеет numpy.copytoфункцию, которая делает то, что вы ищете:

numpy.copyto (dst, src)

Копирует значения из одного массива в другой, передавая их по мере необходимости.

См. Https://docs.scipy.org/doc/numpy/reference/generated/numpy.copyto.html

Фунси
источник
У меня это не работает. Я получаюAttributeError: 'module' object has no attribute 'copyto'
калу
19
a = numpy.array(b)

даже быстрее, чем предлагаемые решения до numpy v1.6, а также делает копию массива. Однако я не смог протестировать его против copyto (a, b), так как у меня нет самой последней версии numpy.

Бенор
источник
Это отличный способ скопировать массив, но он создает новый объект. OP должен знать, как быстро присвоить значения уже созданному массиву.
Брайан Хокинс,
15

Чтобы ответить на ваш вопрос, я поиграл с некоторыми вариантами и профилировал их.

Вывод: для копирования данных из массива numpy в другой используйте одну из встроенных функций numpy numpy.array(src)или numpy.copyto(dst, src)там, где это возможно.

(Но всегда выбирайте более позднее, если dstпамять уже выделена, чтобы повторно использовать память. См. Профилирование в конце сообщения.)

настройка профилирования

import timeit
import numpy as np
import pandas as pd
from IPython.display import display

def profile_this(methods, setup='', niter=10 ** 4, p_globals=None, **kwargs):
    if p_globals is not None:
        print('globals: {0}, tested {1:.0e} times'.format(p_globals, niter))
    timings = np.array([timeit.timeit(method, setup=setup, number=niter,
                                      globals=p_globals, **kwargs) for 
                        method in methods])
    ranking = np.argsort(timings)
    timings = np.array(timings)[ranking]
    methods = np.array(methods)[ranking]
    speedups = np.amax(timings) / timings

    pd.set_option('html', False)
    data = {'time (s)': timings,
            'speedup': ['{:.2f}x'.format(s) if 1 != s else '' for s in speedups],
            'methods': methods}
    data_frame = pd.DataFrame(data, columns=['time (s)', 'speedup', 'methods'])

    display(data_frame)
    print()

код профилирования

setup = '''import numpy as np; x = np.random.random(n)'''
methods = (
    '''y = np.zeros(n, dtype=x.dtype); y[:] = x''',
    '''y = np.zeros_like(x); y[:] = x''',
    '''y = np.empty(n, dtype=x.dtype); y[:] = x''',
    '''y = np.empty_like(x); y[:] = x''',
    '''y = np.copy(x)''',
    '''y = x.astype(x.dtype)''',
    '''y = 1*x''',
    '''y = np.empty_like(x); np.copyto(y, x)''',
    '''y = np.empty_like(x); np.copyto(y, x, casting='no')''',
    '''y = np.empty(n)\nfor i in range(x.size):\n\ty[i] = x[i]'''
)

for n, it in ((2, 6), (3, 6), (3.8, 6), (4, 6), (5, 5), (6, 4.5)):
    profile_this(methods[:-1:] if n > 2 else methods, setup, 
                 niter=int(10 ** it), p_globals={'n': int(10 ** n)})

результаты для Windows 7 на процессоре Intel i7, CPython v3.5.0, numpy v1.10.1.


Также см. Результаты для варианта профилирования, в котором память назначения уже заранее выделена во время копирования значения, поскольку y = np.empty_like(x)это часть настройки:

маб
источник
Также x.copy()работает так же быстро, как np.array(x)и мне гораздо больше нравится синтаксис: $ python3 -m timeit -s "import numpy as np; x = np.random.random((100, 100))" "x.copy()"- 100000 loops, best of 3: 4.7 usec per loop. У меня есть аналогичные результаты для np.array(x). Протестировано на Linux с i5-4210U и numpy 1.10.4
Марко Сулла,
Да, Марко, это, скорее, дело личного вкуса. Но обратите внимание , что np.copyболее прощая: np.copy(False), до np.copy(None)сих пор работают, в то время как a = None; a.copy()броски AttributeError: 'NoneType' object has no attribute 'copy'. Кроме того, мы более точно объявляем, что мы хотим, в этой строке кода, используя функцию вместо синтаксиса метода.
mab
1
Ну факт np.copy(None)не выкидывает ошибку действительно непифонично. Еще одна причина использовать a.copy():)
Марко Сулла
1
Я только что запустил эти тесты с Python 2.7.12, NumPy 1.11.2 и обнаружил, что y[:] = xтеперь они немного быстрее, чем copyto(y, x). Код и вывод на gist.github.com/bhawkins/7cdbd5b9372cb798e34e21f92279d2dc
Брайан Хокинс,
10

вы можете легко использовать:

b = 1*a

это самый быстрый способ, но есть и некоторые проблемы. Если вы не определяете Непосредственно dtypeиз aа также не проверяет dtypeиз bвас может попасть в беду. Например:

a = np.arange(10)        # dtype = int64
b = 1*a                  # dtype = int64

a = np.arange(10.)       # dtype = float64
b = 1*a                  # dtype = float64

a = np.arange(10)        # dtype = int64
b = 1. * a               # dtype = float64

Я надеюсь, что смогу прояснить суть дела. Иногда вы можете изменить тип данных всего за одну небольшую операцию.

Ахельм
источник
1
Нет. Будет создан новый массив. Это эквивалентно b = a.copy ().
Charles Brunet
извини, но я тебя не понимаю. Что вы имеете в виду под созданием нового массива? Все остальные методы, представленные здесь, ведут себя так же. a = numpy.zeros(len(b))или a = numpy.empty(n,dtype=complex)также создаст новый массив.
ahelm
2
Предположим, у вас есть a = numpy.empty (1000). Теперь вам нужно заполнить a данными, не изменяя его адрес в памяти. Если вы укажете [0] = 1, вы не создадите массив заново, вы просто измените его содержимое.
Charles Brunet
1
@CharlesBrunet в какой-то момент придется создать массив. Этот умный однострочник просто сделает все за одну операцию.
heltonbiker 06
7

Вы можете делать много разных вещей:

a=np.copy(b)
a=np.array(b) # Does exactly the same as np.copy
a[:]=b # a needs to be preallocated
a=b[np.arange(b.shape[0])]
a=copy.deepcopy(b)

Вещи, которые не работают

a=b
a=b[:] # This have given my code bugs 
Петер Мёльгаард Паллесен
источник
1

Почему бы не использовать

a = 0 + b

Я думаю, что это похоже на предыдущее умножение, но может быть проще.

JQK
источник