Arrays unterschiedlicher Form arbeiten zusammen
Bisher haben wir Arrays gleicher Form kombiniert. Broadcasting ist das Regelwerk, mit dem NumPy Arrays unterschiedlicher Form ohne Kopieren der Daten verknüpft. Der einfachste Fall ist ein Skalar: a + 10 addiert 10 zu jedem Element, als wäre 10 auf die Form von a gedehnt worden. Dasselbe Dehnen funktioniert zwischen Arrays: Ein Vektor aus drei Zahlen lässt sich zu jeder Zeile einer Matrix mit drei Spalten addieren, ein Wert pro Spalte. So zieht man den Mittelwert jeder Spalte ab, skaliert jeden Wert mit seiner Volatilität oder baut eine Tabelle aller paarweisen Differenzen ohne eine einzige Schleife.
import numpy as np
a = np.array([[1.0, 2.0, 3.0],
[4.0, 5.0, 6.0]]) # shape (2, 3)
print(a + 10) # a scalar is stretched to every element
# [[11. 12. 13.]
# [14. 15. 16.]]
per_column = np.array([1, 10, 100]) # shape (3,): one value per column
print(a * per_column)
# [[ 1. 20. 300.]
# [ 4. 50. 600.]]
print(a - a.mean(axis=0)) # subtract each column's mean
# [[-1.5 -1.5 -1.5]
# [ 1.5 1.5 1.5]]Die Regel: Formen von rechts vergleichen
NumPy richtet die beiden Formen von rechts aus. An jeder Position müssen beide Größen gleich sein, oder eine muss 1 sein (sie wird gedehnt), oder ein Array hat dort schlicht keine Dimension (wird als 1 behandelt). Die Formen (2, 3) und (3,) funktionieren: 3 trifft auf 3, und die fehlende Dimension wird über die 2 Zeilen gedehnt. Die Formen (2, 3) und (2,) scheitern, weil 3 auf 2 trifft: NumPy löst ValueError: operands could not be broadcast together aus. Die Ergebnisform nimmt an jeder Position die größere Größe.
import numpy as np
a = np.ones((2, 3))
print((a + np.ones(3)).shape) # (2, 3) 3 meets 3
try:
a + np.ones(2) # 3 meets 2: no match
except ValueError as error:
print(error) # operands could not be broadcast together with shapes (2,3) (2,)
# Size 1 stretches: (3, 1) with (1, 4) gives (3, 4)
print((np.ones((3, 1)) + np.ones((1, 4))).shape) # (3, 4)Spaltenvektoren und Paartabellen
Ein 1-D-Array der Form (n,) richtet sich immer an der letzten Achse aus und verhält sich wie eine Zeile. Um einen Wert pro Zeile anzuwenden, brauchen Sie eine Spalte der Form (n, 1): schreiben Sie x[:, np.newaxis] oder x.reshape(-1, 1). Eine Spalte mit einer Zeile zu kombinieren baut eine ganze Tabelle: x[:, None] - x[None, :] ist die n×n-Matrix aller Differenzen x[i] - x[j], und corr * vols[:, None] * vols[None, :] macht aus einer Korrelations- eine Kovarianzmatrix. Vorsicht vor dem umgekehrten Fehler: Die Addition der Formen (3,) und (3, 1) ergibt stillschweigend eine 3×3-Tabelle, keinen Vektor aus drei Zahlen.
import numpy as np
x = np.array([1.0, 2.0, 4.0])
print(x[:, np.newaxis].shape, x.reshape(-1, 1).shape, x[None, :].shape) # (3, 1) (3, 1) (1, 3)
# All pairwise differences x[i] - x[j]
print(x[:, None] - x[None, :])
# [[ 0. -1. -3.]
# [ 1. 0. -2.]
# [ 3. 2. 0.]]
# One factor per row needs a column
a = np.array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
print(a * np.array([1.0, 2.0])[:, None])
# [[ 1. 2. 3.]
# [ 8. 10. 12.]]
# Correlation matrix to covariance matrix
corr = np.array([[1.0, 0.5], [0.5, 1.0]])
vols = np.array([0.2, 0.3])
print(corr * vols[:, None] * vols[None, :])
# [[0.04 0.03]
# [0.03 0.09]]
print((np.ones(3) + np.ones((3, 1))).shape) # (3, 3) a silent table, not a vectorimport numpy as np
# Standardise every column at once: (x - mean) / std, column by column
a = np.array([[1.0, 10.0],
[2.0, 20.0],
[3.0, 30.0]])
z = (a - a.mean(axis=0)) / a.std(axis=0) # (3, 2) - (2,) -> (3, 2), then divided by (2,)
print(z.round(3).tolist()) # [[-1.225, -1.225], [0.0, 0.0], [1.225, 1.225]]
print(z.mean(axis=0).round(9).tolist()) # [0.0, 0.0]
# keepdims keeps the reduced axis, so the result can be broadcast back by row too
row_share = a / a.sum(axis=1, keepdims=True)
print(row_share.sum(axis=1).tolist()) # [1.0, 1.0, 1.0]result.shape. Ein (n, n)-Ergebnis, wo (n,) erwartet wurde, oder eine Tabelle mit Milliarden Einträgen aus zwei langen Vektoren, ist das klassische Zeichen für ein fehlendes oder überzähliges [:, None]. Das Ergebnis wird im Speicher aufgebaut: Zwei Vektoren mit 100 000 Werten ergeben eine Tabelle mit 10 Milliarden Einträgen.- Formen werden von rechts ausgerichtet: Größen müssen gleich oder 1 (oder fehlend) sein; das Ergebnis nimmt die größere.
(n,)verhält sich wie eine Zeile; eine Spalte erhalten Sie mitx[:, None]oderx.reshape(-1, 1).- Spalte mit Zeile = Tabelle:
x[:, None] - x[None, :]; prüfen Sieresult.shape, Abweichungen scheitern mitValueError.
Als Nächstes: pandas-Series, eindimensionale Arrays, die zu jedem Wert ein Label tragen.
Lab: Was bewertet wird
- Mit
demean_columnsjede Spalte zentrieren25 Pkt. - Mit
scale_rowsjeder Zeile ihren eigenen Faktor geben25 Pkt. - Mit
pairwise_differencesdie Tabelle aller Differenzen bauen25 Pkt. - Mit
covariance_from_correlationKorrelationen in Kovarianzen umwandeln25 Pkt.