Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Strukturierte Daten (XML, xlsx & JSON)

Create, manipulate, and copy semi-structured data files in the form of xlsx-workbooks and JSON files. For interactive reading and executing code blocks Binder and find xml.ipynb, or install Python and JupyterLab locally.

Dieses Kapitel beginnt mit Hintergrundinformationen über XML und was XML mit Workbooks zu tun hat, und JSON: XML ist eine Abkürzung für Extensible **M*arkup Language, die Regeln für die Kodierung von Dokumenten definiert. XML wurde für eine unkomplizierte Nutzung über das Internet konzipiert und wir begegnen XML-Dokumenten die ganze Zeit, auf Websites (z.B. XHTML), in Form von Office-Dokumenten (z.B. Office Open XML wie docx, pptx oder xlsx), oder Podcasts (z.B. RSS). Die Stärke des XML-Formats ist seine Eigenschaft, sowohl maschinenlesbar zu sein (d.h. ein Computer kann XML-Dateien verarbeiten) als auch menschlesbar (d.h. wir können es wie eine Zeitung lesen). So ist die Eingabe von Formeln in einem xlsx-Arbeitsbuch gleichzeitig maschinenlesbar und menschlesbar, da Menschen und Computer die Formeln in diesem XML-Rahmen interpretieren und auswerten können. Andere Dateiformate wie JSON (JavaScript Object Notation) ähneln XML und Python können Informationen aus und exportieren Informationen in beide Formate. In der Wasserressourcen-Engineering und Forschung, zum Beispiel, sind wir vor allem am Austausch von Informationen mit Büro-Workbooks (xlsx Dateien), oder mit JSON-Dateien, die Randbedingungen für numerische Modelle bieten interessiert.

Arbeitsbuch (xlsx)

Warum wollen wir überhaupt mit Arbeitsmappen kommunizieren? Wir haben bereits gesehen, dass Python viel mächtiger ist als Büroprogramme für die systematische Analyse von Daten. Python erfordert jedoch die Abstraktion von Daten in unseren Köpfen, um beispielsweise die Struktur einer geschachtelten Liste zu visualisieren. Aus diesem Grund sind Daten von und für das Marketing, Ihr Chef oder Behörden oft erforderlich, um visuell einfach zu bedienende Arbeitsmappenformate zu haben, die schnell übersehen werden können. Dennoch wollen wir mit Python den Inhalt solcher Workbook-Informationen effizient nutzen, und wir wollen visuell vereinfachte Ausgabe produzieren, die jeder ohne Python-Wissen lesen kann.

Wir haben bereits gesehen, dass pandas einfache Routinen für den Import und Export von Daten aus bzw. zu Arbeitsmappen bietet (vgl. file reading and writing with pandas). pandas verwendet in erster Linie openpyxl, je nachdem, was in der aktiven Python-Umgebung verfügbar ist. openpyxl ist eine der leistungsfähigsten Optionen für die Handhabung von Arbeitsmappe mit Python (Anm.: diese Behauptung ist subjektiv) und dieser Abschnitt führt openpyxl ein.

Diese Einführung nutzt die folgenden arbeitsbuchbezogenen Begriffe:

  • workbook ist die Hauptdatei xlsx, mit der wir zusammenarbeiten (auch Spreadsheet genannt);

  • sheet ist der tabellarische Inhalt eines Arbeitsbuchs und ein Arbeitsbuch kann mehrere Blätter haben;

  • ** Spalte*** sind vertikale Linien in einem Blatt;

  • **) sind horizontale Linien in einem Blatt;

  • Zellen*s sind Elemente eines Blattes.

Ein Workbook erstellen

openpyxl hat eine Workbook-Klasse, die es ermöglicht, Arbeitsmappen mit Daten zu erstellen und zu füllen. Typischerweise wird eine Instanz der Workbook-Klasse wb genannt und Arbeitsblattvariablen werden ws genannt.

import numpy as np
import openpyxl as oxl
wb = oxl.Workbook()  # create a Workbook instance
ws = wb.active  # activate worksheet
ws.title = "Gaussian 2D"  # name worksheet
ws["A1"] = "Gaussian sample data"  # write to cell A1

# generate some data
x, y = np.meshgrid(np.linspace(-1, 1, 20), np.linspace(-1, 1, 20))
dis = np.sqrt(x * x + y * y)
sigma, mu = 1.0, 0.0
gaussian = np.exp(-((dis - mu) ** 2 / (2.0 * sigma ** 2)))

# write data to worksheet
m, n = gaussian.shape
for i in range(1, m):
    for j in range(1, n):
        _ = ws.cell(row=i+1, column=j, value=gaussian[i-1, j-1])

