So kopieren Sie Daten von einem Numpy-Array in ein anderes

85

Was ist der schnellste Weg, um Daten von Array b nach Array a zu kopieren, ohne die Adresse von Array a zu ändern. Ich brauche dies, weil eine externe Bibliothek (PyFFTW) einen Zeiger auf mein Array verwendet, der sich nicht ändern kann.

Beispielsweise:

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

Ist es möglich, es ohne Schleife zu tun?

Charles Brunet
quelle

Antworten:

85

Ich glaube

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

wird schnell eine tiefe Kopie machen. Wie Funsi erwähnt, haben auch neuere Versionen von numpy die copytoFunktion.

Brian Hawkins
quelle
4
+1. Aber wäre numpy.empty nicht wesentlich schneller als numpy.zeros ?
mg007
9
@ M.ElSaka a = berstellt lediglich einen neuen Verweis auf b. a[:] = bbedeutet "setze alle Elemente agleich denen von b". Der Unterschied ist wichtig, da Numpy-Arrays veränderbare Typen sind.
Brian Hawkins
14
@ mg007 Ich habe einige Tests durchgeführt, die zeigten, dass sie empty()etwa 10% schneller sind als zeros(). Überraschenderweise empty_like()ist noch schneller. copyto(a,b)ist schneller als die Array-Syntax a[:] = b. Siehe gist.github.com/bhawkins/5095558
Brian Hawkins
2
@ Brian Hawkins ist richtig. Wann np.copyto(a, b)und wann a = b.astype(b.dtype)eine Geschwindigkeitsverbesserung erforderlich ist
mab
1
@michael_n Ich war überrascht, dass empty_likees so viel schneller ist als empty, zumal zeros_likees langsamer ist als zeros. Übrigens habe ich gerade meinen Benchmark (jetzt aktualisiert) erneut ausgeführt, und der Unterschied zwischen copyto(a,b)und a[:] = bscheint sich verflüchtigt zu haben. gist.github.com/bhawkins/5095558
Brian Hawkins
26

NumPy Version 1.7 hat die numpy.copytoFunktion, die genau das tut, wonach Sie suchen:

numpy.copyto (dst, src)

Kopiert Werte von einem Array in ein anderes und sendet sie nach Bedarf.

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

Funsi
quelle
Das funktioniert bei mir nicht. Ich bekommeAttributeError: 'module' object has no attribute 'copyto'
Kalu
19
a = numpy.array(b)

ist sogar noch schneller als die vorgeschlagenen Lösungen bis numpy v1.6 und erstellt auch eine Kopie des Arrays. Ich konnte es jedoch nicht gegen copyto (a, b) testen, da ich nicht die neueste Version von numpy habe.

Benor
quelle
Dies ist eine großartige Möglichkeit, ein Array zu kopieren, aber es wird ein neues Objekt erstellt. Das OP muss wissen, wie man einem bereits erstellten Array schnell Werte zuweist.
Brian Hawkins
15

Um Ihre Frage zu beantworten, habe ich mit einigen Varianten gespielt und sie profiliert.

Fazit: Um Daten von einem Numpy-Array in ein anderes zu kopieren, verwenden Sie eine der integrierten Numpy-Funktionen numpy.array(src)oder numpy.copyto(dst, src)wo immer möglich.

(Wählen Sie jedoch immer die spätere Option dst, wenn der Speicher bereits zugewiesen ist, um den Speicher wiederzuverwenden. Siehe Profilerstellung am Ende des Beitrags.)

Profiling-Setup

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

Profiling-Code

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

Ergebnisse für Windows 7 auf Intel i7 CPU, CPython v3.5.0, numpy v1.10.1.


Siehe auch Ergebnisse für eine Variante der Profilerstellung, bei der der Speicher des Ziels bereits während des Wertkopierens vorab zugewiesen wurde , da dies y = np.empty_like(x)Teil des Setups ist:

mab
quelle
Auch x.copy()ist so schnell wie np.array(x)und ich mag die Syntax viel mehr: $ 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. Ich habe ähnliche Ergebnisse für np.array(x). Getestet unter Linux mit einem i5-4210U und numpy 1.10.4
Marco Sulla
Ja Marco, es ist eher eine Frage des persönlichen Geschmacks. Aber beachten Sie, dass np.copymehr verzeiht: np.copy(False), np.copy(None)noch Arbeit, während a = None; a.copy()wirft AttributeError: 'NoneType' object has no attribute 'copy'. Außerdem erklären wir genauer, was in dieser Codezeile geschehen soll, indem wir die Funktion anstelle der Methodensyntax verwenden.
Mab
1
Nun, die Tatsache, np.copy(None)dass kein Fehler auftritt, ist wirklich unpythonisch. Ein Grund mehr zu verwenden a.copy():)
Marco Sulla
1
Ich habe diese Benchmarks gerade mit Python 2.7.12, NumPy 1.11.2 ausgeführt und festgestellt, dass dies y[:] = xjetzt geringfügig schneller ist als copyto(y, x). Code und Ausgabe unter gist.github.com/bhawkins/7cdbd5b9372cb798e34e21f92279d2dc
Brian Hawkins
10

Sie können einfach verwenden:

b = 1*a

Dies ist der schnellste Weg, hat aber auch einige Probleme. Wenn Sie das nicht direkt definieren dtypevon aund auch die nicht überprüft , dtypeder bkann in Schwierigkeiten geraten Sie. Beispielsweise:

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

Ich hoffe, ich konnte den Punkt klarstellen. Manchmal wird der Datentyp mit nur einer kleinen Operation geändert.

ahelm
quelle
1
Dadurch wird ein neues Array erstellt. Es ist äquivalent zu b = a.copy ().
Charles Brunet
Entschuldigung, aber ich verstehe dich nicht. Was meinst du mit dem Erstellen eines neuen Arrays? Alle anderen hier vorgestellten Methoden haben das gleiche Verhalten. a = numpy.zeros(len(b))oder erstellt a = numpy.empty(n,dtype=complex)auch ein neues Array.
Ahelm
2
Angenommen, Sie haben a = numpy.empty (1000). Jetzt müssen Sie a mit Daten füllen, ohne die Adresse im Speicher zu ändern. Wenn Sie [0] = 1 ausführen, erstellen Sie kein Array neu, sondern ändern lediglich den Inhalt des Arrays.
Charles Brunet
1
@CharlesBrunet Das Array muss irgendwann erstellt werden. Dieser clevere Einzeiler erledigt alles in einem Arbeitsgang.
Heltonbiker
5

Es gibt viele verschiedene Dinge, die Sie tun können:

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)

Dinge, die nicht funktionieren

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

Warum nicht verwenden

a = 0 + b

Ich denke, es ist ähnlich wie bei der vorherigen Multiplikation, könnte aber einfacher sein.

JQK
quelle