"Klonen" von Zeilen- oder Spaltenvektoren


154

Manchmal ist es nützlich, einen Zeilen- oder Spaltenvektor in eine Matrix zu "klonen". Mit Klonen meine ich das Konvertieren eines Zeilenvektors wie z

[1,2,3]

In eine Matrix

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

oder ein Spaltenvektor wie

[1
 2
 3
]

in

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

In Matlab oder Oktave ist dies ziemlich einfach:

 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

Ich möchte dies in numpy wiederholen, aber erfolglos

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.]])

Warum In [16]funktionierte die erste Methode ( ) nicht? Gibt es eine Möglichkeit, diese Aufgabe in Python eleganter zu erfüllen?


6
Beachten Sie in Matlab, dass die Verwendung viel schneller ist repmat: repmat([1 2 3],3,1)oderrepmat([1 2 3].',1,3)
Luis Mendo

Oktave hat auch repmat.
ma11hew28

Für diejenigen, die ähnliche Aktionen mit einem Pandas-Datenrahmen durchführen möchten, klicken Sie tile_df hier
zelusp

Antworten:


79

Hier ist eine elegante, pythonische Methode:

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

Das Problem mit [16]scheint zu sein, dass die Transponierung keine Auswirkung auf ein Array hat. Sie möchten wahrscheinlich stattdessen eine Matrix:

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

1
(transpose arbeitet für 2D - Arrays, beispielsweise für das Quadrat eines in dem Beispiel oder beim Drehen in eine (N,1)-Form Array .reshape(-1, 1))
Mark

34
Dies ist sehr ineffizient. Verwenden Sie numpy.tilewie in der Antwort von pv. Gezeigt .
David Heffernan

302

Verwendung numpy.tile:

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

oder zum Wiederholen von Spalten:

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

16
Upvote! Auf meinem System ist die tileMethode für einen Vektor mit 10000 Elementen, die 1000-mal wiederholt wurden, 19,5-mal schneller als die Methode in der aktuell akzeptierten Antwort (unter Verwendung der Multiplikationsoperator-Methode).
Dr. Jan-Philip Gehrcke

1
Im zweiten Abschnitt ("Wiederholen von Spalten") können Sie erklären, was der zweite Satz eckiger Klammern bewirkt, dh [[1,2,3]]
Ant

@Ant es wird ein 2D-Array mit der Länge 1 auf der ersten Achse (vertikal auf Ihrem Bildschirm) und der Länge 3 auf der zweiten Achse (horizontal auf Ihrem Bildschirm) erstellt. Durch das Transponieren hat es dann die Länge 3 in der ersten Achse und die Länge 1 in der zweiten Achse. Eine Kachelform (1, 3)kopiert diese Spalte dreimal, weshalb die Zeilen des Ergebnisses jeweils ein einzelnes Element enthalten.
BallpointBen

Dies sollte die akzeptierte Antwort sein, da Sie jeden bereits initialisierten Vektor übergeben können, während der akzeptierte nur funktionieren kann, wenn Sie das Komma hinzufügen, während Sie den Vektor initialisieren. Vielen Dank !
Yohan Obadia

Ich kann dies nicht für eine 2d zu 3d Lösung zum
Laufen bringen

41

Zunächst ist zu beachten , dass mit numpy des Rundfunk ist es in der Regel nicht erforderlich , Zeilen und Spalten - Operationen zu duplizieren. Siehe dies und das für Beschreibungen.

Aber um dies zu tun, sind Wiederholung und neue Achse wahrscheinlich der beste Weg

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

Dieses Beispiel bezieht sich auf einen Zeilenvektor, aber die Anwendung auf einen Spaltenvektor ist hoffentlich offensichtlich. Wiederholung scheint dies gut zu buchstabieren, aber Sie können es auch wie in Ihrem Beispiel durch Multiplikation tun

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.]])

5
newaxis hat den zusätzlichen Vorteil, dass die Daten erst dann kopiert werden, wenn dies erforderlich ist. Wenn Sie dies tun, um zu multiplizieren oder einem anderen 3x3-Array hinzuzufügen, ist die Wiederholung nicht erforderlich. Informieren Sie sich über Numpy Broadcasting, um auf die Idee zu kommen.
AFoglia

@ AFoglia - Guter Punkt. Ich habe meine Antwort aktualisiert, um darauf hinzuweisen.
Tom10

1
Welche Vorteile np.repeatbietet die Verwendung von vs np.tile?
Mrgloom

@mrgloom: Keine, meistens für diesen Fall. Für ein kleines 1D-Array sind sie ähnlich und es gibt keinen signifikanten Unterschied / Nutzen / Vorteil / etc. Persönlich finde ich die Symmetrie zwischen dem Klonen von Zeilen und Spalten intuitiver und ich mag die für Kacheln erforderliche Transponierung nicht, aber es ist nur eine Frage des Geschmacks. Die Antwort von Mateen Ulhaq besagt auch, dass die Wiederholung schneller ist, dies kann jedoch vom genauen Anwendungsfall abhängen, der berücksichtigt wird, obwohl die Wiederholung der C-Funktionalität viel näher kommt und daher wahrscheinlich etwas schneller bleiben wird. In 2D haben sie unterschiedliche Verhaltensweisen, daher ist es dort wichtig.
Tom10

12

Lassen:

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

Kostenfreie Zuweisungen

Eine Ansicht benötigt keinen zusätzlichen Speicher. Somit sind diese Erklärungen augenblicklich:

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

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

Erzwungene Zuordnung

Wenn Sie möchten, dass sich der Inhalt im Speicher befindet:

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

Alle drei Methoden sind ungefähr gleich schnell.

Berechnung

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

Alle drei Methoden sind ungefähr gleich schnell.


Fazit

Wenn Sie vor einer Berechnung replizieren möchten, sollten Sie eine der "Zero-Cost-Allocation" -Methoden verwenden. Sie werden nicht die Leistungsstrafe der "erzwungenen Zuweisung" erleiden.


8

Ich denke, die Sendung in Numpy zu verwenden ist das Beste und schneller

Ich habe einen Vergleich wie folgt durchgeführt

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

etwa 15 mal schneller mit Broadcast


Sie können mit indizieren None, um dasselbe zu tun.
DanielSank

Was ist Newaxis?!
Dreab

np.newaxis ist ein Alias ​​für None
John Ktejik

Wiederholung war schneller: 5,56 ms = 5560 µs
Augusto Fadel

4

Eine saubere Lösung besteht darin, die Außenproduktfunktion von NumPy mit einem Vektor von Einsen zu verwenden:

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

gibt sich nwiederholende Zeilen. Ändern Sie die Argumentreihenfolge, um sich wiederholende Spalten zu erhalten. Um eine gleiche Anzahl von Zeilen und Spalten zu erhalten, können Sie dies tun

np.outer(np.ones_like(x), x)

3

Sie können verwenden

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

Kachel erzeugt die Wiederholungen des Vektors

und Umformung gibt es die Form, die Sie wollen


1

Wenn Sie einen Pandas-Datenrahmen haben und die d-Typen, auch die Kategorien, beibehalten möchten, ist dies eine schnelle Möglichkeit:

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

-1
import numpy as np
x=np.array([1,2,3])
y=np.multiply(np.ones((len(x),len(x))),x).T
print(y)

Ausbeuten:

[[ 1.  1.  1.]
 [ 2.  2.  2.]
 [ 3.  3.  3.]]
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.