Einführung in NumPy Universal Functions

NumPyBeginner
Jetzt üben

Einführung

In diesem Lab lernen Sie die Grundlagen von NumPy's Universal Functions, allgemein bekannt als ufuncs. Ufuncs sind ein Eckpfeiler des Hochleistungsrechnens in Python und ermöglichen schnelle, elementweise Operationen auf gesamten Datenarrays. Wir werden grundlegende Arithmetik, das mächtige Konzept des Broadcasting, Aggregationsmethoden und die Kontrolle über die Datentypen Ihrer Ergebnisse behandeln. Am Ende dieses Labs werden Sie in der Lage sein, ufuncs zu verwenden, um saubereren und effizienteren Code für die Datenverarbeitung zu schreiben.

Grundlegende Arithmetik mit Ufuncs

Im Kern führen ufuncs elementweise Operationen durch. Das bedeutet, dass, wenn Sie eine Operation auf zwei Arrays anwenden, die Operation auf jedes Paar entsprechender Elemente angewendet wird. Die gebräuchlichsten ufuncs sind die Standard-Arithmetikoperatoren wie +, -, * und /.

Beginnen wir mit einer einfachen Addition zweier NumPy-Arrays.

Öffnen Sie zuerst die Datei ufunc_examples.py im Dateiexplorer auf der linken Seite. Ersetzen Sie den vorhandenen Inhalt durch den folgenden Code. Dieser Code importiert NumPy, erstellt zwei Arrays und addiert sie.

import numpy as np

## Create two arrays
arr1 = np.array([0, 2, 3, 4])
arr2 = np.array([1, 1, -1, 2])

## The '+' operator is a ufunc that adds the arrays element-wise
result = arr1 + arr2

## Print the result
print("Step 1 Result:")
print(result)

Nachdem Sie den Code hinzugefügt haben, speichern Sie die Datei. Führen Sie nun das Skript im Terminal aus, um die Ausgabe zu sehen.

python ufunc_examples.py

Sie sollten das Ergebnis der elementweisen Addition sehen.

Step 1 Result:
[1 3 2 6]

Dies demonstriert das grundlegende Verhalten einer ufunc: arr1[0] wird zu arr2[0] addiert, arr1[1] zu arr2[1], und so weiter, wodurch ein neues Array mit den Ergebnissen erzeugt wird.

Broadcasting in Aktion

Broadcasting ist ein leistungsstarker Mechanismus, der es NumPy ermöglicht, bei arithmetischen Operationen mit Arrays unterschiedlicher Formen zu arbeiten. Intern "broadcastet" NumPy das kleinere Array über das größere Array, sodass sie kompatible Formen haben.

Ein gängiges Beispiel ist die Multiplikation jedes Elements eines Arrays mit einer einzelnen Zahl. Sehen wir uns auch einen komplexeren Fall an, bei dem ein 1D-Array über ein 2D-Array gebroadcastet wird.

Ändern Sie Ihre Datei ufunc_examples.py. Fügen Sie den folgenden Code am Ende des Skripts hinzu.

## --- Appended code for Step 2 ---

## Broadcasting a scalar to an array
arr1 = np.array([1, 2, 3])
scalar_result = arr1 * 10
print("\nStep 2 Result (Scalar Broadcast):")
print(scalar_result)

## Broadcasting a 1D array to a 2D array
arr2d = np.array([[1], [2], [3]]) ## Shape (3, 1)
arr1d = np.array([1, 2, 3])      ## Shape (3,)
broadcast_result = arr2d * arr1d
print("\nStep 2 Result (Array Broadcast):")
print(broadcast_result)

Speichern Sie die Datei und führen Sie sie erneut vom Terminal aus.

python ufunc_examples.py

Sie sehen die Ausgabe für Schritt 1 und Schritt 2.

Step 1 Result:
[1 3 2 6]

Step 2 Result (Scalar Broadcast):
[10 20 30]

Step 2 Result (Array Broadcast):
[[1 2 3]
 [2 4 6]
 [3 6 9]]

Im zweiten Beispiel werden das 1D-Array arr1d (Form (3,)) und das 2D-Array arr2d (Form (3, 1)) vor der elementweisen Multiplikation zu einer gemeinsamen Form von (3, 3) gebroadcastet.

Arrays aggregieren mit .reduce()

Neben elementweisen Operationen verfügen ufuncs über spezielle Methoden zur Durchführung von Aggregationen. Die Methode .reduce() ist eine der nützlichsten. Sie wendet eine ufunc wiederholt entlang einer angegebenen Achse eines Arrays an, bis nur noch eine Dimension übrig ist.

Zum Beispiel ist np.add.reduce(arr) äquivalent zu np.sum(arr). Sehen wir uns an, wie es auf einem 2D-Array funktioniert.

Fügen Sie den folgenden Code zu Ihrer Datei ufunc_examples.py hinzu.

## --- Appended code for Step 3 ---

## Create a 3x3 array
arr = np.arange(9).reshape(3, 3)
print("\nStep 3 Original Array:")
print(arr)

## Reduce the array by summing along axis 1 (the columns)
## This will sum the elements in each row.
## For row 0: 0 + 1 + 2 = 3
## For row 1: 3 + 4 + 5 = 12
## For row 2: 6 + 7 + 8 = 21
reduced_result = np.add.reduce(arr, axis=1)

