#!/usr/bin/env python
# coding: utf-8
# # Dateien, Dateiformate und Persistenz
# Ein Computerprogramm hat seinen Platz im Arbeitsspeicher.
# Das ist ein schneller, aber vergänglicher Ort um Daten abzulegen.
# Möchte man
#
# * Ergebnisse einer Berechnung speichern,
# * existierende Daten einlesen,
# * oder Berechnungen zwischenspeichern
#
# braucht es weiterer Technologien, um die Daten aus dem Arbeitsspeicher z.B. auf die Festplatte oder in einer Datenbank zu speichern und später von dort wieder einzulesen.
#
# Der Überbegriff für jegliches Speichern von Daten in einer Datei ist [Serialisierung](http://de.wikipedia.org/wiki/Serialisierung).
# Die Daten des Programmes werden hierbei kodiert und als lineare Sequenz von Bits&Bytes abgelegt.
# Im folgenden werden einige solcher Formate vorgestellt und kurz erklärt,
# wie sie mit Python gelesen bzw. geschrieben werden können.
# ## Dateien
#
# * Ein **Byte** ist die kleinste Speichereinheit und umfasst 8 Bits (Wahr oder Falsch Werte).
# * Eine **Datei** ist ein Vektor von Bytes (ganz ähnlich wie ein String, bzw. byte-Vektor in Python 3).
# * Dateien werden auf der Festplatte des Computers gespeichert, wobei diese Byte-Vektoren durch physikalische Techniken (Magentismus, Feldeffekttransistoren, Kerr-Effekt, etc.) so abgelegt werden, dass sie auch ohne Stromzufuhr erhalten bleiben und später wieder ausgelesen werden können.
# * Daher werden Festplatten für die
# Persistenz von Information verwendet.
#
# ## Dateisystem & Verzeichnisse
#
# Da es ist in der Praxis sehr mühsam wäre die exakte Speicherpositionen jeder einzelnen Datei auf der Festplatte zu wissen, gibt es [**Dateisysteme**](http://en.wikipedia.org/wiki/File_system) ([POSIX Standard](http://en.wikipedia.org/wiki/POSIX)).
# Ein Dateisystem (wie z.B. EXT4, btrfs, ZFS oder NTFS)
# ist eine Strukturierung der Festplatte (oder einer darin definierten Partition),
# um anhand eines generischen [**Pfades**](http://en.wikipedia.org/wiki/Path_%28computing%29) die Datei zu finden.
# Auch merkt sich das Dateisystem die Größe der Datei
# (neben anderen Informationen wie Zugriffszeit und Berechtigungen)
# und erlaubt damit das einfache Auslesen der gesamten darin gespeicherten Daten. Beispiel:
#
# /home/joe/documents/thesis.tex
#
# Solch ein Pfad besteht zur besseren Strukturierung aus einer Verzeichnisstruktur und einem Dateinamen.
# Diese Verzeichnisse können verschachtelt sein und sind mit einem Schrägstrich "`/`" getrennt.
# `/home/joe/documents` ist das Verzeichnis zu Joe's Dokumenten,
# wo er seine Dissertation unter dem Dateinamen `thesis.tex` gespeichert hat.
# (Windows verwendet übrigens ebenfalls ein POSIX kompatibles Dateisystem mit einem `/`,
# verwendet aber im Interface des Benutzers einen Backslash "\"
# -- der normalerweise [zum Escapen](http://en.wikipedia.org/wiki/Escape_character) verwendet wird
# -- und Bildet daher eine Ausnahme).
#
# Die Dateiendung `tex` ist übrigens ein Hinweis darauf, dass es sich um eine $\LaTeX$ Datei handeln wird.
# Es könnte jedoch auch etwas anderes darin gespeichert sein...
# Im folgenden Beispiel wird eine Datei `verfassung.md` aus dem Unterverzeichnis "`res`" dieses Skriptums eingelesen.
# Damit dies Betriebssystemunabhängig funktioniert,
# wird `os.path.join` für das Zusammenfügen des Dateipfades verwendet:
#
# Regel: **Immer** `os.path.join` statt expliziter Pfade verwenden!
# In[8]:
import os
filename = os.path.join("res", "verfassung.md")
filename
# ## Text-Datei
#
# Es handelt sich in diesem ersten Beispiel um eine Text-Datei.
# Das heißt, die darin gespeicherten Bytes stehen für Buchstaben und Zahlen im Alphabet, einigen Interpunktionszeichen und Steuerzeichen (neue Zeile, etc.).
# Da ein Byte 8 Bit hat, können dadurch nur maximal $2^8 = 256$ Zeichen kodiert werden.
#
# Beispiel: Die Binärzahl "`01000001`" ist der Buchstabe "`A`".
# In[9]:
chr(int("01000001", 2))
# Das historisch erste durchgängig verwendete Kodierungsalphabet ist [ASCII](http://en.wikipedia.org/wiki/ASCII).
# Ein kurzer Blick reicht jedoch um zu sehen, dass es hier keine Umlaute gibt.
# Auch ist in diesen 256 Zeichen kein Platz für Schriftzeichen aus vielen anderen Sprachen.
#
# Daher gibt es modernere Kodierungen wie [UTF-8](http://en.wikipedia.org/wiki/UTF-8) (und verwandte) um alle Schriftzeichen kodieren zu können.
#
# Im folgenden werden nun die Byte-Daten dieser Text-Datei
# mittels der in Python eingebauten Funktion `open` eingelesen.
# Es öffnet zuerst die Datei (und gibt einen File-Handler zurück) und
# dann liest die Methode `.read()` die Byte-Daten ein:
# In[10]:
filehandler = open(filename)
filehandler
# In[11]:
filedata = filehandler.read()
filedata
# In einem eigenständigen Programm wird üblicherweise anschließend die Datei wieder mittels `.close()` geschlossen:
# In[12]:
filehandler.close()
# Wie man sieht, beinhaltet die Datei Text und ein paar extra Zeichen.
#
# * "`\n`" ist eine Zeilenumbruch
# * "`\xNN`" sind hexadezimale Werte für [UTF-8 kodierte Buchstaben](http://en.wikipedia.org/wiki/UTF-8)
# (in unserem Fall der deutschen Sprache sind das Umlaute usw.)
# Das rührt daher, dass beim Erstellen dieser Textdatei diese Soderzeichen genau nach dieser UTF-8 Kodierung
# geschrieben wurden. Beispiel:
# In[13]:
print('\xc3\xa4')
# * Zwischendurch gibt es auch [Markdown Steuerzeichen](http://en.wikipedia.org/wiki/Markdown) zur Formatierung des Textes.
#
# Hier nun eine "bessere" (also, für den Menschen lesbarere Art) wie die Daten dieser Datei dargestellt werden können:
#
# 1. `filedata.decode("utf8")` dekodiert die UTF-8 Kodierung und
# 1. IPython's Markdown parser liest den dekodierten Text ein und konvertiert auf HTML (welches dann hier angezeigt wird bzw. im PDF wird dieses Markdown anschließend auch noch nach LaTeX übersetzt)
# In[16]:
from IPython.display import Markdown, display
display(Markdown(filedata))
# ---
#
# Nun ist es viel einfacher zu lesen!
#
# Hier noch eine zweite Variante, wie UTF-8 kodierter Text besser eingelesen werden kann.
# Das Modul "[`codecs`](https://docs.python.org/2/library/codecs.html)"
# stellt ebenfalls eine `open` Funktion bereit,
# welcher man gleich direkt angeben kann, dass es sich um UTF-8 kodierten Text handelt.
# Anschließend kann die Datei ohne Dekodierungsschritt eingelesen und dargestellt werden.
#
# Hinweis: UTF-8 ist weit verbreitet, wird von so gut wie allen Webseiten verwendet
# und eigentlich der Standard schlechthin.
# Es gibt jedoch noch andere Textformatierungen,
# z.B. in Windows "Westeuropäisch" mit Codepage "`cp1140`" bzw. "`iso-8859-1[5]`".
# ### with statement
#
# Außerdem erweist es sich in der Praxis als sehr nützlich,
# mittels [with statement](https://docs.python.org/2/reference/compound_stmts.html#with)
# ```python
# with ... as x:
# do_something_with(x)
# ```
# einen [Kontext](https://docs.python.org/2/reference/datamodel.html#context-managers)
# für die geöffnete Datei erzeugt.
# Der große Vorteil ist, dass bei Verlassen dieses Kontexts die Datei automatisch geschlossen wird (auch dann, wenn es zu einer Exception kommt).
#
# Regel: Lesen und Schreiben von Dateien im allgmeinen **immer** mit `with`-Kontexten.
# In[17]:
from codecs import open as copen
with copen(filename, "r", "utf8") as verfassung:
text = verfassung.read()
display(Markdown(text))
# ---
#
# Bislang wurde immer die `.read()` Methode des File-Handlers verwendet,
# um die gesamte Datei einzulesen.
# Handelt es sich um sehr große Dateien, passen sie nicht mehr in den Arbeitsspeicher oder verlangsamen die Verarbeitung.
# Daher gibt es z.B. die Möglichkeit, eine Textdatei Zeilenweise einzulesen oder immer nur Blockweise.
#
# * `.read()`
# * `.readlines()` iteriert über die Zeilen
# Einlesen der ersten 10 und dann 20 Bytes
# In[18]:
with open(filename) as verfassung:
print("10 bytes: %r" % verfassung.read(10))
print("weitere 20 bytes: %r" % verfassung.read(20))
# Um zwischendurch wieder an den Anfang der Datei zu springen, gibt es die `.seek(0)` Methode:
# Einlesen der ersten 10 Zeilen mittels `.readlines()`, welches beim Iterieren in einer `for`-Schleife auch automatisch aufgerufen werden würde:
# In[19]:
with copen(filename, "r", "utf8") as verfassung:
for line_number, line in enumerate(verfassung.readlines()):
print(u"Zeile %2d: %s" % (line_number, line[:-1]))
if line_number >= 10:
break
# ## Schreiben einer Datei
#
# Bislang haben wir uns ausschließlich mit dem Einlesen einer Datei beschäftigt.
# Das Schreiben läuft ganz ähnlich ab:
#
# 1. File-Handler für eine Datei, aber mit dem **Modus "w" für "write"** (statt "r" für "read").
# (sollen Binärdaten geschrieben werden, gibt es auch "wb").
# Für Textdateien am besten **`codecs.open("...", "w", "utf8")`**.
# 2. Die Methode `.write(...)` des File-Handlers.
# 3. Abschließen der Schreibvorgangs durch `.close()` bzw. innerhalb eines `with`-Kontexts arbeiten.
#
# Hinweis: Im Unterschied zur Verwendung des `print` Statements oder Funktion, muss ein Zeilenumbruch explizit mit "`\n`" eingefügt werden.
# In[20]:
import os, codecs
filename = os.path.join("res", "textdatei.txt")
with codecs.open(filename, "w", "utf8") as outfile:
from math import log
outfile.write("Die Berechneten Werte\n")
outfile.write(" x: %10s %12s\n" % ("f(x)", "lg(f(x))"))
for i in range(1,11):
fx = (i + i**5) / 3.
outfile.write("%3d: %10.2f %12.2f\n" % (i, fx, log(fx, 10)))
outfile.write("ENDE")
# Anschließendes Einlesen dieser Datei um zu sehen, was gerade passiert ist:
# In[21]:
with codecs.open(filename, "r") as infile:
print(infile.read())
# ## Strukturierte Dateien
#
# Neben Textdokumenten, die verwirrenderweise auch mit Text-Formatierungen strukturiert sein können aber trotzdem unstrukturiert sind, gibt es eine Vielzahl von Dateien mit einem klar definierten "Format" zur Strukturierung.
#
# NB: Text-Formatierungen sind hierbei "normale" Buchstaben oder "Befehle",
# die eine besondere Bedeutung erhalten -- z.B. "fett" oder "Aufzählung".
#
# Das "Format" einer strukturierten Daten ist viel genauer und strenger:
# Es legt Regeln für die gesamte Datei fest,
# so wie z.B. die Programmiersprache Python für den gesamten geschriebenen Code genaue Linguistische Regeln festlegt.
# ## Python Pickle
#
# [Python Pickles](https://docs.python.org/2/library/pickle.html)
# sind eine für Python spezifische Technik,
# Daten eines Objektes zu speichern.
# Der Vorteil ist, dass es sehr schnell und speicherplatsparend funktioniert.
# Die Nachteile sind, dass dies eine rein Python spezifische Lösung ist und
# zum Laden des pickles wird genau die richtige Klasse (mit derselben Struktur) benötigt.
# In[32]:
from os.path import join
import pickle
# In[33]:
class Data(object):
def __init__(self, a, b):
self.a = a
self.b = b
d1 = Data(a = "this is value a", b = [2, 2, 31])
print(id(d1))
# Es ist immer eine gute Idee, das Protokoll auf die aktuellste Version zu stellen.
# Standardmäßig wird immer die niedrigste (und viel schlechtere) Variante genommen,
# um rückwärskompatibel zu sein.
# In[34]:
pickle_fn = join("res", "data.pickle")
with open(pickle_fn, "wb") as pickle_file:
pickle.dump(d1, pickle_file, protocol=pickle.HIGHEST_PROTOCOL)
# Nun wird die Klasse wieder geladen, in `d2` statt `d1`, und gelesen.
# Man sieht, die Klasse ist rekonstruiert worden, die Daten sind verfügbar, und die Speicheradresse ist eine andere.
# In[35]:
d2 = pickle.load(open(pickle_fn))
print("{} {}".format(type(d2), id(d2)))
d2.a, d2.b
# ### CSV: Comma-Separated-Values
#
# Dies ist ein sehr einfaches, rein textbasiertes Format.
# Es handelt sich um spatenweise angeordnete Daten in Form einer Tabelle,
# wobei Spaten durch "`,`", "`;`" oder Tabulator "\t" getrennt werden.
# Die erste Zeile ist üblicherweise eine Beschriftung der Spalten,
# darunter ein Eintrag pro Zeile.
# Strings werden üblicherweise in Anführungszeichen gestellt,
# insbesondere um Verwechslungen mit dem trennenden Zeichen zu vermeiden.
# Dokumentation: [Python CSV](https://docs.python.org/2/library/csv.html)
#
# Achtung: Es gibt keine Typeninformation! Daher sind Zahlen Strings und müssen vorsichtig konvertiert werden.
# In[36]:
from os.path import join
flugzeit_csv = join("res", "flugzeit.csv")
# IPython's `%%writefile`-magic Funktion schribt in die Datei `flugzeit_csv` den hier angegebenen Text.
# In[37]:
get_ipython().run_cell_magic('writefile', '$flugzeit_csv', 'origin,destination,time,distance\nVIE,LHR,2:30,1100\nVIE,JFK,8:59,6823\nJFK,ZCL,4:39,3346\nSFO,JFK,5:30,4162\nPEK,ZRH,10:55,7993\nHNL,YOW,10:08,7758\nVIE,BER,1:09,676\nCDG,TIP,3:01,2019\nCDG,ATZ,4:47,3453\nCDG,FCO,1:52,1102\nVIE,FCO,1:28,780\nLIS,AMM,5:39,4138\nLIS,BER,3:22,2307\nLIS,WAW,3:55,2754\nPEK,SVO,7:43,5811\nBRN,HUU,13:20,10331\n')
# Die Datei wird geöffnet und mittels der [`csv.reader`](https://docs.python.org/2/library/csv.html) Methode eingelesen, wobei als Trennzeichen der "," angegeben wird.
# In[38]:
import csv
with open(flugzeit_csv) as fzeit:
for line in csv.reader(fzeit, delimiter=","):
print line
# Wie man sieht, Python's `csv` modul hat die Datei mühelos lesen können.
# Einzig das Trennzeichen musste gesetzt werden.
# Jede Zeile ist eine `list` von `str` Objekten.
# Die Zahlen müssten für eine weitere Verarbeitung umgewandelt werden.
# ### CSV mittels Pandas
#
# Pandas kann DatenFrames von CSV Tabellen einlesen.
# Dabei werden die Datentypen (wenn möglich) automatisch konvertiert
# und es lassen sich Header und Indices genauer angeben.
# [Pandas' `from_csv` Dokumentation](http://pandas.pydata.org/pandas-docs/stable/io.html#io-read-csv-table)
#
# In diesem gegebenen Fall wird wieder der Strichpunkt als Trennzeichen angegeben
# und keine der Spalten als Index definiert.
# In[39]:
import pandas as pd
flighttimes = pd.DataFrame.from_csv(flugzeit_csv, sep=",", index_col=None)
flighttimes
# Die `time` Spalte wird nun explizit mit einer Parserfunktion `hm_time`
# in Stunden konvertiert und als neue Spalte `time2` eingeführt.
# In[40]:
def hm_time(entry):
h, m = entry.split(":")
return int(h) + int(m) / 60.
flighttimes["time2"] = flighttimes.time.apply(hm_time)
flighttimes
# In[41]:
# maximum overall distance
flighttimes.ix[flighttimes.distance.argmax()]
# In[42]:
# maximum distance, for each origin
for group, subdf in flighttimes.groupby("origin"):
m = flighttimes.ix[subdf.distance.argmax()]
print("Maximum distance from %s: %5d km to %s" % (group, m.distance, m.destination))
# Zur Auflockerung eine grafische Darstellung der Weg-Zeit Abhängigkeit.
# Wenig Überraschend, es ist direkt proportional!
# In[43]:
get_ipython().run_line_magic('matplotlib', 'inline')
import matplotlib
matplotlib.rcParams["figure.figsize"] = (12,7)
# In[44]:
# plot of distance vs. converted time in hours. easy for pandas dataframes!
flighttimes.plot(x = "distance", y = "time2", kind="scatter", ylim=0, xlim=0)
# ## Datenbank (SQLite)
#
# Für größere Datenmengen bzw. im professionellen/wissenschaftlichen Umfeld werden Datenbanken eingesetzt.
# Die Daten werden in einem für die Datenbank spezifische Art gespeichert
# (sind also nicht wie CSV Dateien von beliebigen Programmen lesbar),
# beinhalten aber zusätzlich Informationen zu den Typen jeder Spalte.
# Datenbanken bieten eingebaute Hilfsmittel wie Suchindizes und eine Abfragesprache,
# es können mehrere Tabellen auf verschiedenste Arten verknüpft werden,
# und die Verarbeitungsgeschwindigkeit ist viel höher.
#
# Eingebaut in Python ist [`sqlite3`](https://docs.python.org/2/library/sqlite3.html).
#
# Das folgende Beispiel speichert die Daten der CSV Datei in einer sqlite3 Datenbank und macht eine Abfrage.
# 1. Erstellen: lösche die gegebenenfalls existierende Datenbankdatei und befülle sie dann.
# In[45]:
db_filename = join("res", "flugzeit.db")
# Löschen der Datenbank, falls sie existiert
import os
if os.path.exists(db_filename):
os.unlink(db_filename)
# In[46]:
import sqlite3
conn = sqlite3.connect(db_filename)
# In[47]:
# for the connection `conn`, get a cursor and operate on the table
curs = conn.cursor
# In[48]:
curs = conn.cursor()
curs.execute('''
CREATE TABLE flugzeit
(origin text, destination text, distance real, time real)
''')
# In[49]:
for idx, (orig, dest, _, dist, time2) in flighttimes.iterrows():
curs.execute("INSERT INTO flugzeit VALUES(?, ?, ?, ?)", (orig, dest, dist, time2))
# In[50]:
# Hilfsfunktion: gibt die Abfrage Zeilenweise aus
def show_query(query):
for row in query:
out = []
for entry in row:
if isinstance(entry, float): out.append("{:>9.2f}".format(entry))
elif isinstance(entry, int): out.append("{:>9d}".format(entry))
else: out.append("{:>7s}".format(entry))
print(" ".join(out))
# In[51]:
show_query(curs.execute("SELECT * FROM flugzeit"))
# In[52]:
# sort by decreasing distance
show_query(curs.execute("SELECT * FROM flugzeit ORDER BY -distance"))
# In[53]:
# minimum time from each origin
show_query(curs.execute("""
SELECT origin, destination, min(time)
FROM flugzeit
GROUP BY origin
"""))
# In[54]:
# maximum distance from LIS
curs.execute("""
SELECT *
FROM flugzeit
WHERE origin = 'LIS' AND
distance = (SELECT max(distance) FROM flugzeit WHERE origin = 'LIS')
""").fetchone()
# ## JSON
#
# [JSON](http://www.json.org) ist eine der modernen und am weitesten verbreiteten Serialisierungssprachen.
# Es handelt sich um eine von [JavaScript](http://en.wikipedia.org/wiki/JavaScript) abgeleiteten Datenstruktur,
# welche nicht nur von vielen Programmiersprachen verstanden wird,
# sondern auch auf einfache Art komplex verschachtelte Datenstrukturen speichern kann.
# JSON (und ähnliche Serialisierungsformate) sind als einzelner großer String zu verstehen,
# welche mit mehreren Syntaxzeichen (eckige Klammer, geschwungene Klammer, Doppelpunkt, ...) die Struktur definieren.
#
# Python liefert hierfür das [json](https://docs.python.org/2/library/json.html) ([json in python 3](https://docs.python.org/3/library/json.html)) mit.
#
# Wir bleiben bei dem Beispiel der Flugdistanzen und Konvertieren die Pandas Daten zu JSON -- in diesem Fall als Liste von assoziativen Dictionaries.
# In[55]:
flighttimes_py = []
for _, data in flighttimes.iterrows():
entry = {"orig" : data.origin,
"dest" : data.destination,
"dist" : data.distance,
"hours": data.time2 }
flighttimes_py.append(entry)
# Konvertierung der Python Datenstruktur (welche schon so gut wie JSON ist!) zu JSON.
# `indent=2` ist nicht notwendig, liefert aber die hübsche Einrückung.
# In[56]:
import json
flighttimes_json = json.dumps(flighttimes_py, indent=2)
print(flighttimes_json)
# `loads` läd diese Datenstruktur aus der Zeichenkette:
# In[57]:
flightimes_py2 = json.loads(flighttimes_json)
print(flightimes_py2[1])
# ## YAML
#
# [YAML](http://www.yaml.org/) ist ähnlich wie JSON ein textbasiertes, programmiersprachenunabhängiges Format zur strukturierten Speicherung von Daten.
# Im Vergleich zu JSON hat es einige zusätzliche Funktionen und ist für den Menschen besser lesbar.
#
# Im folgenden wird die externe Bibliothek [pyyaml](https://bitbucket.org/xi/pyyaml) verwendet,
# um ganz analog zu JSON die Daten zuerst als String zu kodieren und dann wieder zurück zu lesen.
# In[58]:
import yaml
flightimes_yaml = yaml.dump(flighttimes_py, default_flow_style=False, indent=2)
print(flightimes_yaml)
# Im Gegensatz zu JSON, gibt es keine geschwungenen Klammern und sonstige besondere Auszeichnungen.
# Der Bindestrich signalisiert einen neuen Eintrag und die mit Doppelpunkt getrennte Assoziation ist eine kompaktere Darstellung.
#
# Mit wenigen Parameteränderungen gibt es aber auch ausgaben, die bis auf kleine Zeichenkodierungsunterschiede wie JSON sind.
# Daher ist YAML eine Übermenge von JSON.
# In[59]:
flightimes_yaml2 = yaml.dump(flighttimes_py, default_flow_style=True, indent=0)
print(flightimes_yaml2)
# In[60]:
flightimes_yaml3 = yaml.dump(flighttimes_py, default_flow_style=None, indent=0)
print(flightimes_yaml3)
# Zurücklesen des YAML Datensatzes in ein Python Objekt:
# In[61]:
flightimes_py3 = yaml.load(flightimes_yaml)
print(flightimes_py3[1])
# ## XML
#
# Ein sehr altes, ebenfalls programmiersprachenunabhängiges und textbasiertes, Serialisierungsformat ist
# [XML](http://en.wikipedia.org/wiki/XML).
# Das dahinterstehende Konzept ist ein Baum, dessen Wurzelknoten das gesamte Dokument umfasst.
# Jeder Knoten kann Attribute und Kinderknoten haben.
# Zwischen den Kinderknoten können auch Textknoten stehen.
#
# Man trifft es nur noch selten an und wird am ehesten noch im Geschäftsumfeld verwendet.
# Das in Python eingebaute [`xml`](https://docs.python.org/2/library/xml.html) ([`xml` in python 3](https://docs.python.org/3/library/xml.html)) modul stellt beispielsweise über das Submodul `minidom` und `etree` einfache Schnittstelle zum Verarbeiten von XML bereit.
#
# HTML ist ähnlich zu XML, und daher sind einige Bibliotheken zum automatisierten Verarbeiten von HTML auf basis von XML konstruiert.
# In[62]:
import xml.etree.ElementTree as ET
# wurzelknoten
flighttimes_xml = ET.Element("flighttimes")
for _, data in flighttimes.iterrows():
ft_entry = ET.SubElement(flighttimes_xml, "flighttime")
ft_entry.set("orig", data.origin)
ft_entry.set("dest", data.destination)
ft_distance = ET.SubElement(ft_entry, "distance")
ft_distance.set("unit", "km")
ft_distance.text = str(data.distance)
ft_hours = ET.SubElement(ft_entry, "hours")
ft_hours.set("unit", "hour")
ft_hours.text = str(data.time2)
# Zur schöneren Formatierung, wird das Ergebnis von `ET.tostring` noch durch `minidom`'s `toprettyxml` geschickt.
# In[63]:
from xml.dom import minidom
raw_xml = ET.tostring(flighttimes_xml, "UTF-8")
flighttimes_xml_ser = minidom.parseString(raw_xml).toprettyxml(indent=" ")
print(flighttimes_xml_ser)
# Zurücklesen, diesmal über `minidom`:
# In[64]:
flighttimes_dom = minidom.parseString(flighttimes_xml_ser)
# In[65]:
ft_dom_root = flighttimes_dom.firstChild
ft_dom_node1 = ft_dom_root.childNodes[1]
print(ft_dom_node1.toxml())
# In[66]:
ft_dom_node1.getAttribute("orig")
# In[67]:
ft_dom_node1.getAttribute("dest")
# Man beachte, dass hier weit mehr Aufwand getätigt werden muss und es auch keinerlei Unterstützung für Datentypen gibt.
# * `.getElementsByTagName` sucht anch allen Kindknoten mit dem angegebenen Namen -- es wird mittels `[0]` das aller erste genommen.
# * Von diesem Knoten aus, steigt man über `.childNodes[0]` weiter den Baum hinab, um in dieser `TextNode` dann über das `.data` Attribut endlich zu dem eigentlichen Wert zu gelangen.
# In[68]:
distance_node = ft_dom_node1.getElementsByTagName("distance")[0]
unit = distance_node.getAttribute("unit")
value = int(distance_node.childNodes[0].data)
print("%d [%s]" % (value, unit))
# In[69]:
hours_node = ft_dom_node1.getElementsByTagName("hours")[0]
unit = hours_node.getAttribute("unit")
value = float(hours_node.childNodes[0].data)
print("%f [%s]" % (value, unit))
# Alternativ lässt sich auch mit `.findall()` aus der ElementTree Bibliothek nach den Namen der XML-Knoten suchen:
# In[71]:
ftimes_et = ET.fromstring(flighttimes_xml_ser)
for ftime in ftimes_et.findall("flighttime"):
print("{} {} {}".format(ftime.get("orig"), ftime.get("dest"), ftime.find("distance").text))
# ## Rastergrafiken (JPEG, PNG, etc.)
#
# Ein Bild wird üblicherweise als Rastergrafik gespeichert.
# Das heißt, es ist ein rechteckiges Array von Bildpunkten (Pixel),
# denen Farbwerte oder Graustufenwerte zugewiesen werden.
# Darüber hinaus werden zum Sparen von Speicherplatz diese Pixelwerte komprimiert
# (d.h. es findet eine verlustbehaftete Kodierung der Information statt).
#
# Hier nun ein Beispiel, wie ein JPEG Bild mittels `scipy.ndimage` eingelesen und dargestellt werden kann:
#
# Hinweis: Falls es nicht funktioniert, muss das [`pillow` Module](https://python-pillow.github.io/) installiert werden
# (`enpkg pillow` oder `pip install pillow`)
# In[72]:
from os.path import join
import numpy as np
import scipy.ndimage as ndimage
elephant = ndimage.imread(join("res", "elephant.jpg"))
# Es handelt sich um ein Bild mit 652x1107 Bildpunkten und drei Farbkanälen -- als `numpy.ndarray`!
# In[73]:
elephant.shape
# Daten in einer Teilmatrix.
# Jeder Wert gibt hier an, wie stark der rote Farbton in diesem Pixel aufgedreht werden soll (0 bis 255):
# In[74]:
np.set_printoptions(linewidth = 105)
print(elephant[100:110, 500:550, 0])
# Hier nun eine für den Menschen verständlichere Form:
# Ein Babyelefant mit Muttertier.
# In[75]:
get_ipython().run_line_magic('config', "InlineBackend.figure_formats=['png']")
get_ipython().run_line_magic('matplotlib', 'inline')
import matplotlib.pyplot as plt
plt.rcParams["figure.figsize"] = (12, 7)
plt.imshow(elephant)
# ... oder hier nur der grüne Farbkanal einer Teilmatrix mit Spiegelung:
# In[76]:
plt.imshow(elephant[380:630,850:510:-1,0],
cmap=plt.cm.Greens_r,
interpolation="lanczos")
# Oder experimenteller: Der Durchschnittswert aller drei Kanäle ...
# In[77]:
ele2 = np.mean(elephant, axis=2)
print(ele2.shape)
plt.imshow(ele2, cmap = plt.cm.Greys_r)
# ... mit Anschließender Anwendung eines
# [2D Kernels über beide Dimensionen](http://docs.scipy.org/doc/scipy-dev/reference/generated/scipy.signal.convolve2d.html):
#
# Man muss genau hinsehen was passiert: durch so einen Kernel werden Kanten mit bestimmten Kontrastwechseln herausgefiltert!
# In[78]:
from scipy import signal
ckernel = np.array([[-1, -1, 0],
[-1, 0, 1],
[ 0, 1, 1]])
ele3 = signal.convolve2d(ele2, ckernel, boundary='symm', mode='same')
plt.imshow(ele3, cmap = plt.cm.bwr)
_ = plt.colorbar(shrink=.5)
# In[ ]: