Cách sao chép dữ liệu từ một mảng numpy sang mảng khác


86

Cách nhanh nhất để sao chép dữ liệu từ mảng b sang mảng a mà không sửa đổi địa chỉ của mảng a. Tôi cần điều này vì thư viện bên ngoài (PyFFTW) sử dụng một con trỏ đến mảng của tôi mà không thể thay đổi.

Ví dụ:

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

Có thể làm điều đó mà không cần vòng lặp?

Câu trả lời:


86

tôi tin

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

sẽ tạo một bản sao sâu một cách nhanh chóng. Như Funsi đã đề cập, các phiên bản gần đây của numpy cũng có copytochức năng này.


4
+1. Nhưng không phải numpy.empty sẽ nhanh hơn đáng kể so với numpy.zeros ?
mg007

9
@ M.ElSaka a = bchỉ tạo một tham chiếu mới đến b. a[:] = bcó nghĩa là "thiết lập tất cả các phần tử abằng của b". Sự khác biệt rất quan trọng vì mảng numpy là kiểu có thể thay đổi.
Brian Hawkins

14
@ mg007 Tôi đã chạy một số thử nghiệm, cho thấy empty()tốc độ nhanh hơn khoảng 10% zeros(). Đáng ngạc nhiên empty_like()là thậm chí còn nhanh hơn. copyto(a,b)nhanh hơn cú pháp mảng a[:] = b. Xem gist.github.com/bhawkins/5095558
Brian Hawkins

2
@Brian Hawkins nói đúng. Để biết khi nào sử dụng np.copyto(a, b)và khi nào a = b.astype(b.dtype)cần cải thiện tốc độ, hãy xem câu trả lời bên dưới: stackoverflow.com/a/33672015/3703716
mab

1
@michael_n Tôi đã rất ngạc nhiên empty_likelà nhanh hơn rất nhiều empty, đặc biệt là vì zeros_likechậm hơn zeros. BTW Tôi vừa chạy lại điểm chuẩn của mình (hiện đã cập nhật) và sự khác biệt giữa copyto(a,b)a[:] = bdường như đã biến mất. gist.github.com/bhawkins/5095558
Brian Hawkins

26

NumPy phiên bản 1.7 có numpy.copytochức năng thực hiện những gì bạn đang tìm kiếm:

numpy.copyto (dst, src)

Sao chép các giá trị từ mảng này sang mảng khác, phát sóng khi cần thiết.

Xem: https://docs.scipy.org/doc/numpy/reference/generated/numpy.copyto.html


Điều này không hiệu quả với tôi. Tôi nhận đượcAttributeError: 'module' object has no attribute 'copyto'
kalu

19
a = numpy.array(b)

thậm chí còn nhanh hơn các giải pháp được đề xuất cho đến numpy v1.6 và tạo bản sao của mảng. Tuy nhiên, tôi không thể kiểm tra nó với copyto (a, b), vì tôi không có phiên bản mới nhất của numpy.


Đây là một cách tuyệt vời để sao chép một mảng, nhưng nó tạo ra một đối tượng mới. OP cần biết cách nhanh chóng gán giá trị cho một mảng đã được tạo.
Brian Hawkins

15

Để trả lời câu hỏi của bạn, tôi đã chơi với một số biến thể và phân tích chúng.

Kết luận: để sao chép dữ liệu từ một mảng numpy sang mảng khác, hãy sử dụng một trong các hàm numpy tích hợp sẵn numpy.array(src)hoặc numpy.copyto(dst, src)bất cứ khi nào có thể.

(Nhưng luôn chọn sau nếu dstbộ nhớ của đã được cấp phát, để sử dụng lại bộ nhớ. Xem sơ lược ở cuối bài đăng.)

thiết lập hồ sơ

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()

mã hồ sơ

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)})

kết quả cho Windows 7 trên CPU Intel i7, CPython v3.5.0, numpy v1.10.1.


Ngoài ra, hãy xem kết quả cho một biến thể của cấu hình trong đó bộ nhớ của đích đã được cấp phát trước trong quá trình sao chép giá trị, vì y = np.empty_like(x)là một phần của thiết lập:


Cũng x.copy()nhanh như np.array(x)và tôi thích cú pháp hơn nhiều: $ 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. Tôi có kết quả tương tự cho np.array(x). Đã thử nghiệm trên Linux với i5-4210U và numpy 1.10.4
Marco Sulla

Vâng, Marco, đó là vấn đề của sở thích cá nhân. Nhưng lưu ý rằng np.copydễ tha thứ hơn : np.copy(False), np.copy(None)vẫn hoạt động, trong khi a = None; a.copy()ném AttributeError: 'NoneType' object has no attribute 'copy'. Ngoài ra, chúng tôi khai báo chính xác hơn những gì chúng tôi muốn xảy ra trong dòng mã này bằng cách sử dụng hàm thay vì cú pháp phương thức.
mab

1
Chà, thực tế là np.copy(None)không mắc lỗi thực sự là một điều khó hiểu. Thêm một lý do để sử dụng a.copy():)
Marco Sulla

1
Tôi vừa chạy các điểm chuẩn này với Python 2.7.12, NumPy 1.11.2 và thấy rằng y[:] = xhiện nhanh hơn một chút copyto(y, x). Mã và đầu ra tại gist.github.com/bhawkins/7cdbd5b9372cb798e34e21f92279d2dc
Brian Hawkins

10

bạn có thể dễ dàng sử dụng:

b = 1*a

đây là cách nhanh nhất, nhưng cũng có một số vấn đề. Nếu bạn không xác định trực tiếp dtypecủa avà cũng không kiểm tra dtypecủa bbạn có thể gặp rắc rối. Ví dụ:

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

Tôi hy vọng, tôi có thể làm cho điểm rõ ràng. Đôi khi bạn sẽ thay đổi kiểu dữ liệu chỉ với một thao tác nhỏ.


1
Không. Làm như vậy sẽ tạo ra một mảng mới. Nó tương đương với b = a.copy ().
Charles Brunet

xin lỗi, nhưng tôi không hiểu bạn. Ý bạn là gì với việc tạo một mảng mới? Tất cả các phương pháp khác được trình bày ở đây có cùng một hành vi. a = numpy.zeros(len(b))hoặc a = numpy.empty(n,dtype=complex)cũng sẽ tạo một mảng mới.
ahelm

2
Giả sử bạn có a = numpy.empty (1000). Bây giờ, bạn cần điền vào một dữ liệu mà không cần thay đổi địa chỉ của nó trong bộ nhớ. Nếu bạn thực hiện một [0] = 1, bạn không tạo lại một mảng, bạn chỉ thay đổi nội dung của mảng.
Charles Brunet

1
@CharlesBrunet mảng sẽ phải được tạo tại một số điểm. Một lớp lót thông minh này chỉ cần thực hiện tất cả trong một thao tác.
heltonbiker

7

Có nhiều điều bạn có thể làm:

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)

Những thứ không hoạt động

a=b
a=b[:] # This have given my code bugs 

1

Tại sao không sử dụng

a = 0 + b

Tôi nghĩ nó tương tự như phép nhân trước nhưng có thể đơn giản hơn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.