print("\nStep 3 Result (reduce on axis=1):")
print(reduced_result)

Speichern Sie die Datei und führen Sie sie aus.

python ufunc_examples.py

Die Ausgabe enthält nun die Ergebnisse dieses Schritts.

... (previous output) ...

Step 3 Original Array:
[[0 1 2]
 [3 4 5]
 [6 7 8]]

Step 3 Result (reduce on axis=1):
[ 3 12 21]

Wie Sie sehen können, hat .reduce() das Array entlang der angegebenen Achse kollabiert, indem die add-Operation auf seine Elemente angewendet wurde.

Ausgabedatentypen festlegen

NumPy bestimmt normalerweise den Datentyp des Ausgabearrays automatisch. Sie können jedoch den Ausgabedatentyp explizit mit dem Argument dtype festlegen. Dies ist nützlich, um die Speichernutzung zu steuern oder die numerische Präzision sicherzustellen.

Führen wir eine Reduktion mit Multiplikation durch und erzwingen, dass die Ausgabe eine Gleitkommazahl ist, obwohl die Eingabe ein Integer-Array ist.

Fügen Sie den folgenden Code am Ende von ufunc_examples.py hinzu.

## --- Appended code for Step 4 ---

## Use the same 3x3 array from Step 3
arr = np.arange(1, 10).reshape(3, 3) ## Using 1-9 to avoid multiplying by zero
print("\nStep 4 Original Array:")
print(arr)

## Reduce with multiplication, casting the output to float
## For row 0: 1 * 2 * 3 = 6
## For row 1: 4 * 5 * 6 = 120
## For row 2: 7 * 8 * 9 = 504
multiply_result = np.multiply.reduce(arr, axis=1, dtype=float)

print("\nStep 4 Result (multiply.reduce with dtype=float):")
print(multiply_result)

Speichern und führen Sie das Skript aus.

python ufunc_examples.py

Beachten Sie die Ausgabe für Schritt 4.

... (previous output) ...

Step 4 Original Array:
[[1 2 3]
 [4 5 6]
 [7 8 9]]

Step 4 Result (multiply.reduce with dtype=float):
[  6. 120. 504.]

Beachten Sie die nachgestellten Punkte (.) im Ausgabearray [ 6. 120. 504.]. Dies zeigt an, dass die Elemente nun Gleitkommazahlen sind, wie wir es mit dtype=float angegeben haben.

Ufunc-Verhalten überschreiben

Das ufunc-System von NumPy ist erweiterbar. Sie können eigene Array-ähnliche Objekte erstellen, die definieren, wie ufuncs auf ihnen operieren sollen. Dies ist eine fortgeschrittene Funktion, die typischerweise durch Unterklassenbildung von NumPy's ndarray und Überschreiben spezieller Methoden wie __add__ (für den +-Operator) realisiert wird.

Erstellen wir eine einfache benutzerdefinierte Array-Klasse, die eine Nachricht ausgibt, wann immer eine Addition darauf durchgeführt wird.

Fügen Sie diesen letzten Codeblock zu ufunc_examples.py hinzu.

## --- Appended code for Step 5 ---

## Define a custom array class by subclassing np.ndarray
class MyArray(np.ndarray):
    def __add__(self, other):
        print("\nStep 5: Custom add method called!")
        ## Call the original implementation from the parent class
        return super().__add__(other)

## Create an instance of our custom class
## We must use .view() to cast the ndarray to our custom class
my_arr = np.array([10, 20, 30]).view(MyArray)

## Perform addition, which will trigger our custom method
override_result = my_arr + 5

print("Step 5 Result (Overridden Ufunc):")
print(override_result)

Speichern Sie die Datei und führen Sie sie ein letztes Mal aus.

python ufunc_examples.py

Überprüfen Sie die endgültige Ausgabe.

... (previous output) ...

Step 5: Custom add method called!
Step 5 Result (Overridden Ufunc):
[15 25 35]

Sie können sehen, dass unsere benutzerdefinierte Nachricht vor dem Additionsergebnis ausgegeben wurde, was bestätigt, dass unsere __add__-Methode aufgerufen wurde. Dies zeigt die leistungsstarke Flexibilität des ufunc-Systems.

Zusammenfassung

In diesem Lab haben Sie die Grundlagen von NumPy's Universal Functions (ufuncs) kennengelernt. Wir begannen mit grundlegender elementweiser Arithmetik, die die Basis für vektorisierte Berechnungen bildet. Anschließend haben Sie Broadcasting erkundet, ein wichtiges Merkmal, das es NumPy ermöglicht, Operationen auf Arrays unterschiedlicher Formen durchzuführen. Wir haben auch behandelt, wie ufunc-Methoden wie .reduce() für die Datenaggregation verwendet werden und wie der Ausgabedatentyp mit dem dtype-Argument gesteuert wird. Schließlich haben Sie ein fortgeschrittenes Beispiel dafür gesehen, wie das ufunc-Verhalten durch Unterklassenbildung von np.ndarray angepasst werden kann. Mit diesen Fähigkeiten sind Sie nun besser gerüstet, um effizienten, lesbaren und leistungsstarken numerischen Code mit NumPy zu schreiben.