Numerische Operationen auf Numpy-Arrays

Operatoren in einer fraktalen Darstellung



In unserem Python-Tutorial haben wir viele Operatoren gesehen. Darüber haben wir dort gelernt, wie man Operatoren mittels "magischer Funktionen" überladen kann. Wir wissen nun, dass wir beispielsweise das "+"-Operatorzeichen nutzen können, um numerische Werte zu addieren oder Strings und Listen zu konkatenieren:

42 + 5

"Python ist eine der besten " + "oder die beste Programmiersprache!"

In dieser Einführung werden wir nun lernen, dass auch in Numpy die Operatorzeichen entsprechend überladen sind, sodass wir sie in "natürlicher" Weise nutzen können.

So können wir beispielsweise Skalare zu Arrays addieren, d.h. der Skalar wird zu jeder Komponente addiert. Das Gleiche ist möglich für die Subtraktion, die Division, die Multiplikation und sogar für Funktionen wie Sinus, Kosinus und so weiter.

Selbstverständlich können wir auch all diese Operatoren auf zwei Arrays anwenden.



Operatoren und Skalare

Beginnen wir mit der skalaren Addition:



In [1]:
import numpy as np
lst = [2,3, 7.9, 3.3, 6.9, 0.11, 10.3, 12.9]
v = np.array(lst)
print(v + 2)
[  4.     5.     9.9    5.3    8.9    2.11  12.3   14.9 ]

Multiplikation, Subtraktion, Division und Exponentiation sind ebenso leicht zu bewerkstelligen wie die vorige Addition:

In [2]:
print(v * 2.2)
[  4.4     6.6    17.38    7.26   15.18    0.242  22.66   28.38 ]

In [3]:
print(v - 1.38)
[  0.62   1.62   6.52   1.92   5.52  -1.27   8.92  11.52]

In [4]:
print(v ** 2)
print(v ** 1.5)
[  4.00000000e+00   9.00000000e+00   6.24100000e+01   1.08900000e+01
   4.76100000e+01   1.21000000e-02   1.06090000e+02   1.66410000e+02]
[  2.82842712e+00   5.19615242e+00   2.22044815e+01   5.99474770e+00
   1.81248172e+01   3.64828727e-02   3.30564215e+01   4.63323753e+01]

Wir hatten dieses Beispiel mit einer Liste lst begonnen. Wie kann man zu einer numerischen Liste einen Skalar addieren, so wie wir es mit dem Array v getan hatten?


Zu diesem Zweck kann man natürlich eine for-Schleife nutzen. Im folgenden addieren wir 2 zu den Werten dieser Liste:

In [5]:
lst = [2,3, 7.9, 3.3, 6.9, 0.11, 10.3, 12.9]
res = []
for val in lst:
    res.append(val + 2)

print(res)
[4, 5, 9.9, 5.3, 8.9, 2.11, 12.3, 14.9]

Obwohl diese Lösung funktioniert, ist sie nicht elegant und pythonisch. Zu diesem Zweck nutzt man besser eine Listenabstraktion (englisch: list comprehension) statt der umständlichen Lösung mit for-Schleife. Wir empfehlen unser Kapitel über Listenabstraktion unseres Python-Tutorials, falls jemand mit der Thematik nicht oder nicht mehr vertraut sein sollte.

In [6]:
res = [ val + 2 for val in lst]
print(res)
[4, 5, 9.9, 5.3, 8.9, 2.11, 12.3, 14.9]

Obwohl wir bereits einen Zeitvergleich zwischen Numpy und reinem Python durchgeführt hatten, wollen wir dennoch auch diese beiden Ansätze vergleichen:

In [7]:
v = np.random.randint(0, 100, 1000)

%timeit v + 1
1000000 loops, best of 3: 1.92 µs per loop

In [8]:
lst = list(v)

%timeit [ val + 2 for val in lst]
1000 loops, best of 3: 581 µs per loop


Arithmetische Operationen auf zwei Arrays


Falls wir ein weiteres Array statt einem Skalar benutzen, werden die Elemente von beiden Arrays komponentenweise miteinander verknüpft:

In [9]:
import numpy as np

A = np.array([ [11, 12, 13], [21, 22, 23], [31, 32, 33] ])
B = np.ones((3,3))

print("Addition zweier Arrays: ")
print(A + B)

print("\nMultiplikation zweier Arrays: ")
print(A * (B + 1))
Addition zweier Arrays: 
[[ 12.  13.  14.]
 [ 22.  23.  24.]
 [ 32.  33.  34.]]

