Як скопіювати дані з масиву 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

швидко зробить глибоку копію. Як згадує Funsi, останні функції 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
@Brian Hawkins має рацію. Про те, коли використовувати np.copyto(a, b)і коли a = b.astype(b.dtype)для покращення швидкості, дивіться відповідь нижче: stackoverflow.com/a/33672015/3703716
маб

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 повинен знати, як швидко призначати значення масиву, який уже створений.
Brian Hawkins

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
Припустимо, у вас є = numpy.empty (1000). Тепер вам потрібно заповнити a даними, не змінюючи його адреси в пам'яті. Якщо ви робите [0] = 1, ви не відтворюєте масив, ви просто змінюєте вміст масиву.
Charles Brunet

1
@CharlesBrunet масив колись доведеться створити. Цей розумний однокласник просто робить все за одну операцію.
heltonbiker 06.03.13

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.