Как скопировать данные из массива 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

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

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