Datenvisualisierung mit Matplotlib und pandas#
Import#
Zunächst importieren wir die benötigten Python-Bibliotheken. Neben pandas nutzen wir nun auch Matplotlib.
import matplotlib.pyplot as plt
import pandas as pd
%matplotlib inline
Einlesen der Daten#
Im letzten Kapitel haben wir bereits schnelle Visualisierungen nur mit pandas erstellt. Nun wollen wir diese Datenvisualisierungen mit Hilfe von Matplotlib etwas solider, besser lesbar und ansprechender gestalten. Dazu lesen wir unsere bekannte Datengrundlage wieder ein.
df = pd.read_json('../data/AvH-letters-with-tokens.json')
df.loc[:, 'date'] = pd.to_datetime(df.loc[:, 'date'], unit='ms')
print(df.shape)
df.head(3)
Visualisierung verschiedener Diagramme#
Balkendiagramm#
Zunächst möchten wir ein Balkendiagramm erstellen, das eine Übersicht über die zehn häufigsten Adressorte bietet. Dafür möchten wir die Größe der Grafik über den Parameter figsize= anpassen. Außerdem setzen wir einen Titel sowie Achsenbeschriftungen und deren Schriftgröße fest. Und ganz wichtig: Wir bringen die Balken in eine absteigende Reihenfolge.
Probieren Sie gerne verschiedene Werte bei den unterschiedlichen Parametern aus und schauen Sie, wie sich die Visualisierung verändert.
Matplotlib bietet eine Fülle von Möglichkeiten, die Visualisierungen nach eigenen Wünschen und Bedarfen anzupassen. Ein Blick in die Dokumentation hilft hier weiter und viele Anregungen zur Ausgestaltung von Visualisierungen erhält man über die Beispiele in der Matpltolib-Galerie
fig, ax = plt.subplots(figsize=(10,6))
# Erstellen der zu plottenden Series
df_top_places = df.loc[:, 'place'].value_counts()
# Auswahl der 10 häufigsten Orstangabe, Definition als Balkendiagramm, Zuweisung der ax
df_top_places.head(10).plot(kind='barh',
ax=ax)
# Formatierung der Überschrift des Diagramms
ax.set_title(label='Top 10 Ortsangaben zu den versandten Briefen',
family='serif',
color='grey',
weight='semibold',
size=14
)
# Formatierung und Beschriftung von x-Achse und y-Achse
ax.set_xlabel('Häufigkeit',
size=12
)
ax.set_ylabel('Ortsangabe',
size=12,
)
# Beschriftung der Features vergrößern
ax.set_yticklabels(ax.get_yticklabels(),
size=14)
# Reihenfolg der Balken invertieren
ax.invert_yaxis()
# Zeichnen nur der Gitterlinien der x-Achse
ax.xaxis.grid(True)
plt.show()
Für die Spalten sender und receiver können wir äquivalent vorgehen - allerdings müssen wir natürlich die Beschriftungen entsprechend anpassen.
Aufgabe: Erstellen Sie jeweils ein Diagramm zur Übersicht der häufigsten Absender und Empfänger der Briefe.
# your code
Histogramm#
Über ein Histogramm können wir eine Übersicht über die Verteilung der Brieflänge erhalten. Wir werden axes-Objekte nutzen, um unterschiedliche Histogramme mit verschiedenen Größe der bins, also der Anzahl der Intervalle, in die die Werte eingeordnet werden sollen, zu plotten. Beim Erstellen der subplots geben wir daher mit nrows=, die Anzahl der Zeilen an, in denen jeweils ein Histogramm zu sehen sein wird. Zusätzlich geben wir auch an, dass alle Unterdiagramme die gleichen x- bzw. y-Skalen nutzen. Bei den einzelnen ‘plot’-Befehlen nutzen wir den Paramter ax und die Indexierung, um die einzelnen Diagramme in eine der Zeilen zu platzieren.
fig, ax = plt.subplots(nrows=3,
figsize=(12,8),
sharex=True,
sharey=True)
df.loc[:, 'nr_token' ].plot(kind='hist',
bins=30,
title='bins=30',
ax=ax[0])
df.loc[:, 'nr_token' ].plot(kind='hist',
bins=50,
title='bins=50',
ax=ax[1],
color='green')
df.loc[:, 'nr_token' ].plot(kind='hist',
bins=80,
title='bins=80',
ax=ax[2],
color='orange')
# einzelne ylabel für die Unterdiagramme ausschalten
for x in range(3):
ax[x].set_ylabel(ylabel='')
# Beschriftungen des Gesamtdiagramms
fig.suptitle('Verteilung der Brieflängen',
size=16)
fig.supylabel('Häufigkeit',
size=14)
fig.supxlabel('Anzahl',
size=14)
fig.tight_layout()
plt.show;
Durchschnittliche Brieflänge nach Jahren#
Wenn wir unsere vorliegenden Daten nach Jahren aufbereiten wollen, haben wir ein Problem, das wir schon bemerkt haben: Nicht für jedes Jahr sind Briefe, also auch Daten, vorhanden. Wenn wir unsere gruppierten Dataframes visualisieren lassen, dann werden nur die Daten zu den darin enthaltenen Jahren gemacht. Wir benötigen also ein Weg, um auch die Jahre mit in unsere Visualisierung aufzunehmen, die nicht im Dataframe enthalten sind.
Dazu erstellen wir zunächst aus einem Dictionary einen Dataframe. Das Dictionary hat nur den key year und somit hat der Dataframe nur die Spalte year. Die Werte dieser Spalte sind die Jahre zwischen 1796 und 1860, die wir als values des Dictionaries mit der range()-Funktion übergeben.
Als nächstes gruppieren wir mit Hilfe der Datumspalte unsere bekannten Daten nach Jahren und nutzen mit der Methode agg() zwei Spalten, die wir aggregieren wollen: Zum einen die Spalte nr_token bei der wir den Durschschnitt bilden und zum anderen nutzen wir die Spalte sender, um mit size die Anzahl der Brief pro Jahr zu bestimmen. Danach ergänzen wir bei diesem Dataframe noch die Spalte year, die wir aus dem Index des Dataframes, der durch die Gruppierung nach Jahren eine Time-Index enthält. Dann geben wir die Dimensionalität und die ersten Zeilen dieses Dataframes aus.
all_years = pd.DataFrame({'year': range(1796, 1860)})
df_grouped = df.groupby(df.loc[:, 'date'].dt.to_period('Y')).agg({'nr_token': 'mean',
'sender': 'size'})
df_grouped.loc[:, 'year'] = df_grouped.index.year
print(df_grouped.shape)
df_grouped.head()
Jetzt *mergen* wir die zwei Dataframes, um tatsächlich die Werte aller Jahre in der Visualisierung anzeigen zu können. Über den Parameter on='year' mergen wir über die in beiden Dataframes enthaltenen Spalte year den Dataframe mit allen Jahreszahlen mit den ausgewählten Spalten des Dataframes, der die Grupperung nach Jahren enthält. Die leeren Werten füllen wir mit fillna() mit dem Wert ‘0’ auf. Anschließend zeigen wir den Dataframe an, der nun alle 64 Jahre zwischen 1796 und 1860 sowie die drei Spalten year, nr_token und sender enthält.
df_all_years = pd.merge(all_years,
df_grouped.loc[:, ['year', 'nr_token', 'sender']],
on='year',
how='left').fillna(0)
print(df_all_years.shape)
df_all_years.head()
Nun können wir die Daten nach Jahren - und zwar nach allen Jahren - plotten. Wir nutzen wieder eine figure, setzen aber zwei axes darauf. Eine Zeile zeigt die durchschnittliche Länge der Briefe an, das andere Balkendiagramm zeigt die Anzahl der Brief pro Jahr. Auf diese Weise können wir direkt den Bezug zwischen durchschnittlicher Brieflänge und Anzahl pro Brief herstellen. Was fällt Ihnen z.B. für das Jahr 1848 auf? Die Einstellungen zu den jeweiligen Beschriftungen des Diagramms sollte aus dem nachfolgenden Codeblock hervorgehen.
fig, ax = plt.subplots(nrows=2,
figsize=(16, 6),
sharex=True)
# Balkendiagramme plotten
ax[0].bar(df_all_years.loc[:,'year'], df_all_years.loc[:, 'nr_token'])
ax[1].bar(df_all_years.loc[:,'year'], df_all_years.loc[:, 'sender'], color='green')
# Gitternetzlinien einblenden
ax[0].yaxis.grid(True)
ax[1].yaxis.grid(True)
# Titel der einzelnen Diagramme
ax[0].set_title('Durchschnittliche Länge der Briefe nach Jahren',
size=14)
ax[1].set_title('Anzahl der Briefe nach Jahren',
size=14)
# Beschriftung der y-Achse der einzelnen Diagrmme
ax[0].set_ylabel('Anzahl Wörter',
size=12)
ax[1].set_ylabel('Anzahl Briefe',
size=12)
# Beschriftungen des Gesamtdiagramms
fig.suptitle('Durchschnittliche Länge und Anzahl der Briefe nach Jahren zwischen 1796 und 1859',
size=16,
weight='bold')
fig.supxlabel('Jahr',
size=14)
fig.tight_layout()
plt.show;