print("Workbook data in cell A2: "+ str(ws["A2"].value))
print("Corresponds to np.array value: " + str(gaussian[0, 0]))

# save and close (destruct object) workbook
wb.save(filename="data/python_workbook.xlsx")
wb.close()
Workbook data in cell A2: 0.3678794411714422
Corresponds to np.array value: 0.3678794411714422
python excel file creation

Figure 1:Das daraus resultierende Arbeitsbuch.

Lesen und Manipulate eines vorhandenen Arbeitsbuches

openpyxl liest bestehende Arbeitsbücher mit openpyxl.load_workbook(filename=str()). Diese Funktion akzeptiert optionale Keyword-Argumente, wie:

  • read_only=BOOLEAN entscheidet, ob ein Arbeitsbuch im read-only Modus geöffnet wird. Ein Arbeitsbuch kann nur manipuliert werden, wenn read_only=False (dies ist die Standardoption, die nützlich sein kann, um große Dateien zu handhaben oder sicherzustellen, dass grafische Objekte nicht verloren gehen).

  • write_only=BOOLEAN entscheidet, ob ein Arbeitsbuch im write only Modus geöffnet wird. Wenn write_only=True (die Standardeinstellung ist False) keine Daten aus einem Arbeitsbuch gelesen werden können, aber die Schreibdaten sind deutlich schneller (d.h. diese Option ist für das Schreiben großer Datensätze nützlich).

  • data_only=BOOLEAN bestimmt, ob Zellformeln oder Zelldaten gelesen werden. Wenn z.B. der Inhalt einer Workbook-Zelle =PI(), data_only=False (dies ist die Standardoption) den Zellwert als =PI() und data_only=True lautet, lautet der Zellwert als 3.14159265359.

  • keep_vba=BOOLEAN kontrolliert, ob Visual Basic-Elemente (Makros) aufbewahrt werden oder nicht. Die Standardeinstellung ist keep_vba=False (d.h. keine Konservierung) und keep_vba=True wird immer noch keine Änderung von Visual Basic-Elementen ermöglichen.

Wenn read_only=False, können wir Zellwerte und auch Zellformate manipulieren, einschließlich Datenformate (z.B. Datum, Uhrzeit und viel mehr), Font-Eigenschaften (und viele weitere Zell-Styles), oder Farben in HEX Color Code (findet Ihre Lieblingsfarbe hier). Das folgende Beispiel öffnet die oben erstellte python_workbook.xlsx, fügt ein neues Arbeitsblatt hinzu, verdeutlicht die Implementierung von Zellstilen und füllt das Arbeitsbuch mit zufälligen Entladungsmessungen.

import datetime
from openpyxl.styles import Font, Alignment, PatternFill
wb = oxl.load_workbook(filename="data/python_workbook.xlsx", read_only=False)
ws = wb.create_sheet(title="Discharge")

# define title styles
title_font = Font(name="Tahoma", size="11", bold=True, italic=True, color="C1D0DE")
title_fill = PatternFill(fill_type="solid", start_color="050505", end_color="073AD4")
title_align = Alignment(horizontal='center', vertical='bottom', text_rotation=0,
                        wrap_text=False, shrink_to_fit=False, indent=0)

date_time_format = "yyyy-mm-dd hh:mm:ss"
ws["A1"] = "Date-Time (%s)" % date_time_format

title_cell_flow = ws["B1"]
title_cell_flow.value = "Discharge (CMS)"
title_cell_flow.font = title_font
title_cell_flow.fill = title_fill
title_cell_flow.alignment = title_align

# define time period and time delta of 1 hour = 3600 seconds
current_date_time = datetime.datetime(2040, 12, 24, 0, 0)
dt = datetime.timedelta(seconds=3600)

# write random discharges to workbooks
for row in ws.iter_rows(min_row=2, max_row=26, min_col=1, max_col=2):
    row[0].value = current_date_time
    row[0].number_format = date_time_format
    row[1].value = np.random.random_sample(size=None) * 100
    row[1].number_format = "0.00"
    current_date_time += dt
    
wb.save("data/python_workbook_reloaded.xlsx")
wb.close()
python xlsx styling

Figure 2:Das aktualisierte Arbeitsbuch.

Der folgende Codeblock bietet die kurze Helper-Funktion read_columns, um nur eine oder mehrere Spalten in eine (nässte) list zu lesen ( liest bis die maximale Anzahl der Zeilen, die von ws.rows definiert sind, in einem Arbeitsbuch erreicht ist). Eine ähnliche Funktion kann zum Lesen von Zeilen geschrieben werden.

