Cara menyalin data dari array numpy ke yang lain

87

Apa cara tercepat untuk menyalin data dari larik b ke larik a, tanpa mengubah alamat larik a. Saya memerlukan ini karena perpustakaan eksternal (PyFFTW) menggunakan penunjuk ke array saya yang tidak dapat berubah.

Sebagai contoh:

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

Apakah mungkin untuk melakukannya tanpa loop?

Charles Brunet
sumber

Jawaban:

89

aku percaya

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

akan membuat salinan dalam dengan cepat. Seperti yang disinggung Funsi, numpy versi terbaru juga punya copytofungsi.

Brian Hawkins
sumber
4
+1. Tapi bukankah numpy.empty secara substansial lebih cepat daripada numpy.zeros ?
mg007
9
@ M.ElSaka a = bhanya membuat referensi baru ke b. a[:] = bberarti "mengatur semua elemen asama dengan yang ada b". Perbedaannya penting karena array numpy adalah tipe yang bisa berubah.
Brian Hawkins
14
@ mg007 Saya menjalankan beberapa tes, yang menunjukkan empty()sekitar 10% lebih cepat daripada zeros(). Ternyata empty_like()lebih cepat. copyto(a,b)lebih cepat dari sintaks array a[:] = b. Lihat gist.github.com/bhawkins/5095558
Brian Hawkins
2
@ Brian Hawkins benar. Untuk kapan harus digunakan np.copyto(a, b)dan kapan a = b.astype(b.dtype)untuk peningkatan kecepatan, lihat jawaban di bawah ini: stackoverflow.com/a/33672015/3703716
mab
1
@michael_n Saya terkejut empty_likejauh lebih cepat daripada empty, terutama karena zeros_likelebih lambat dari zeros. BTW Saya baru saja menjalankan kembali benchmark saya (sekarang diperbarui), dan perbedaan antara copyto(a,b)dan a[:] = btampaknya telah menguap. gist.github.com/bhawkins/5095558
Brian Hawkins
27

NumPy versi 1.7 memiliki numpy.copytofungsi yang melakukan apa yang Anda cari:

numpy.copyto (dst, src)

Menyalin nilai dari satu larik ke larik lainnya, menyiarkan seperlunya.

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

Funsi
sumber
Ini tidak berhasil untuk saya. Saya mendapatkanAttributeError: 'module' object has no attribute 'copyto'
kalu
20
a = numpy.array(b)

bahkan lebih cepat daripada solusi yang disarankan hingga numpy v1.6 dan juga membuat salinan larik. Namun saya tidak dapat mengujinya dengan copyto (a, b), karena saya tidak memiliki versi terbaru dari numpy.

Benor
sumber
Ini adalah cara yang bagus untuk menyalin larik, tetapi ini membuat objek baru. OP perlu mengetahui cara menetapkan nilai dengan cepat ke array yang sudah dibuat.
Brian Hawkins
15

Untuk menjawab pertanyaan Anda, saya bermain dengan beberapa varian dan membuat profilnya.

Kesimpulan: untuk menyalin data dari array numpy ke yang lain gunakan salah satu fungsi numpy bawaan numpy.array(src)ataunumpy.copyto(dst, src) jika memungkinkan.

(Tapi selalu pilih nanti jika dst memori sudah dialokasikan, untuk menggunakan kembali memori. Lihat profil di akhir posting.)

pengaturan profil

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

kode profil

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

hasil untuk Windows 7 pada Intel i7 CPU, CPython v3.5.0, numpy v1.10.1.


Selain itu, lihat hasil untuk varian pembuatan profil di mana memori tujuan sudah dialokasikan sebelumnya selama penyalinan nilai, karena y = np.empty_like(x)merupakan bagian dari penyiapan:

mab
sumber
Juga x.copy()secepat np.array(x)dan saya lebih suka sintaksnya: $ 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. Saya memiliki hasil yang serupa untuk np.array(x). Diuji di Linux dengan i5-4210U dan numpy 1.10.4
Marco Sulla
Ya Marco, ini lebih merupakan masalah selera pribadi. Tapi catatan itu np.copylebih memaafkan: np.copy(False), np.copy(None)masih kerja, sementara a = None; a.copy()melempar AttributeError: 'NoneType' object has no attribute 'copy'. Juga, kami lebih tepat dalam mendeklarasikan apa yang kami inginkan terjadi di baris kode ini menggunakan fungsi daripada sintaks metode.
mab
1
Nah, fakta np.copy(None)tidak melempar kesalahan itu benar-benar unpythonic. Satu alasan lagi untuk menggunakan a.copy():)
Marco Sulla
1
Saya baru saja menjalankan benchmark ini dengan Python 2.7.12, NumPy 1.11.2 dan menemukan bahwa y[:] = xsekarang sedikit lebih cepat dari copyto(y, x). Kode dan keluaran di gist.github.com/bhawkins/7cdbd5b9372cb798e34e21f92279d2dc
Brian Hawkins
10

Anda dapat dengan mudah menggunakan:

b = 1*a

ini adalah cara tercepat, tetapi juga memiliki beberapa masalah. Jika Anda tidak menentukan secara langsung dtypedari adan juga tidak memeriksa dtypedari bAnda bisa mendapat masalah. Sebagai contoh:

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

Saya harap, saya bisa menjelaskan maksudnya. Terkadang Anda akan mengalami perubahan tipe data hanya dengan satu operasi kecil.

ahelm
sumber
1
Tidak. Melakukannya akan membuat larik baru. Ini setara dengan b = a.copy ().
Charles Brunet
maaf, tapi saya tidak mengerti. Apa yang Anda maksud dengan membuat larik baru? Semua metode lain yang disajikan di sini memiliki perilaku yang sama. a = numpy.zeros(len(b))atau a = numpy.empty(n,dtype=complex)juga akan membuat array baru.
ahelm
2
Misalkan Anda memiliki a = numpy.empty (1000). Sekarang, Anda perlu mengisi dengan data, tanpa mengubah alamatnya di memori. Jika Anda melakukan a [0] = 1, Anda tidak membuat ulang larik, Anda cukup mengubah konten larik.
Charles Brunet
1
@CharlesBrunet array harus dibuat di beberapa titik. One-liner pintar ini hanya melakukan semuanya dalam satu operasi.
heltonbiker
7

Ada banyak hal berbeda yang dapat Anda lakukan:

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)

Hal-hal yang tidak berhasil

a=b
a=b[:] # This have given my code bugs 
Peter Mølgaard Pallesen
sumber
2

Mengapa tidak digunakan

a = 0 + b

Menurut saya ini mirip dengan perkalian sebelumnya tetapi mungkin lebih sederhana.

JQK
sumber