Multiplikation zweier Arrays: 
[[ 22.  24.  26.]
 [ 42.  44.  46.]
 [ 62.  64.  66.]]

"A * B" im vorigen Beispiel sollte keinesfalls mit der Matrizenmultiplikation verwechselt werden. Wie bereits gesagt werden in unserem Beispiel die Arrays nur komponentenweise multipliziert!



Matrix-Multiplikation und dot-Produkt

Zu diesem Zweck können wir das dot-Produkt verwenden. Benutzen wir die Arrays aus dem vorigen Beispiel, dann können wir die Matrizenmultiplikation wie folgt ausführen:

In [10]:
print(np.dot(A, B))
[[ 36.  36.  36.]
 [ 66.  66.  66.]
 [ 96.  96.  96.]]


Definition des dot-Produktes

Die Syntax der dot-Funktion sieht wie folgt aus:

dot(a, b, out=None)

Für 2-D-Arrays entspricht das dot-Produkt der Matrix-Multiplikation. Für 1-D-Arrays entspricht es dem inneren Produkt von Vektoren (ohne komplexe Konjugation). Für N Dimensionen wird das Summenprodukt über die letzte Achse von 'a' und die vorletzte Achse von 'b' gebildet.

Parameter Bedeutung
a Array oder Array-ähnliches Argument (z.B. Liste)
b Array oder Array-ähnliches Argument (z.B. Liste)
out 'out' ist ein optionaler Parameter, der genau identisch sein muss zu der Art von Array, die zurückgeliefert würde, wenn 'out' nicht benutzt worden wäre. Es handelt sich um ein Leistungsmerkmal. Falls die Bedingungen nicht erfüllt sind, wird eine Ausnahme erhoben, statt sich den Bedingungen anzupassen.

Die Funktion dot liefert das dot-Produkt von 'a' und 'b' zurück. Falls sowohl 'a' als auch 'b' Skalare sind oder beide eindimensionale Arrays, wird ein Skalar zurückgegeben, ansonsten wird ein Array zurückgegeben.

Sie erhebt einen ValueError, falls die Shape der letzten Dimension von 'a' nicht die gleiche Größe wie die Shape der zweitletzten Dimension von 'b' hat, d.h. es muss gelten a.shape[-1] == b.shape[-2].



Beispiele zum dot-Produkt

Wir beginnen mit den Fällen, in denen beide Argumente Skalare oder eindimensionale Arrays sind:

In [11]:
print(np.dot(3, 4))
x = np.array([3])
y = np.array([4])
print(x.ndim)
print(np.dot(x, y))

x = np.array([3, -2])
y = np.array([-4, 1])
print(np.dot(x, y))
12
1
12
-14

Schauen wir uns nun den zweidimensionalen Fall an:

In [12]:
A = np.array([ [1, 2, 3], 
               [3, 2, 1] ])
B = np.array([ [2, 3, 4, -2], 
               [1, -1, 2, 3],
               [1, 2, 3, 0] ])

# es muss gelten:
print(A.shape[-1] == B.shape[-2], A.shape[1]) 
print(np.dot(A, B))
True 3
[[ 7  7 17  4]
 [ 9  9 19  0]]

Aus dem vorigen Beispiel können wir lernen, dass die Anzahl der Spalten des ersten zweidimensionalen Arrays gleich der Anzahl der Zeilen des zweiten zweidimensionalen Arrays sein muss.


Das dot-Produkt im 3-dimensionalen Fall

Es wird ziemlich verzwickt, wenn wir 3-dimensionale Arrays als Argumente von dot benutzen. It's getting really vexing, if we use 3-dimensional arrays as the arguments of dot.

Im ersten Beispiel benutzen wir zwei symmetrische 3-dimensionale Arrays:

