1 Numerical Methods in Extraterrestrial Physics

Werbung
1
Numerical Methods in Extraterrestrial Physics - Introduction to
Python II
2
Datei Input/Output
Beim Arbeiten mit dem Computer ist es unerlsslich auf Dateien zu zugreifen um z.B. Messdaten einlesen oder
Ergebnisse speichern zu können. Hierzu gibt es die eingebaute Funktion ’open’ die ein Objekt erzeugt über dass
die gewählte Datei bearbeitet werden kann.
# Oeffnen einer Datei mit L e s e z u g r i f f
>>> file1 = open ( " datei . txt " ," r " )
# Erzeugen einer Datei mit S c h r e i b z u g r i f f
>>> file1 = open ( " datei . txt " ," w " ) # V o r s i c h t ! Eine ggf . b e s t e h e n d e Datei " datei . txt "
# wird g e l o e s c h t
# Oeffnen / erzeuge n einer Datei mit S c h r e i b z u g r i f f
>>> file1 = open ( " datei . txt " ," a " ) # Falls Datei " datei . txt " e x i s t i e r t wird sie
# g e o e f f n e t mit der M o e g l i c h k e i t Daten ans Ende
# der Datei a n z u h a e n g e n . A n s o n s t e n wird eine
# neue Dtai erzeugt
>>> file1 . close ()
# S c h l i e s s t die a k t u e l l e Datei
Das oben erzeugte Objekt ’file1’ kann jetzt dazu verwendet werden um aus der Datei zu lesen oder in sie zu
schreiben. Das auslesen erfolgt über die Methode ’readline’ (mehr zu Objekten und Methoden folgt spter).
datei . txt ascii - datei
x y z
1 2 3
>>> file1 = open ( " datei . txt " ," r " )
>>> file1 . readline ()
# gibt die 1. Zeile aus datei . txt als String zurueck
’x y z {\ backslash } n ’
>>> file1 . readline ()
# gibt die 2. Zeile aus datei . txt als String zurueck
’\ n ’
>>> file1 . readline ()
# gibt die 3. Zeile aus datei . txt als String zurueck
’1 2 3 ’
Beachte : Nach dem Ende der Datei gibt ’ readline ’ einen leeren String zurueck
>>> file1 . readline ()
# gibt einen leeren String zurueck
’’
# A n m e r k u n g : die S t r i n g m e t h o d e ’ split ’ kann dafuer v e r w e n d e t werden e i n g e l e s e n e
# Zeilen weiter zu v e r a r b e i t e n . Hierbei sind auch die T y p e n k o n v e r t i e r u n g e n
# int ( arg ) , float ( arg ) h i l f r e i c h .
>>> ’1 2 3 ’. split ()
[ ’1 ’ , ’2 ’ , ’3 ’]
>>> int ( ’1 2 3 ’. split ()[0])
1
Um in ’file1’ zu schreiben gibt es die Methode ’write’.
>>> file1 = open ( " datei2 . txt " ," w " ) # erzeugt leere Datei mit S c h r e i b z u g r i f f
>>> file1 . write ( " x y z " )
# s c h r e i b t ’x y z ’ ans Ende von ’ datei2 . txt ’
>>> file1 . write ( " 1 2 3 " )
# s c h r e i b t ’1 2 3 ’ ans Ende von ’ datei2 . txt ’
# Beachte : Z e i l e n u m b r u e c h e muessen e x p l i z i t m i t g e s c h r i e b e n werden
# datei2 . txt haette f o l g e n d e n Inhalt
x y z1 2 3
>>> file1 = open ( " datei2 . txt " ," w " ) # Die b e s t e h e n d e Datei ’ datei2 . txt ’
>>> file1 . write ( " x y z \ n " )
# wird dabei g e l o e s c h t
>>> file1 . write ( " 1 2 3 " )
datei2 . txt haette nun folgenden Inhalt
x y z
1 2 3
Wichtig ist zu beachten dass Änderungen in der nicht sofort beim Aufruf von ’write’ geschrieben werden. Um
sicher zu stellen dass alles wie gewünscht in die Datei geschrieben wird ist es wichtig die Datei am Ende mit
der Methode ’close’ zu schliessen. Gleichzeitig wird auch das zugehörige obj ’file1’ zerstört.
Im obige Beispiel
>>> file1 = open ( " datei2 . txt " ," w " )
>>> file1 . write ( " x y z \ n " )
>>> file1 . write ( " 1 2 3 " )
Beachte : der folgende Aufruf wuerde noch eine leere Datei ’ datei2 . txt ’ anzeigen
>>> less datei2 . txt
Erst bei Aufruf
>>> file1 . close ()
hat datei2 . txt den erzeugten Inhalt
>>> less datei2 . txt
x y z
1 2 3
1
3
Module
Ein wichtiges Konzept von Python ist die Auslagerung von Funktionen, Klassen und Variablen in sogenannte
Module. Der Vorteil der Modularisierung ist, dass komplexe Projekte in mehrere kleinere Strukturen aufgeteilt
werden knnen, welche unabhängig voneinander weiter entwickelt und gepflegt werden können.
3.1
Import von Modulen
Die Sprache Python bietet bereits einige Standard Module, welche direkt nach dem Start des Interpreters
verfügbar sind, sowie einige ”third-party”Module, welche zuerst installiert werden müssen (siehe Installation.pdf). Um auf Module zugreifen zu können, müssen diese zuerst dem Namensraum des Python Skriptes
mittels des keywords import hinzugefügt werden.
>>> import random
# i m p o r t i e r t das Modul random
>>> dir ( random )
# zeigt die v e r f u e g b a r e n F u n k t i o n e n des Moduls
[ ... , ’ randint ’ , ... , ’ uniform ’ , ... ]
Um auf die verfügbaren Funktionen und Variablen des Moduls zuzugreifen, verwendet man die Syntax, die auch
schon für Methoden eingesetzt wurden.
Syntax :
Modulname . Funktion ()
Beispiel :
>>> a = random . randint (0 ,10) # Zugriff auf die randint F u n k t i o n des Moduls
>>> print a
4
>>> b = random . uniform (0 ,10) # Zugriff auf die uniform F u n k t i o n des Moduls
>>> print b
5 .0 08 7 11 0 42 7 73 79 2 4
Funktionen und Variablen des Moduls können auch direkt in den Namensraum des aktuellen Skriptes eingepflegt werden.
>>>
>>>
0.0
>>>
>>>
0.0
from math import sin , pi
print sin ( pi )
# import sin and pi from module math
from math import sin as Sinus # Import sin and add it to the n a m e s p a c e as Sinus
print Sinus ( pi )
4
NumPy Arrays
Zwei der nützlichsten Module für das wissenschaftliche Arbeiten mit Python sind NumPy und SciPy. NumPy
stellt dem Benutzer als wichtigstes Feature leistungsstarke multidimensionale Arrays und Matrizen und dazugehörige Operationen zur Verfügung, während SciPy unter anderem Module zur wissenschaftlichen Analyse
und Simulation bietet. Für den Gebrauch von NumPy und SciPy is es empfehlenswert, die jeweiligen OnlineReferenzen zu den Paketen anzuschauen und griffbereit zu halten:
• http://docs.scipy.org/doc/numpy/reference/
• http://docs.scipy.org/doc/scipy/reference/
Vorsicht ist geboten, da diese Dokumentation in der Regel den aktuellen Entwicklungsstand von NumPy/SciPy widerspiegelt und nicht zwangsläufig die auf dem Rechner installierte Version. Sollten beim Arbeiten mit
NumPy/SciPy Diskrepanzen gegenüber der Referenz auftreten, findet man auf http://docs.scipy.org/doc/
Links zu den Referenzen vorheriger Versionen.
4.1
Initialisierung
Das NumPy Arrays ist das wichtigste Konstrukt in NumPy. NumPy Arrays sind im wesentlichen ähnlich zu den
bereits bekannten Listen, jedoch mit einigen Einschränkungen und Vorzügen. Nachteilig im Vergleich zu Listen
ist die Einschränkung, dass die Elemente eines NumPy Arrays alle denselben Datentypen haben müssen. Dies
wird aber durch die deutlich höhere Zugriffs- und Verarbeitungsgeschwindigkeit bei Arrayoperationen gegenüber
Listen wettgemacht. Um Arrays benutzen zu können müssen wir erst das Modul numpy importieren, also dem
Namensraum bekanntmachen.
2
>>> import numpy
# Import des Modules numpy
Um jetzt ein NumPy-Array aus z.B. einer Liste von Ganzzahlen zu erzeugen kann man man die Funktion
array() aus dem Modul numpy benutzen:
>>> myList = range (5)
>>> myArr = numpy . array ( myList )
>>> print myArr , type ( myArr )
[0 1 2 3 4] < type ’ numpy . ndarray ’ >
# Liste der G a n z z a h l e n 0 bis 4
# Array der G a n z z a h l e n 0 bis 4
Die Konvertierung von Listen mit gemischten Typen schlägt unter Umständen fehl, wenn keine einheitliche
Konvertierung der Datentypen möglich ist:
>>> myList = [4 , ’ spam ’ ,4.5 ,(4 ,6)]
>>> myArr = numpy . array ( myList )
(...)
ValueError : setting an array element with a sequence
>>> myList = myList [: -1]
# loesche Tupel
>>> myArr = numpy . array ( myList )
>>> print myArr
[ ’4 ’ ’ spam ’ ’ 4.5 ’]
Im ersten Fall schlug die Konvertierung fehl, da kein gemeinsamer Datentyp für alle Elemente gefunden werden konnte (wegen des Tupels). Im zweiten Fall wurde als gemeinsamer Datentyp von int, string und float der
string-Datentyp gewählt und für das Array festgelegt.
Natürlich muss man nicht vorher Tupel oder Listen definieren, um Arrays zu erzeugen, es gibt eine Reihe
nützlicher Funktionen zur Erzeugung von Arrays. Ähnlich zu range() für Listen gibt es arange() zur Erzeugung
von auf- oder absteigenden Arrays von Zahlen. Praktischer an arange() ist hierbei, dass sich auch Ranges von
Float-Zahlen generieren lassen:
>>> myArr = numpy . arange (5)
>>> print myArr
[0 1 2 3 4]
>>> myArr = numpy . arange ( -1 , -0.5 ,0.1)
>>> print myArr
[ -1. -0.9 -0.8 -0.7 -0.6]
# drittes A r g u m e n t ist S c h r i t t w e i t e
Dazu ähnlich ist die Funktion linspace(), die jedoch als drittes Argument die Anzahl an Elementen nimmt,
und standardmäßig den Endwert enthält.
>>> myArr = numpy . linspace (10 ,20 ,5)
>>> print myArr
[ 10. , 12.5 , 15. , 17.5 , 20. ]
# drittes A r g u m e n t ist Anzahl E l e m e n t e
Weitere praktische Funktionen zur Generierung von Arrays sind zeros(N), ones(N) und empty(N), die jeweils
Arrays aus N zu null, eins oder uninitialisierten Elementen erzeugen.
4.2
Elementweise Operationen
Ein wichtiger Vorteil von Arrays gegenber Listen ist es, dass arithmetische und Vergleichs-Operationen elementweise angewandt werden. Somit hat das Array eher Ähnlichkeiten zu einem Vektor als zu einer Liste, bei der
dies nur ber Listen oder die noch nicht vorgestellen List Comprehensions möglich ist. Zu beachten ist, dass die
elementweisen Funktionen aus dem Namensraum numpy statt aus math importiert werden müssen, um richtig
auf Arrays zu funktionieren:
>>> myArr
>>> print
[0 2 4
>>> print
[ 0.
= numpy . arange (5)
myArr *2
6 8]
numpy . sqrt ( myArr ) # nicht aus math
1.
1.41421356 1.73205081
2.
]
Mathematische Verknüpfungen zweier Arrays werden im Gegensatz zu Listen ebenfalls elementweise abgehandelt:
>>> myArr = numpy . arange (5)
>>> myBrr = numpy . arange (0 ,1 ,0.2)
>>> print myArr ** myBrr
[ 1.
, 1.
, 1.31950791 , 1.93318204 , 3.03143313]
>>> myBrr = numpy . arange (0 ,1 ,0.4)
>>> print myArr ** myBrr # falsche Anzahl E l e m e n t e
...
ValueError : shape mismatch : objects cannot be broadcast to a single shape
3
Dies bedeutet aber auch, dass sich Arrays im Gegensatz zu Listen nicht mit dem Additionsoperator vergrößern
lassen. Hierzu muss das Array vergrößert werden, dies kann mit den im nächsten Abschnitt vorgestellten Funktionen geschehen.
Auch Vergleichsoperatoren arbeiten elementweise auf Arrays und geben sogenannte Masken zurück (dazu
mehr im nächsten Abschnitt):
>>> myMask = myArr > 2
>>> print myMask
[ False False False True
4.3
True ]
Multidimensionalität und Indizierung
Wie eingangs erwähnt ist das NumPy Array ein multidimensionaler Container. Zur Erzeugung von mehrdimensionalen Arrays gibt es etliche Möglichkeiten, eine einfache ist es z.B. die Funktionalität der von numpy
bereitgestellten Erzeugungsfunktionen zu nutzen, ein sogenanntes shape-Tupel als Argument zu nehmen:
>>> mdArray = numpy . zeros ((2 ,3))
>>> print mdArray
[[ 0. 0. 0.]
[ 0. 0. 0.]]
# (2 ,3) gibt die shape an
Diese Funktion erzeugt ein 2x3-Array und initialisiert jedes Element zu 0. Das Argument hierbei ist ein Tupel,
der jeweils die Anzahl Elemente jeder Dimension angibt. Die Länge des Tupels gibt somit die Dimensionalität
des Arrays an. Überprüfen kann man die Form (shape) des Arrays mit dem Attribut shape des Arrays, die
Dimensionalität mit ndim, und die Gesamtzahl an Elementen mit size.
>>> print mdArr . shape
(2 ,3)
>>> print mdArr . ndim
2
>>> print mdArr . size
6
Die Indizierung von Arrays funktioniert genau wie bei Listen:
>>> myArr = numpy . arange (5)
>>> print myArr [2]
2.
Genauso ist das Slicen von Arrays möglich:
>>> print myArr [1: -1]
[1 2 3]
Indizierung von multidimensionale Arrays lässt sich entweder über mehrere Indizierungsoperatorklammern oder
ber Komma-getrennte Indizierungsanweisungen in einer Operatorklammer realisieren:
>>>
>>>
>>>
[[0
[3
>>>
3
>>>
2
mdArr
mdArr
print
1 2]
4 5]]
print
= numpy . arange (6)
= mdArr . reshape ((2 ,3))
mdArr
mdArr [1][0]
print mdArr [0 ,2]
Die Methode reshape() konvertiert das Array in die angegebene Form. Eine ähnliche Methode ist resize(),
die darüber hinaus as Array auch vergrößern kann, wenn die im Tupel angegebene Anzahl Elemente größer als
das Ausgangsarray ist.
Natrlich sind auch hierbei in jeder Dimension Slices möglich:
>>> print mdArr [0 ,:] # erste Zeile
[0 1 2]
>>> print mdArr [: , -1] # letzte Spalte
[2 5]
print mdArr [: -1 ,1:]
[[1 , 2]]
4
Ein entscheidender Vorteil von NumPy Arrays ist es, dass zur Indizierung auch Listen/Arrays (allg. Sequenzen) oder Masken (True/False Arrays gleicher Form) benutzt werden können. Im ersten Fall besitzt die
Index-Liste weniger Elemente als die Ursprugsliste, und jedes Element gibt an welche Indizes des Ursprungsarrays in das Ausgabearrays übernommen werden.
>>>
>>>
>>>
[10
myArr = numpy . arange (10 ,20)
indArr = numpy . arange (0 ,10 ,2) # I n d i z i e r e jedes zweite Element
print myArr [ indArr ]
12 14 16 18]
Die bereits oben erwähnten Masken können ebenfalls zur Indizierung genutzt werden, und bieten eine natürliche
Schreibweise zur Reduktion von Arrays. Zu beachten ist jedoch, dass die Masken die gleiche Form wie die zu
indizierenden Arrays haben müssen. Daraus folgt auch, dass die Masken auf alle Arrays dieser Form angewandt
werden können. Dies ist z.B. sehr nützlich zum gleichzeitigen Slicen mehrerer Spalten eines Datensatzes
>>>
>>>
>>>
>>>
[16
myArr = numpy . arange (10 ,20)
myBrr = numpy . arange (40 ,50)
myMask = myArr > 15
print myArr [ myMask ] , myBrr [ myMask ]
17 18 19] [46 47 48 49]
Masken lassen sich ebenfalls einfach über die Multiplikations- und Additionsoperatoren schneiden oder vereinigen:
>>> myMask = ( myArr > 15) * ( myBrr < 49)
>>> print myArr [ myMask ] , myBrr [ myMask ]
[16 17] [46 47]
Masken können auch zur Erzeugung von Indexlisten genutzt werden, indem sie an die Funktion where()
übergeben werden:
>>> myInd = numpy . where ( myMask )
>>> print myArr [ myInd ] , myBrr [ myInd ]
[16 17] [46 47]
5
Übungen
Für die Durchführung der Übungen nutzen Sie bitte die Online-Referenzen zu NumPy oder die help-Funktion,
um sich mit der Funktionsweise der genannten Funktionen vertraut zu machen
1. Lesen Sie die drei Spalten der Datei ’Data.dat’ als Listen Typ ein. Die erste und zweite Spalte enthält die
Koordinaten x und y (Längen- und Breitengrad der Marsoberfläche) und die dritte zugehörige Höhenmessung
der Oberfläche, H(x, y). Konvertieren sie die Listen in NumPy arrays.
2. Entfernen Sie fehlerhafte Messungen der Höhe (z = −999) aus den in x, y, z eingelesenen Daten. Benutzen
sie hierfür die Masken Funktionalität des NumPy arrays.
3. Erstellen Sie aus den drei eingelesenen und korrigierten NumPy Arrays ein zwei-dimensionales NumPy
Array, Z. Hierbei soll Z die Dimension (1000, 1000) besitzen und die (gemittelten) Messwerte H(x, y)
enthalten. Benutzen sie hierfür die Funktion histogram2d aus dem NumPy Modul.
Zum Überprüfen des erzeugten Histograms, stellen sie dieses Grafisch dar:
>>> import pylab
>>> Z , x , y = histogram2d (...)
>>> pylab . pcolormesh ( x [: -1] , y [: -1] , Z . T )
5
Herunterladen