วิธีคัดลอกข้อมูลจากอาร์เรย์ numpy ไปยังอื่น


87

วิธีที่เร็วที่สุดในการคัดลอกข้อมูลจากอาร์เรย์ b ไปยังอาร์เรย์ a คืออะไรโดยไม่ต้องแก้ไขที่อยู่ของอาร์เรย์ a. ฉันต้องการสิ่งนี้เนื่องจากไลบรารีภายนอก (PyFFTW) ใช้ตัวชี้ไปยังอาร์เรย์ของฉันที่ไม่สามารถเปลี่ยนแปลงได้

ตัวอย่างเช่น:

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

เป็นไปได้ที่จะทำโดยไม่ต้องวนซ้ำ?

คำตอบ:


89

ฉันเชื่อ

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 เป็นประเภทที่ไม่แน่นอน
Brian Hawkins

14
@ mg007 ฉันวิ่งทดสอบบางอย่างซึ่งแสดงให้เห็นempty()อยู่ที่ประมาณ 10% zeros()เร็วกว่า น่าแปลกที่empty_like()เร็วกว่าด้วยซ้ำ จะเร็วกว่าไวยากรณ์อาร์เรย์copyto(a,b) a[:] = bดูgist.github.com/bhawkins/5095558
Brian Hawkins

2
@ ไบรอันฮอว์กินส์พูดถูก สำหรับเวลาที่จะใช้np.copyto(a, b)และเมื่อa = b.astype(b.dtype)การปรับปรุงความเร็วให้ดูคำตอบดังต่อไปนี้: stackoverflow.com/a/33672015/3703716
ไฟ

1
@michael_n ฉันรู้สึกประหลาดใจempty_likeเป็นอย่างมากได้เร็วกว่าemptyโดยเฉพาะอย่างยิ่งตั้งแต่ช้ากว่าzeros_like zerosBTW ฉันเพิ่งรันเกณฑ์มาตรฐานของฉันอีกครั้ง (อัปเดตแล้ว) และความแตกต่างระหว่างcopyto(a,b)และa[:] = bดูเหมือนจะหายไป gist.github.com/bhawkins/5095558
Brian Hawkins

27

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'
kalu

20
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 บน CPU 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
Marco Sulla

ใช่มาร์โกมันเป็นเรื่องของรสนิยมส่วนตัว แต่ทราบว่า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():)
Marco Sulla

1
ฉันเพิ่งรันเกณฑ์มาตรฐานเหล่านี้ด้วย Python 2.7.12, NumPy 1.11.2 และพบว่าy[:] = xตอนนี้เร็วกว่าcopyto(y, x)ไฟล์. รหัสและเอาต์พุตที่gist.github.com/bhawkins/7cdbd5b9372cb798e34e21f92279d2dc
Brian Hawkins

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) ตอนนี้คุณต้องกรอกข้อมูลโดยไม่ต้องเปลี่ยนที่อยู่ในหน่วยความจำ หากคุณทำ [0] = 1 คุณไม่ได้สร้างอาร์เรย์ใหม่คุณเพียงแค่เปลี่ยนเนื้อหาของอาร์เรย์
Charles Brunet

1
@CharlesBrunet อาร์เรย์จะต้องถูกสร้างขึ้นในบางจุด หนึ่งซับที่ชาญฉลาดนี้ทำได้ทั้งหมดในการดำเนินการเดียว
heltonbiker

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 

2

ทำไมไม่ใช้

a = 0 + b

ฉันคิดว่ามันคล้ายกับการคูณก่อนหน้านี้ แต่อาจจะง่ายกว่า

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.