Bir numpy dizisinden diğerine veri nasıl kopyalanır


87

A dizisinin adresini değiştirmeden verileri b dizisinden a dizisine kopyalamanın en hızlı yolu nedir? Buna ihtiyacım var çünkü harici bir kitaplık (PyFFTW) dizime değiştirilemeyen bir işaretçi kullanıyor.

Örneğin:

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

Döngü olmadan yapmak mümkün mü?

Yanıtlar:


89

inanıyorum

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

hızlı bir şekilde derin bir kopya oluşturacaktır. Funsi'nin bahsettiği gibi, numpy'nin son sürümleri de copytoişleve sahiptir.


4
+1. Fakat numpy.empty, numpy.zeros'tan çok daha hızlı olmaz mıydı ?
mg007

9
@ M.ElSaka a = bsadece yeni bir referans oluşturur b. a[:] = b"tüm öğeleri aşağıdakilere aeşit ayarlamak" anlamına gelir b. Aradaki fark önemlidir çünkü numpy dizileri değiştirilebilir türlerdir.
Brian Hawkins

14
@ mg007'den empty()yaklaşık% 10 daha hızlı olduğunu gösteren bazı testler yaptım zeros(). Şaşırtıcı bir şekilde empty_like()daha da hızlı. copyto(a,b)dizi sözdiziminden daha hızlıdır a[:] = b. Bkz. Gist.github.com/bhawkins/5095558
Brian Hawkins

2
@ Brian Hawkins haklı. Ne zaman kullanılacağını np.copyto(a, b)ve ne zaman a = b.astype(b.dtype)hız iyileştirmesi gerektiğini öğrenmek için aşağıdaki cevaba bakın: stackoverflow.com/a/33672015/3703716
mab

1
@michael_n şaşırdığımdan empty_likeçok daha hızlı empty, özellikle zeros_likedaha yavaş olduğu için zeros. BTW ben sadece (şimdi güncellenir) benim kriter-ran yeniden ve arasındaki fark copyto(a,b)ve a[:] = bbuharlaştırılarak gibi görünüyor. gist.github.com/bhawkins/5095558
Brian Hawkins


20
a = numpy.array(b)

numpy v1.6'ya kadar önerilen çözümlerden bile daha hızlıdır ve dizinin de bir kopyasını oluşturur. Bununla birlikte, en son numpy sürümüne sahip olmadığım için copyto (a, b) ile test edemedim.


Bu, bir diziyi kopyalamanın harika bir yoludur, ancak yeni bir nesne oluşturur. OP'nin, önceden oluşturulmuş bir diziye hızla nasıl değer atayacağını bilmesi gerekir.
Brian Hawkins

15

Sorunuzu cevaplamak için bazı varyantlarla oynadım ve profillerini çıkardım.

Sonuç: bir numpy dizisinden diğerine veri kopyalamak için yerleşik numpy işlevlerinden birini numpy.array(src)veya numpy.copyto(dst, src)mümkün olan her yerde kullanın.

(Ancak dsthafızayı yeniden kullanmak için hafızası zaten ayrılmışsa her zaman sonrasını seçin . Gönderinin sonunda profil oluşturmaya bakın.

profil oluşturma kurulumu

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

profil oluşturma kodu

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

Intel i7 CPU, CPython v3.5.0, numpy v1.10.1 üzerinde Windows 7 için sonuçlar .


Ayrıca, kurulumun bir parçası olduğundan , hedef belleğinin değer kopyalama sırasında önceden tahsis edildiği bir profil oluşturma varyantının sonuçlarına bakın y = np.empty_like(x):


Ayrıca x.copy()o kadar hızlı np.array(x)ve sözdizimini daha çok seviyorum: $ 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. İçin benzer sonuçlarım var np.array(x). Linux'ta i5-4210U ve numpy 1.10.4 ile test edildi
Marco Sulla

Evet Marco, bu daha çok kişisel bir zevk meselesi. Ama dikkat np.copydaha bağışlayıcı olduğunu: np.copy(False), np.copy(None)hala iş, süre a = None; a.copy()atar AttributeError: 'NoneType' object has no attribute 'copy'. Ayrıca, bu kod satırında ne olmasını istediğimizi, yöntem sözdizimi yerine işlevi kullanarak açıklamakta daha kesin.
mab

1
Aslında, np.copy(None)bir hata atmıyor, gerçekten de ruhani değil. Kullanmak için bir neden daha a.copy():)
Marco Sulla

1
Bu kıyaslamaları Python 2.7.12, NumPy 1.11.2 ile çalıştırdım ve y[:] = xşimdi biraz daha hızlı olduğunu buldum copyto(y, x). Gist.github.com/bhawkins/7cdbd5b9372cb798e34e21f92279d2dc
Brian Hawkins

10

kullanımı kolay:

b = 1*a

bu en hızlı yoldur, ancak bazı problemler de var. Doğrudan tanımlamak yoksa dtypebir akontrol etmez, aynı zamanda ve dtypebir bsize sıkıntı içine alabilirsiniz. Örneğin:

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

Umarım konuyu netleştirebilirim. Bazen sadece küçük bir işlemle veri türü değişikliğiniz olur.


1
Hayır. Bunu yapmak yeni bir dizi oluşturur. B = a.copy () ile eşdeğerdir.
Charles Brunet

üzgünüm ama seni anlamıyorum. Yeni bir dizi oluşturmakla ne demek istiyorsun? Burada sunulan diğer tüm yöntemler aynı davranışa sahiptir. a = numpy.zeros(len(b))veya a = numpy.empty(n,dtype=complex)ayrıca yeni bir dizi oluşturacaktır.
ahelm

2
Bir = numpy.empty (1000) değeriniz olduğunu varsayalım. Şimdi, hafızadaki adresini değiştirmeden bir veriyi doldurmanız gerekiyor. Bir [0] = 1 yaparsanız, bir diziyi yeniden oluşturmazsınız, yalnızca dizinin içeriğini değiştirirsiniz.
Charles Brunet

1
@CharlesBrunet dizisinin bir noktada oluşturulması gerekecektir. Bu akıllı tek astar, hepsini tek bir işlemde yapar.
heltonbiker

7

Yapabileceğiniz birçok farklı şey var:

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)

Çalışmayan şeyler

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

2

Neden kullanmıyorsun

a = 0 + b

Sanırım önceki çarpmaya benzer ama daha basit olabilir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.