def read_columns(ws, start_row=0, columns="ABC"):
    return [ws["{}{}".format(column, row)].value for row in range(start_row, ws.max_row + 1) for column in columns]

# example usage:
wb = oxl.load_workbook(filename="data/python_workbook.xlsx", read_only=False)
ws = wb.active
col_D = read_columns(ws, start_row=2, columns="D")
col_F = read_columns(ws, start_row=2, columns="F")
wb.close()

Formeln in Arbeitsbüchern

Das optionale Keyword-Argument data_only=False ermöglicht das Lesen von Workbook-Formeln anstelle von Zellwerten. Allerdings werden nicht alle Workbook-Formeln durch openpyxl erkannt und im Zweifelsfall ist ein schmutziger Try-and-Eror-Ansatz das einzige Mittel. Ändern Sie im folgenden Beispiel SQRT an die betreffende Formel.

from openpyxl.utils import FORMULAE
print("SQRT" in FORMULAE)
True

(Un)merge Cells

Zusammenführende und nicht-verschmelzende Zellen ist eine beliebte Bürofunktion für Stilzwecke und openpyxl bietet auch Funktionen zur Durchführung von Zusammenführungen:

ws.merge_cells(start_row=1, end_row=3, start_column=1, end_column=2)
ws.unmerge_cells(start_row=1, end_row=3, start_column=1, end_column=2)

Diagramme (Plots)

Im unwahrscheinlichen Fall, dass Sie Grundstücke direkt in Arbeitsbücher mit Python einfügen möchten (matplotlib ist sowieso leistungsfähiger), openpyxl bietet hierzu auch Features. Um die Erstellung eines Flächendiagramms zu illustrieren, verwendet der nachfolgende Codeblock die erste Spalte von Zufallswerten in der oben erstellten python_workbook.xlsx.

from openpyxl.chart import AreaChart, Reference, Series

wb = oxl.load_workbook(filename="data/python_workbook.xlsx", read_only=False)
ws = wb.active

chart = AreaChart()
chart.title = "Random Gaussian"
chart.style = 10
chart.x_axis.title = "Cell row"
chart.y_axis.title = "Random value (-)"

col_D = Reference(ws, min_col=4, min_row=2, max_row=20)
col_F = Reference(ws, min_col=6, min_row=2, max_row=20)

chart.add_data(col_F, titles_from_data=False)
chart.add_data(col_D, titles_from_data=False)

ws.add_chart(chart, "B2")

wb.save("data/python_workbook_chart.xlsx")
wb.close()
plot python excel file

Figure 3:Das Arbeitsbuch mit dem in Python erstellten Grundstück.

Andere Arbeitsmappen sind verfügbar und ihre Umsetzung (noch: warum würden Sie?) wird in der openpyxl docs erklärt.

Workbook Manipulation anpassen

Es gibt viele Möglichkeiten, Arbeitsmappen zu modifizieren und openpyxl bietet nah an “shovel-ready” Methoden, um ein Arbeitsbuch zu manipulieren. Um jedoch zu vermeiden, diese Lektion jedes Mal neu zu lesen, wenn Sie ein Arbeitsbuch manipulieren möchten, ist es bequemer, Ihre eigenen Arbeitsmappe Manipulationsklassen bereit zu arbeiten. Zu diesem Zweck definiert der folgende Codeblock benutzerdefinierte Read und Write-Klassen, wobei Read die Elternklasse der Write-Klasse ist (Recall the Section on inheritance of classes). Die Read-Klasse kann maßgeschneiderte Funktionen zum Lesen bestimmter Spalten, Zeilen oder Arrays enthalten. Der folgende Codeblock nutzt auch die oben definierte read_columns-Funktion, die als Methode der Read-Klasse implementiert wird.

import openpyxl as oxl

class Read:
    def __init__(self, workbook_name="", *args, **kwargs):
        read_only = kwargs.get("read_only")
        data_only = kwargs.get("data_only")
        sheet_name = kwargs.get("sheet_name")
        self.wb = oxl.load_workbook(filename=workbook_name, read_only=read_only, data_only=data_only)
        if sheet_name:
            self.ws = self.wb[sheet_name]
        else:
            self.ws = self.wb[self.wb.sheetnames[0]]
            
    def read_columns(self, start_row=0, columns="ABC"):
        return [self.ws["{}{}".format(column, row)].value for row in range(start_row, self.ws.max_row + 1) for column
                in columns]
            
    def __call__(self):
        print(dir(self))

            
