„Klonowanie” wektorów wierszowych lub kolumnowych

155

Czasami przydatne jest „sklonowanie” wektora wierszowego lub kolumnowego do macierzy. Przez klonowanie mam na myśli konwersję wektora wierszowego, takiego jak

[1,2,3]

W matrycę

[[1,2,3]
 [1,2,3]
 [1,2,3]
]

lub wektor kolumnowy, taki jak

[1
 2
 3
]

w

[[1,1,1]
 [2,2,2]
 [3,3,3]
]

W Matlabie lub oktawie można to zrobić dość łatwo:

 x = [1,2,3]
 a = ones(3,1) * x
 a =

    1   2   3
    1   2   3
    1   2   3

 b = (x') * ones(1,3)
 b =

    1   1   1
    2   2   2
    3   3   3

Chcę to powtórzyć odrętwieniem, ale bezskutecznie

In [14]: x = array([1,2,3])
In [14]: ones((3,1)) * x
Out[14]:
array([[ 1.,  2.,  3.],
       [ 1.,  2.,  3.],
       [ 1.,  2.,  3.]])
# so far so good
In [16]: x.transpose() * ones((1,3))
Out[16]: array([[ 1.,  2.,  3.]])
# DAMN
# I end up with 
In [17]: (ones((3,1)) * x).transpose()
Out[17]:
array([[ 1.,  1.,  1.],
       [ 2.,  2.,  2.],
       [ 3.,  3.,  3.]])

Dlaczego pierwsza metoda ( In [16]) nie działała? Czy jest sposób na wykonanie tego zadania w Pythonie w bardziej elegancki sposób?

Boris Gorelik
źródło
6
W Matlabie zauważ, że jest znacznie szybszy w użyciu repmat: repmat([1 2 3],3,1)lubrepmat([1 2 3].',1,3)
Luis Mendo
Octave również repmat.
ma11hew28
Dla tych, którzy chcą zrobić coś podobnego z pandą Dataframe, sprawdź tile_df link tutaj
zelusp

Odpowiedzi:

80

Oto elegancki sposób na zrobienie tego w Pythonie:

>>> array([[1,2,3],]*3)
array([[1, 2, 3],
       [1, 2, 3],
       [1, 2, 3]])

>>> array([[1,2,3],]*3).transpose()
array([[1, 1, 1],
       [2, 2, 2],
       [3, 3, 3]])

problem z [16]wydaje się być taki, że transpozycja nie ma wpływu na tablicę. prawdopodobnie chcesz zamiast tego matrycę:

>>> x = array([1,2,3])
>>> x
array([1, 2, 3])
>>> x.transpose()
array([1, 2, 3])
>>> matrix([1,2,3])
matrix([[1, 2, 3]])
>>> matrix([1,2,3]).transpose()
matrix([[1],
        [2],
        [3]])
Piotr
źródło
1
(transpozycja działa dla tablic 2D, np. dla kwadratu w przykładzie, lub podczas (N,1)zmiany na tablicę -shape przy użyciu .reshape(-1, 1))
Mark
34
Jest to wysoce nieefektywne. Użyj, numpy.tilejak pokazano w odpowiedzi pv .
David Heffernan,
304

Zastosowanie numpy.tile:

>>> tile(array([1,2,3]), (3, 1))
array([[1, 2, 3],
       [1, 2, 3],
       [1, 2, 3]])

lub dla powtarzających się kolumn:

>>> tile(array([[1,2,3]]).transpose(), (1, 3))
array([[1, 1, 1],
       [2, 2, 2],
       [3, 3, 3]])
pv.
źródło
16
Głosuj za! W moim systemie dla wektora z 10000 elementów powtórzonych 1000 razy tilemetoda jest 19,5 razy szybsza niż metoda w aktualnie akceptowanej odpowiedzi (przy użyciu metody mnożenia-operatora).
Dr.Jan-Philip Gehrcke
1
W drugiej sekcji („powtarzające się kolumny”), czy możesz wyjaśnić, co robi drugi zestaw nawiasów kwadratowych, np. [[1,2,3]]
Ant
@Ant tworzy tablicę 2D o długości 1 na pierwszej osi (pionowa na ekranie) i długości 3 na drugiej osi (pozioma na ekranie). Transpozycja sprawia, że ​​ma długość 3 na pierwszej osi i długość 1 na drugiej osi. Kształt kafelka (1, 3)kopiuje tę kolumnę trzy razy, dlatego wiersze wyniku zawierają po jednym odrębnym elemencie.
BallpointBen
Powinna to być akceptowana odpowiedź, ponieważ możesz przekazać dowolny wektor już zainicjowany, podczas gdy zaakceptowany może działać tylko wtedy, gdy dodasz przecinek podczas inicjalizacji wektora. Dzięki !
Yohan Obadia,
Nie mogę zmusić tego do pracy dla rozwiązania 2D do 3D :(
john ktejik
42

Po pierwsze, zauważ, że w przypadku operacji rozgłaszania numpy zwykle nie jest konieczne powielanie wierszy i kolumn. Zobacz to i to dla opisów.

Ale aby to zrobić, powtórz i newaxis są prawdopodobnie najlepszym sposobem

In [12]: x = array([1,2,3])

In [13]: repeat(x[:,newaxis], 3, 1)
Out[13]: 
array([[1, 1, 1],
       [2, 2, 2],
       [3, 3, 3]])

In [14]: repeat(x[newaxis,:], 3, 0)
Out[14]: 
array([[1, 2, 3],
       [1, 2, 3],
       [1, 2, 3]])

Ten przykład dotyczy wektora wierszowego, ale zastosowanie go do wektora kolumnowego jest miejmy nadzieję oczywiste. Powtarzanie wydaje się dobrze to przeliterować, ale możesz to również zrobić poprzez mnożenie, jak w swoim przykładzie

In [15]: x = array([[1, 2, 3]])  # note the double brackets

In [16]: (ones((3,1))*x).transpose()
Out[16]: 
array([[ 1.,  1.,  1.],
       [ 2.,  2.,  2.],
       [ 3.,  3.,  3.]])
tom10
źródło
5
newaxis ma tę dodatkową zaletę, że w rzeczywistości nie kopiuje danych, dopóki nie jest to konieczne. Więc jeśli robisz to, aby pomnożyć lub dodać do innej tablicy 3x3, powtórzenie jest niepotrzebne. Przeczytaj o numpy broadcasting, aby zrozumieć pomysł.
AFoglia
@AFoglia - Słuszna uwaga. Zaktualizowałem moją odpowiedź, aby to zaznaczyć.
tom10
1
Jakie korzyści z używania np.repeatvs np.tile?
mrgloom
@mrgloom: Brak, głównie w tym przypadku. W przypadku małej tablicy 1D są podobne i nie ma znaczącej różnicy / korzyści / korzyści / itp. Osobiście uważam, że symetria między klonowaniem wierszy i kolumn jest bardziej intuicyjna i nie podoba mi się transpozycja wymagana dla kafelków, ale to tylko kwestia gustu. Odpowiedź Mateena Ulhaqa również mówi, że powtarzanie jest szybsze, ale może to zależeć od dokładnego rozważanego przypadku użycia, chociaż powtórzenie jest znacznie bliższe funkcji C, więc prawdopodobnie pozostanie nieco szybsze. W 2D zachowują się inaczej, więc ma to znaczenie.
tom10,
12

Pozwolić:

>>> n = 1000
>>> x = np.arange(n)
>>> reps = 10000

Alokacje bez kosztów

Widok nie ponosi żadnej dodatkowej pamięci. Stąd te deklaracje są natychmiastowe:

# New axis
x[np.newaxis, ...]

# Broadcast to specific shape
np.broadcast_to(x, (reps, n))

Przydział przymusowy

Jeśli chcesz wymusić, aby zawartość rezydowała w pamięci:

>>> %timeit np.array(np.broadcast_to(x, (reps, n)))
10.2 ms ± 62.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

>>> %timeit np.repeat(x[np.newaxis, :], reps, axis=0)
9.88 ms ± 52.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

>>> %timeit np.tile(x, (reps, 1))
9.97 ms ± 77.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Wszystkie trzy metody mają mniej więcej taką samą prędkość.

Obliczenie

>>> a = np.arange(reps * n).reshape(reps, n)
>>> x_tiled = np.tile(x, (reps, 1))

>>> %timeit np.broadcast_to(x, (reps, n)) * a
17.1 ms ± 284 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

>>> %timeit x[np.newaxis, :] * a
17.5 ms ± 300 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

>>> %timeit x_tiled * a
17.6 ms ± 240 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Wszystkie trzy metody mają mniej więcej taką samą prędkość.


Wniosek

Jeśli chcesz przeprowadzić replikację przed obliczeniem, rozważ użycie jednej z metod „alokacji z zerowym kosztem”. Nie będziesz cierpieć z powodu spadku wydajności wynikającego z „wymuszonego przydziału”.

Mateen Ulhaq
źródło
8

Myślę, że używanie transmisji w numpy jest najlepsze i szybsze

Zrobiłem porównanie w następujący sposób

import numpy as np
b = np.random.randn(1000)
In [105]: %timeit c = np.tile(b[:, newaxis], (1,100))
1000 loops, best of 3: 354 µs per loop

In [106]: %timeit c = np.repeat(b[:, newaxis], 100, axis=1)
1000 loops, best of 3: 347 µs per loop

In [107]: %timeit c = np.array([b,]*100).transpose()
100 loops, best of 3: 5.56 ms per loop

około 15 razy szybciej dzięki transmisji

smartkevin
źródło
Możesz indeksować za pomocą, Noneaby zrobić to samo.
DanielSank
co to jest newaxis ?!
dreab
np.newaxis to alias dla None
john ktejik
powtórka była szybsza: 5,56 ms = 5560 µs
Augusto Fadel
4

Jednym czystym rozwiązaniem jest użycie funkcji produktu zewnętrznego NumPy z wektorem jedynek:

np.outer(np.ones(n), x)

daje npowtarzające się wiersze. Zmień kolejność argumentów, aby uzyskać powtarzające się kolumny. Aby uzyskać taką samą liczbę wierszy i kolumn, możesz to zrobić

np.outer(np.ones_like(x), x)
Jon Deaton
źródło
3

Możesz użyć

np.tile(x,3).reshape((4,3))

kafelek wygeneruje powtórzenia wektora

a zmiana kształtu nada mu pożądany kształt

licznik fasoli
źródło
1

Jeśli masz ramkę danych pandy i chcesz zachować dtypy, nawet kategorie, jest to szybki sposób:

import numpy as np
import pandas as pd
df = pd.DataFrame({1: [1, 2, 3], 2: [4, 5, 6]})
number_repeats = 50
new_df = df.reindex(np.tile(df.index, number_repeats))
The Unfun Cat
źródło
-1
import numpy as np
x=np.array([1,2,3])
y=np.multiply(np.ones((len(x),len(x))),x).T
print(y)

plony:

[[ 1.  1.  1.]
 [ 2.  2.  2.]
 [ 3.  3.  3.]]
kibitzforu
źródło