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