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)

For interactive reading and executing code blocks Binder and find xml.ipynb, or install Python and JupyterLab locally.

Erstellen, manipulieren und kopieren Sie halbstrukturierte Dateien in Form von xlsx-Workbooks und JSON-Dateien: 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 ist eine textbasierte Markup-Sprache zur Darstellung von Dokumentenstruktur und Daten. Ein XLSX-Workbook ist ein Office Open XML-Paket: Es wird normalerweise als ZIP-Archiv mit XML-Teilen und anderen Ressourcen gespeichert. Anwendungen wie Tabellenkalkulationsprogramme interpretieren diese Teile und präsentieren die Arbeitsmappe den Benutzern. HTML hat eigene Syntax- und Parsing-Regeln, obwohl HTML auch mit XML-Syntax seriellisiert werden kann. Andere strukturierte Dateiformate wie JSON (JavaScript Object Notation) ist ein separates Textformat, das strukturierte Daten mit Objekten, Arrays, Strings, Zahlen, Boolean-Werten und null darstellen kann. Der Umgang mit strukturierten Daten ist wichtig in der Wasserressourcen-Engineering, wo wir in der Praxis oft am Austausch von Informationen mit Managern interessiert sind, die Büro-Workbooks bevorzugen (.xlsxDateien). Auch können JSON-Dateien effizient numerische Modelle-Setups speichern (z.B. für BASEMENT).

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 + 1):
  for j in range(1, n + 1):
	  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.load_workbook(...) akzeptiert Optionen:

  • read_only=True für eine speichereffiziente, schreibgeschützte Sicht auf ein bestehendes Arbeitsbuch;

  • data_only=False, um den Formeltext freizugeben, oder data_only=True, um den zuletzt von einer Tabellenkalkulation gespeicherten Cache-Wert freizugeben;

  • keep_vba=True, um VBA-Inhalte zu erhalten, ohne es zu bearbeiten.

Schreib-only-Modus gilt bei der Erstellung eines neuen Arbeitsbuchs: openpyxl.Workbook(write_only=True).

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=1, columns="ABC"):
  """Return one list per requested worksheet column."""
  return [
	  [ws[f"{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

openpyxl kann Formelnzeichenfolgen lesen und schreiben, aber es berechnet sie nicht. Mit data_only=False gibt eine Formelzelle ihren Formeltext aus. Mit data_only=True stellt sie das zuletzt von einer Tabellenkalkulation gespeicherte Kached-Ergebnis frei, das möglicherweise fehlt oder stillsteht. Die Mitgliedschaft in openpyxl.utils.FORMULAE kann informativ sein, aber sie bewertet oder validiert keine Formel. 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. 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, *, read_only=False,
			   data_only=False, sheet_name=None):
	  self.wb = oxl.load_workbook(
		  filename=workbook_name,
		  read_only=read_only,
		  data_only=data_only,
	  )
	  self.ws = self.wb[sheet_name] if sheet_name else self.wb.active

  def read_columns(self, start_row=1, columns="ABC"):
	  return [
		  self.ws[f"{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, *, data_only=False,
			   sheet_name=None):
	  super().__init__(
		  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 tritt Python mit dem Standard-Bibliotheksmodul json ein, das JSON kodiert und dekodiert. JSON-Werte können Objekte, Arrays, Strings, Zahlen, true,false oder null sein. Ein Objekt enthält String-Namen, die mit Werten gepaart sind, beispielsweise {"name": "Vanilla Flow"}. Ein Array ist eine geordnete Wertefolge, z.B. [1, 3, 7, 31].

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. 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.