Jak skopiować dane z tablicy numpy do innej


86

Jaki jest najszybszy sposób na skopiowanie danych z tablicy b do tablicy a bez modyfikowania adresu tablicy a. Potrzebuję tego, ponieważ biblioteka zewnętrzna (PyFFTW) używa wskaźnika do mojej tablicy, której nie można zmienić.

Na przykład:

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

Czy można to zrobić bez pętli?

Odpowiedzi:


86

wierzę

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

szybko utworzy głęboką kopię. Jak wspomina Funsi, ostatnie wersje numpy również mają tę copytofunkcję.


4
+1. Ale czy numpy.empty nie byłby znacznie szybszy niż numpy.zeros ?
mg007

9
@ M.ElSaka a = btworzy jedynie nowe odniesienie do b. a[:] = boznacza „ustaw wszystkie elementy na arówne z b”. Różnica jest ważna, ponieważ tablice numpy są zmiennymi typami.
Brian Hawkins

14
@ mg007 wykonałem kilka testów, które pokazały, że empty()jest około 10% szybsze niż zeros(). Zaskakujące empty_like()jest jeszcze szybsze. copyto(a,b)jest szybszy niż składnia tablicy a[:] = b. Zobacz gist.github.com/bhawkins/5095558
Brian Hawkins

2
@Brian Hawkins ma rację. Aby dowiedzieć się, kiedy użyć np.copyto(a, b)i kiedy a = b.astype(b.dtype)poprawić szybkość, zobacz odpowiedź poniżej: stackoverflow.com/a/33672015/3703716
mab

1
@michael_n Zdziwiłem się, że empty_likejest o wiele szybszy niż empty, zwłaszcza że zeros_likejest wolniejszy niż zeros. BTW właśnie ponownie uruchomiłem mój benchmark (teraz zaktualizowany) i różnica między copyto(a,b)i a[:] = bwydaje się, że wyparowała. gist.github.com/bhawkins/5095558
Brian Hawkins


19
a = numpy.array(b)

jest nawet szybszy niż sugerowane rozwiązania do numpy w wersji 1.6 i również tworzy kopię tablicy. Nie mogłem jednak przetestować go na copyto (a, b), ponieważ nie mam najnowszej wersji numpy.


To świetny sposób na skopiowanie tablicy, ale tworzy nowy obiekt. OP musi wiedzieć, jak szybko przypisać wartości do tablicy, która została już utworzona.
Brian Hawkins

15

Odpowiadając na twoje pytanie, bawiłem się kilkoma wariantami i sprofilowałem je.

Wniosek: aby skopiować dane z tablicy numpy do innej, użyj jednej z wbudowanych funkcji numpy numpy.array(src)lub numpy.copyto(dst, src)gdziekolwiek jest to możliwe.

(Ale zawsze wybieraj późniejsze, jeśli dstpamięć jest już przydzielona, ​​aby ponownie użyć pamięci. Zobacz profilowanie na końcu postu).

konfiguracja profilowania

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

kod profilujący

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

wyniki dla systemu Windows 7 z procesorem Intel i7, CPython 3.5.0, numpy v1.10.1.


Zobacz również wyniki dla wariantu profilowania, w którym pamięć miejsca docelowego jest już wstępnie przydzielona podczas kopiowania wartości, ponieważ y = np.empty_like(x)jest częścią konfiguracji:


Również x.copy()jest tak szybki, jak np.array(x)i bardziej podoba mi się składnia: $ 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. Mam podobne wyniki dla np.array(x). Testowane na Linuksie z i5-4210U i numpy 1.10.4
Marco Sulla

Tak Marco, to raczej kwestia gustu. Ale uwaga, że np.copyjest bardziej wyrozumiały: np.copy(False), np.copy(None)nadal pracować, gdy a = None; a.copy()rzuca AttributeError: 'NoneType' object has no attribute 'copy'. Ponadto jesteśmy bardziej precyzyjni w deklarowaniu tego, co chcemy, aby się wydarzyło w tym wierszu kodu, używając funkcji zamiast składni metody.
mab

1
Cóż, fakt, np.copy(None)że nie rzuca błędu, jest naprawdę niepytoniczny. Jeszcze jeden powód do użycia a.copy():)
Marco Sulla

1
Właśnie przeprowadziłem te testy porównawcze w Pythonie 2.7.12, NumPy 1.11.2 i stwierdziłem, że y[:] = xjest teraz nieznacznie szybszy niż copyto(y, x). Kod i dane wyjściowe na gist.github.com/bhawkins/7cdbd5b9372cb798e34e21f92279d2dc
Brian Hawkins

10

możesz łatwo używać:

b = 1*a

jest to najszybszy sposób, ale mają też pewne problemy. Jeśli nie zdefiniujesz bezpośrednio dtypeof, aa także nie zaznaczysz dtypeof b, możesz wpaść w kłopoty. Na przykład:

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

Mam nadzieję, że potrafię jasno to wyjaśnić. Czasami będziesz mieć zmianę typu danych za pomocą jednej małej operacji.


1
Nie. Spowoduje to utworzenie nowej tablicy. Jest równoważne b = a.copy ().
Charles Brunet

przepraszam, ale cię nie rozumiem. Co masz na myśli, mówiąc o tworzeniu nowej tablicy? Wszystkie inne metody, które są tutaj prezentowane, zachowują się tak samo. a = numpy.zeros(len(b))lub a = numpy.empty(n,dtype=complex)też utworzy nową tablicę.
ahelm

2
Załóżmy, że masz a = numpy.empty (1000). Teraz musisz wypełnić dane, nie zmieniając adresu w pamięci. Jeśli wykonasz [0] = 1, nie tworzysz ponownie tablicy, po prostu zmieniasz zawartość tablicy.
Charles Brunet

1
@CharlesBrunet tablica będzie musiała zostać utworzona w pewnym momencie. Ten sprytny jednorzędowiec robi to wszystko w jednej operacji.
heltonbiker

7

Możesz zrobić wiele różnych rzeczy:

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)

Rzeczy, które nie działają

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

1

Dlaczego nie skorzystać

a = 0 + b

Myślę, że jest podobny do poprzedniego mnożenia, ale może być prostszy.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.