class Write(Read):
    def __init__(self, workbook_name="", *args, **kwargs):
        data_only = kwargs.get("data_only")
        sheet_name = kwargs.get("sheet_name")
        Read.__init__(self, workbook_name=workbook_name, read_only=False, data_only=data_only, sheet_name=sheet_name)

Ein erweitertes Beispiel-Skript mit komplexer Read und WriteKlassen kann von der course repository heruntergeladen werden.

Ein Beispiel für Wasserressourcentechnik und Forschung

Die ökologische Sanierung oder Verbesserung der Flüsse erfordert unter anderem Informationen über bevorzugte Wassertiefen und Strömungsgeschwindigkeiten von Zielfischarten. Diese Informationen werden von Biologen erstellt und dann oft in Form von sogenannten habitat Eignungsindex (HSI)Kurven in Arbeitsmappenformaten bereitgestellt. Typischerweise produzieren wir geospatial explizite Daten über Wassertiefe und Strömungsgeschwindigkeit mit numerischen Modellen. Die Ausgabe von zwei oder dreidimensionalen numerischen Modellen ist viel zu groß, um mit Büroanwendungen zu handhaben. Wir brauchen also ein fortschrittliches Tool, wie Python, um die geospatial expliziten Daten zu verarbeiten und HSI-Kurven von Arbeitsmappen zu lesen und zu interpolieren. Wie sieht das technisch aus? Die exercises on geospatial Python lässt Sie in den aquatischen Lebensraum (Bewertungen) eintauchen.

JSON

JavaScript Object Notation (JSON) Dateien haben eine ähnliche Struktur wie XML und ermöglichen die strukturierte Speicherung von (menschlesbaren) Daten. Zum Beispiel verwendet der Zahlencode BASEMENT v.3.x (read more in the numerical modeling chapter) eine model.json und eine simulation.json-Datei, um Modell-Setup-Parameter wie Materialeigenschaften zu speichern. So beinhaltet die Automatisierung von numerischen Modelleinstellungen mit Python die Änderung von Modellparametern, die in json-Dateien gespeichert sind. Hier geht Python mit dem JSON Paket und pandas’ JSON Modulen ein.

JSON Dateistruktur

Eine JSON-Datei besteht aus zwei Arten von Datenstrukturen, die diktionäre Objekte und arrays in Form von Listen von Werten sind. Die dictionary-Objekte in einer JSON-Datei entsprechen dem gleichen Format, das wir bereits in Python kennen: Paare von keys (Namen) und Werte, die von Curly Brackets (braces) {"name": value} umfasst sind. Die value kann ein string, numeric, eine komma-separierte list[] (array) von Daten oder ein anderes Dictionary sein. Das folgende Beispiel zeigt eine JSON-Datei namens river_struct.json mit einer RIVER-Taste, die ein geschachteltes Wörterbuch als Wert hat. Der Wert-Dictionary enthält drei Schlüssel (NAME,GEOMETRY und HYDRAULICS).

{
	"RIVER": {
		"NAME": "Vanilla Flow",
		"GEOMETRY": {
			"REGIONS": [
				{
				  "type": "wet",
				  "name": "riverbed"
				},
				{
				  "type": "dry",
				  "name": "floodplain"
				}
			],
			"FLOWBOUNDARIES": [
				{
				  "name": "Inflow",
				  "nodes": [1, 3, 7, 31]
				},
				{
				  "name": "Outflow",
				  "nodes": [89, 90, 76, 69, 95]
				}
			]
		},
		"HYDRAULICS": {
			"BOUNDARY": [
				{
					"discharge_file": "/simulation/directory/Inflow.txt",
					"name": "Inflow",
					"slope": 0.005,
					"type": "hydrograph"
				},
				{
					"name": "Outflow",
					"type": "zero_gradient"
				}
			],
			"FRICTION": {
				"cobble": 20.0,
				"gravel": 26.0,
				"sand": 41
			}
		},
		"LOCATION": [48.744079, 9.103928]
	}
}
{'RIVER': {'NAME': 'Vanilla Flow', 'GEOMETRY': {'REGIONS': [{'type': 'wet', 'name': 'riverbed'}, {'type': 'dry', 'name': 'floodplain'}], 'FLOWBOUNDARIES': [{'name': 'Inflow', 'nodes': [1, 3, 7, 31]}, {'name': 'Outflow', 'nodes': [89, 90, 76, 69, 95]}]}, 'HYDRAULICS': {'BOUNDARY': [{'discharge_file': '/simulation/directory/Inflow.txt', 'name': 'Inflow', 'slope': 0.005, 'type': 'hydrograph'}, {'name': 'Outflow', 'type': 'zero_gradient'}], 'FRICTION': {'cobble': 20.0, 'gravel': 26.0, 'sand': 41}}, 'LOCATION': [48.744079, 9.103928]}}