In [13]:
import numpy as np
X = np.array( [[[3, 1, 2],
                [4, 2, 2],
                [2, 4, 1]],

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

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

Y = np.array( [[[2, 3, 1],
                [2, 2, 4],
                [3, 4, 4]],
            
               [[1, 4, 1],
                [4, 1, 2],
                [4, 1, 2]],
            
               [[1, 2, 3],
                [4, 1, 1],
                [3, 1, 4]]])


R = np.dot(X, Y)

print("The shapes:")
print(X.shape)
print(Y.shape)
print(R.shape)

print("\nThe Result R:")
print(R)
The shapes:
(3, 3, 3)
(3, 3, 3)
(3, 3, 3, 3)

The Result R:
[[[[14 19 15]
   [15 15  9]
   [13  9 18]]

  [[18 24 20]
   [20 20 12]
   [18 12 22]]

  [[15 18 22]
   [22 13 12]
   [21  9 14]]]


 [[[16 21 19]
   [19 16 11]
   [17 10 19]]

  [[25 32 32]
   [32 23 18]
   [29 15 28]]

  [[13 18 12]
   [12 18  8]
   [11 10 17]]]


 [[[11 14 14]
   [14 11  8]
   [13  7 12]]

  [[17 23 19]
   [19 16 11]
   [16 10 22]]

  [[19 25 23]
   [23 17 13]
   [20 11 23]]]]

Um zu zeigen, wie das dot-Produkt im drei-dimensionalen Fall funktioniert, werden wir jedoch nun zwei nicht symmetrische drei-dimensionale Arrays im folgenden Beispiel benutzen:

In [14]:
import numpy as np
X = np.array(
    [[[3, 1, 2],
      [4, 2, 2]],

     [[-1, 0, 1],
      [1, -1, -2]],
     
     [[3, 2, 2],
      [4, 4, 3]],

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

Y = np.array(
    [[[2, 3, 1, 2, 1],
      [2, 2, 2, 0, 0],
      [3, 4, 0, 1, -1]],

     [[1, 4, 3, 2, 2],
      [4, 1, 1, 4, -3],
      [4, 1, 0, 3, 0]]])


R = np.dot(X, Y)



print("X.shape: ", X.shape, "   X.ndim: ", X.ndim)
print("Y.shape: ", Y.shape, "   Y.ndim: ", Y.ndim)
print("R.shape: ",     R.shape, "R.ndim: ", R.ndim)


print("\nThe result array R:\n")
print(R)
X.shape:  (4, 2, 3)    X.ndim:  3
Y.shape:  (2, 3, 5)    Y.ndim:  3
R.shape:  (4, 2, 2, 5) R.ndim:  4

The result array R:

[[[[ 14  19   5   8   1]
   [ 15  15  10  16   3]]

  [[ 18  24   8  10   2]
   [ 20  20  14  22   2]]]


 [[[  1   1  -1  -1  -2]
   [  3  -3  -3   1  -2]]

  [[ -6  -7  -1   0   3]
   [-11   1   2  -8   5]]]


 [[[ 16  21   7   8   1]
   [ 19  16  11  20   0]]

  [[ 25  32  12  11   1]
   [ 32  23  16  33  -4]]]


 [[[ 11  14   6   5   1]
   [ 14  11   8  15  -2]]

  [[ 17  23   5   9   0]
   [ 19  16  10  19   3]]]]

Schauen wir uns nun die folgenden Summen-Produkte an:

In [15]:
i = 0
for j in range(X.shape[1]):
    for k in range(Y.shape[0]):
        for m in range(Y.shape[2]):
            fmt = "    sum(X[{}, {}, :] * Y[{}, :, {}] :  {}"
            arguments = (i, j, k, m, sum(X[i, j, :] * Y[k, :, m]))
            print(fmt.format(*arguments))
    sum(X[0, 0, :] * Y[0, :, 0] :  14
    sum(X[0, 0, :] * Y[0, :, 1] :  19
    sum(X[0, 0, :] * Y[0, :, 2] :  5
    sum(X[0, 0, :] * Y[0, :, 3] :  8
    sum(X[0, 0, :] * Y[0, :, 4] :  1
    sum(X[0, 0, :] * Y[1, :, 0] :  15
    sum(X[0, 0, :] * Y[1, :, 1] :  15
    sum(X[0, 0, :] * Y[1, :, 2] :  10
    sum(X[0, 0, :] * Y[1, :, 3] :  16
    sum(X[0, 0, :] * Y[1, :, 4] :  3
    sum(X[0, 1, :] * Y[0, :, 0] :  18
    sum(X[0, 1, :] * Y[0, :, 1] :  24
    sum(X[0, 1, :] * Y[0, :, 2] :  8
    sum(X[0, 1, :] * Y[0, :, 3] :  10
    sum(X[0, 1, :] * Y[0, :, 4] :  2
    sum(X[0, 1, :] * Y[1, :, 0] :  20
    sum(X[0, 1, :] * Y[1, :, 1] :  20
    sum(X[0, 1, :] * Y[1, :, 2] :  14
    sum(X[0, 1, :] * Y[1, :, 3] :  22
    sum(X[0, 1, :] * Y[1, :, 4] :  2

Hoffentlich ist Ihnen aufgefallen, die die Werte die wir erzeugt haben den Elementen von R[0] entsprechen:

In [16]:
print(R[0])
[[[14 19  5  8  1]
  [15 15 10 16  3]]

 [[18 24  8 10  2]
  [20 20 14 22  2]]]

Dies bedeutet, dass wir das Array R auch über die Summen-Produkte hätten erzeugen können. Um dies zu "beweisen" werden wir im folgenden Beispiel ein Array R2 unter Benutzung der Summen-Produkte erzeugen. Anschließend prüfen wir, ob R2 gleich R ist.

In [17]:
R2 = np.zeros(R.shape, dtype=np.int)

for i in range(X.shape[0]):
    for j in range(X.shape[1]):
        for k in range(Y.shape[0]):
            for m in range(Y.shape[2]):
                R2[i, j, k, m] = sum(X[i, j, :] * Y[k, :, m])


print( np.array_equal(R, R2) )
True



Matrizen im Vergleich zu Zwei-Dimensionalen-Arrays

Manche haben wohl in den vorigen Abschnitten die zwei-dimensionalen Arrays von Numpy als Matrizen angesehen. Das ist prinzipiell in Ordnung, weil sie sich in den meisten Aspekten wie unsere mathematische Idee einer Matrix verhalten. Wie haben sogar gesehen, dass wir eine Matrix-Multiplikation auf ihnen durchführen konnten. Dennoch gibt es einen feinen Unterschied. Es gibt "echte" Matrizen in Numpy. Sie sind eine Teilmenge der zwei-dimensionalen Arrays. Wir können jedes zwei-dimensionale Array in eine Matrix mit der mat-Funktion wandeln. Der wesentliche Unterschied zeigt sich, wenn wir Arrays und Matrizen mit "*" miteinander verknüpfen. Im Fall von zwei Matrizen haben wir eine Matrix-Multiplikation und 2-dimensionalen Arrays werden elementweise miteinander multipliziert, wie wir bereits gesehen hatten:

In [18]:
import numpy as np

A = np.array([ [1, 2, 3], [2, 2, 2], [3, 3, 3] ])
B = np.array([ [3, 2, 1], [1, 2, 3], [-1, -2, -3] ])

R = A * B
print(R)
[[ 3  4  3]
 [ 2  4  6]
 [-3 -6 -9]]

In [19]:
MA = np.mat(A)
MB = np.mat(B)

R = MA * MB
print(R)
[[ 2  0 -2]
 [ 6  4  2]
 [ 9  6  3]]


Vergleichsoperatoren

Wir kennen bereits Vergleichsoperatoren in Python, die wir auf Integer, Floats oder Strings angewendet haben. Sie liefern True oder False zurück. Vergleichen wir zwei Arrays miteinander, erhalten wir keinen "einfachen" Boolschen Wert zurück. Die Vergleiche werden elementweise durchgeführt. Dies bewirkt, dass wir ein Boolsches Array als Rückgabewert erhalten:

In [20]:
import numpy as np

A = np.array([ [11, 12, 13], [21, 22, 23], [31, 32, 33] ])
B = np.array([ [11, 102, 13], [201, 22, 203], [31, 32, 303] ])

A == B
Out[20]:
array([[ True, False,  True],
       [False,  True, False],
       [ True,  True, False]], dtype=bool)

Man kann aber auch Arrays vollständig auf Gleichheit überprüfen. Dazu benutzen wir array_equal. array_equal liefert True zurück, falls zwei Arrays die gleiche Shape haben und alle Elemente gleich sind. Ansonsten wird False zurückgeliefert.

In [21]:
print(np.array_equal(A, B))
print(np.array_equal(A, A))
False
True





Logische Operatoren

Wir können Arrays auch komponentenweise auf ein logisches 'oder' und logisches 'und' vergleichen. Dazu gibt es die Funktionen 'logical_or' und 'logical_and'.

In [22]:
a = np.array([ [True, True], [False, False]])
b = np.array([ [True, False], [True, False]])

print(np.logical_or(a, b))
print(np.logical_and(a, b))
[[ True  True]
 [ True False]]
[[ True False]
 [False False]]



Anwendung von Operatoren auf Arrays mit unterschiedlicher Shape

Bis jetzt hatten wir die Basisoperatoren ("+", "*" usw.) in zwei verschiedenen Fällen angewendet:

  • ein Operator wurde auf ein Array und ein Skalar angewendet
  • ein Operator wurde auf zwei Arrays mit der gleichen Shape angewendet.

Im Folgenden werden wir sehen, dass wir Operatoren auch auf Arrays mit verschiedenen Shapes anwenden können. Dies funktioniert jedoch nur unter gewissen Bedingungen.



Broadcasting

Unter dem Namen "Broadcasting" stellt Numpy einen mächtigen Mechanismus zur Verfügung, der es erlaubt arithmetische Operatoren auch auf Arrays mit unterschiedlicher Shape anzuwenden. Dies bedeutet, dass wir einen Operator auf ein Array mit einer kleineren und einer größeren Shape anwenden. Um die entsprechende Operation durchzuführen wird nun das "kleinere" Array entweder in eine "passende Form" transformiert oder wird mehrmals auf das "größere" angewendet. In anderen Worten: Unter bestimmten Bedingungen erfolgt ein "Broadcast" des kleineren Arrays, bis es die gleiche Shape wie das größere hat.

Mit Hilfe des Broadcasting können wir Schleifen in unseren Python-Programmen vermeiden. Die Schleifenbildung erfolgt dann implizit in der Numpy-Implementierung, d.h. in C. Dadurch vermeiden wir außerdem unnötige Kopien von unseren Daten.

Wir demonstrieren die Arbeitsweise von Broadcasting in drei einfachen und anschaulichen Beispielen.

Erstes Beispiel zu Broadcasting:

In [23]:
import numpy as np

A = np.array([ [11, 12, 13], [21, 22, 23], [31, 32, 33] ])
B = np.array([1, 2, 3])

print("Multiplikation mit Broadcasting: ")
print(A * B)
print("... und nun die Addition mit Broadcasting: ")
print(A + B)
Multiplikation mit Broadcasting: 
[[11 24 39]
 [21 44 69]
 [31 64 99]]
... und nun die Addition mit Broadcasting: 
[[12 14 16]
 [22 24 26]
 [32 34 36]]

Das folgende Diagramm illustriert die Arbeitsweise von Broadcasting:

Arbeitsweise von Broadcasting, Diagramm 1

B wird benutzt als wäre es wie folgt aufgebaut:

In [24]:
B = np.array([[1, 2, 3],] * 3)
print(B)
[[1 2 3]
 [1 2 3]
 [1 2 3]]

Zweites Beispiel:

Für dieses Beispiel müssen wir wissen, wie man einen Zeilenvektor in einen Spaltenvektor wandelt:

In [25]:
B = np.array([1, 2, 3])
B[:, np.newaxis]
Out[25]:
array([[1],
       [2],
       [3]])

Nun können wir die Multiplikation mittels Broadcasting durchführen:

In [26]:
A * B[:, np.newaxis]
Out[26]:
array([[11, 12, 13],
       [42, 44, 46],
       [93, 96, 99]])

B wird benutzt als wäre es wie folgt aufgebaut:

In [27]:
np.array([[1, 2, 3],] * 3).transpose()
Out[27]:
array([[1, 1, 1],
       [2, 2, 2],
       [3, 3, 3]])
Arbeitsweise von Broadcasting, Diagramm 2

Drittes Beispiel:

In [28]:
A = np.array([10, 20, 30])
B = np.array([1, 2, 3])
A[:, np.newaxis]
Out[28]:
array([[10],
       [20],
       [30]])
In [29]:
A[:, np.newaxis] * B
Out[29]:
array([[10, 20, 30],
       [20, 40, 60],
       [30, 60, 90]])
Arbeitsweise von Broadcasting, Diagramm 3

Ohne Broadcasting

Wir können es auch ohne Broadcasting bewerkstelligen:

In [30]:
import numpy as np

A = np.array([ [11, 12, 13], [21, 22, 23], [31, 32, 33] ])

B = np.array([1, 2, 3])

B = B[np.newaxis, :]
B = np.concatenate((B, B, B))

print("Multiplikation: ")
print(A * B)
print("... und nun wieder die Addition: ")
print(A + B)
Multiplication: 
[[11 24 39]
 [21 44 69]
 [31 64 99]]
... and now addition again: 
[[12 14 16]
 [22 24 26]
 [32 34 36]]

Benutzung von 'tile':

In [31]:
import numpy as np

A = np.array([ [11, 12, 13], [21, 22, 23], [31, 32, 33] ])

B = np.tile(np.array([1, 2, 3]), (3, 1))

print(B)

print("Multiplikation: ")
print(A * B)
print("... und nun wieder die Addition: ")
print(A + B)
[[1 2 3]
 [1 2 3]
 [1 2 3]]
Multiplication: 
[[11 24 39]
 [21 44 69]
 [31 64 99]]
... and now addition again: 
[[12 14 16]
 [22 24 26]
 [32 34 36]]



Distanzmatrix

In der Mathematik, der Informatik und insbesondere in der Graph-Theorie, versteht man unter der Distanzmatrix ein zweidimensionales Array, das die "Entfernungen" zwischen den Elementen einer Menge paarweise beinhaltet. Die Größe dieses zweidimensionales Arrays ist n x n, falls die Menge aus n Elementen besteht.

Ein praktisches Beispiel einer Distanzmatrix ist eine Matrix mit den Entfernungen zwischen geografischen Lokationen, in unserem Beispiel europäische Städte:

In [32]:
cities = ["Barcelona", "Berlin", "Brüssel", "Bukarest",
          "Budapest", "Kopenhagen", "Dublin", "Hamburg", "Istanbul",
          "Kiew", "London", "Madrid", "Mailand", "Moskau", "München",
          "Paris", "Prag", "Rome", "Sankt Petersburg", 
          "Stockholm", "Wien", "Warschau"]

dist2barcelona = [0,  1498, 1063, 1968, 
                  1498, 1758, 1469, 1472, 2230, 
                  2391, 1138, 505, 725, 3007, 1055, 
                  833, 1354, 857, 2813, 
                  2277, 1347, 1862]

dists =  np.array(dist2barcelona[:12])
print(dists)
print(np.abs(dists - dists[:, np.newaxis]))
[   0 1498 1063 1968 1498 1758 1469 1472 2230 2391 1138  505]
[[   0 1498 1063 1968 1498 1758 1469 1472 2230 2391 1138  505]
 [1498    0  435  470    0  260   29   26  732  893  360  993]
 [1063  435    0  905  435  695  406  409 1167 1328   75  558]
 [1968  470  905    0  470  210  499  496  262  423  830 1463]
 [1498    0  435  470    0  260   29   26  732  893  360  993]
 [1758  260  695  210  260    0  289  286  472  633  620 1253]
 [1469   29  406  499   29  289    0    3  761  922  331  964]
 [1472   26  409  496   26  286    3    0  758  919  334  967]
 [2230  732 1167  262  732  472  761  758    0  161 1092 1725]
 [2391  893 1328  423  893  633  922  919  161    0 1253 1886]
 [1138  360   75  830  360  620  331  334 1092 1253    0  633]
 [ 505  993  558 1463  993 1253  964  967 1725 1886  633    0]]



3-Dimensionales Broadcasting

In [33]:
A = np.array([ [[3, 4, 7], [5, 0, -1] , [2, 1, 5]],
      [[1, 0, -1], [8, 2, 4], [5, 2, 1]],
      [[2, 1, 3], [1, 9, 4], [5, -2, 4]]])

B = np.array([ [[3, 4, 7], [1, 0, -1], [1, 2, 3]] ])

B * A
Out[33]:
array([[[ 9, 16, 49],
        [ 5,  0,  1],
        [ 2,  2, 15]],

       [[ 3,  0, -7],
        [ 8,  0, -4],
        [ 5,  4,  3]],

       [[ 6,  4, 21],
        [ 1,  0, -4],
        [ 5, -4, 12]]])

Wir werden die folgenden Transformationen auch in unserem Kapitel über chapter on Images Manipulation and Processing benutzen:

In [34]:
B = np.array([1, 2, 3])

B = B[np.newaxis, :]
print(B.shape)
B = np.concatenate((B, B, B)).transpose()
print(B.shape)
B = B[:, np.newaxis]
print(B.shape)
print(B)

print(A * B)
(1, 3)
(3, 3)
(3, 1, 3)
[[[1 1 1]]

 [[2 2 2]]

 [[3 3 3]]]
[[[ 3  4  7]
  [ 5  0 -1]
  [ 2  1  5]]

 [[ 2  0 -2]
  [16  4  8]
  [10  4  2]]

 [[ 6  3  9]
  [ 3 27 12]
  [15 -6 12]]]