Lesen (Decode) und Schreiben (Encode) JSON-Dateien mit der json Bibliothek

JSON-Dateien können in vielen Programmiersprachen implementiert werden, einschließlich HTML und Python. Deshalb können Jupyter Notebooks (wie in diesem eBook verwendet) auch in Python ausgeführt und als Webseite angezeigt werden. Python verfügt über eine integrierte json Bibliothek, die JSON-Dekodierung und Codierung ermöglicht. Die json Bibliothek bietet eine json.dumps(DATA) Methode an “dump” (d.h. encode) Daten im JSON-Format. Die json.load()-Funktion liest Daten aus JSON-Dateien. Das folgende Beispiel veranschaulicht die Kodierung und Dekodierung eines willkürlich geschachtelten Datensatzes mit der json Bibliothek.

import json
# create arbitrary nested data (list, dictionary, tuple)
data_for_json = ["list_element1", {"dict_key": ("tuple_element", "text", 1.0, None)}]

# create a json file
json_file = open("data/my-first.json", mode="w+")
# encode the random nested data list in json format and write to file
json_file.write(json.dumps(data_for_json))
# close file
json_file.close()

# re-open the json file to read data
with open("data/my-first.json", mode="r") as re_opened_file:
    raw_data = re_opened_file.readline()

# decode json data in a Python variable
data_from_json = json.loads(raw_data)
print(json.dumps(data_from_json))
["list_element1", {"dict_key": ["tuple_element", "text", 1.0, null]}]

Die Python docs bietet weitere Optionen und Beschreibungen über die Bibliothek json. Hier werden wir jedoch (noch einmal) die pandas-Bibliothek nutzen, die leistungsstarke Funktionen für die Handhabung von Json-Daten bietet.

Lesen (Decode) und Schreiben (Encode) JSON-Dateien mit pandas

pandas (recalldata and file handling with pandas) ermöglicht das Lesen von JSON-Dateien in sein bequemes Tabellenformat mit eingebetteter Nutzung der jsonBibliothek. Der folgende Codeblock verwendet die pandas.read json(FILE)-Funktion, um die oben dargestellte RIVER-Probendatei zu lesen (download river struct.json).

import pandas as pd
river = pd.read_json("data/river_struct.json")
print(river)
                                                        RIVER
NAME                                             Vanilla Flow
GEOMETRY    {'REGIONS': [{'type': 'wet', 'name': 'riverbed...
HYDRAULICS  {'BOUNDARY': [{'discharge_file': '/simulation/...
LOCATION                                [48.744079, 9.103928]

Da ein Fluss ohne Daten wie Eis ohne Geschmack ist, werden wir der Datenstruktur (random) Daten über Fließeigenschaften hinzufügen. Nehmen wir an, dass wir die Daten von river_struct.json verwendet haben, um eine stationäre Entladung in einem zweidimensionalen numerischen Modell zu simulieren. Als Ergebnis haben wir zwei regelmäßige Gitter (Arrays) mit Daten über Strömungsgeschwindigkeit und Wassertiefe. Nun wollen wir sowohl die Strömungsgeschwindigkeit als auch die Wassertiefe Arrays in Form einer Ergebnisstruktur (diktionär) an river_struct.json anhängen und dem Fluss einen neuen Namen geben.

# create random data
import numpy as np
h = np.random.weibull(np.arange(0,100)).reshape(10, 10)
u = np.random.weibull(np.arange(0,100)).reshape(10, 10)

# append RESULTS row to pandas dataframe
river_dict = river.to_dict()
river_dict["RIVER"].update({"RESULTS": {"water_depth": h, "flow_velocity": u}})
updated_river = pd.DataFrame.from_dict(river_dict)

# re-NAME RIVER
updated_river.loc["NAME", "RIVER"] = "Honey river"
print(updated_river)

# export to JSON
updated_river.to_json("data/river_results.json")
                                                        RIVER
NAME                                              Honey river
GEOMETRY    {'REGIONS': [{'type': 'wet', 'name': 'riverbed...
HYDRAULICS  {'BOUNDARY': [{'discharge_file': '/simulation/...
LOCATION                                [48.744079, 9.103928]
RESULTS     {'water_depth': [[0.0, 1.4016850027236283, 0.6...
python json file creation manipulation

Figure 4:Das daraus resultierende Arbeitsbuch.

Erfolgsprüfung lernen

Nehmen Sie den Lernerfolgstest für dieses Jupyter Notebook.