Anpassung der Zeit- und Datumsskalen in ggplot2


In the last post of this series, we dealt with axis systems. In this post, we are also dealing with axes but this time we are taking a look at the position scales of dates, time, and datetimes. Since we at statworx are often forecasting - and thus plotting - time series, this is an important issue for us. The choice of axis ticks and labels can make the message conveyed by a plot clearer. Oftentimes, some points in time are – e.g. due to their business implications – more important than others and should be easily identified. Unequivocal, yet parsimonious labeling is key to the readability of any plot. Luckily, ggplot2 enables us to do so for dates and times with almost any effort at all.
We are using ggplot's economics data set. Our base Plot looks like this:
Im letzten Beitrag dieser Serie haben wir uns mit Achsensystemen beschäftigt. In diesem Beitrag geht es ebenfalls um Achsen, jedoch betrachten wir diesmal die Positionsskalen von Datumsangaben, Uhrzeiten und Zeitstempeln. Da wir bei statworx häufig Prognosen erstellen – und somit Zeitreihen visualisieren –, ist dies ein wichtiges Thema für uns. Die Wahl der Achsenticks und -beschriftungen kann die Botschaft, die ein Diagramm vermittelt, klarer machen. Oft sind bestimmte Zeitpunkte – beispielsweise aufgrund ihrer geschäftlichen Bedeutung – wichtiger als andere und sollten leicht erkennbar sein. Eine eindeutige, aber sparsame Beschriftung ist entscheidend für die Lesbarkeit eines Diagramms. Glücklicherweise ermöglicht uns ggplot2, dies für Datums- und Zeitangaben mit minimalem Aufwand zu tun.
Wir verwenden den „economics“-Datensatz von ggplot. Unser Basisdiagramm sieht folgendermaßen aus:

base_plot <- ggplot(data = economics) +
geom_line(aes(x = date, y = unemploy),
color = "#09557f",
alpha = 0.6,
size = 0.6) +
labs(x = "Date",
y = "US Unemployed in Thousands",
title = "Base Plot") +
theme_minimal()
Skalentypen
Derzeit unterstützt ggplot2 drei Klassen für Datums- und Zeitangaben: POSIXct, Date und hms. Abhängig von der verwendeten Klasse können Achsenticks und -beschriftungen mit den entsprechenden Skalierungsfunktionen gesteuert werden:
- scale_*_datetime für POSIXct
- scale_*_date für Date
- scale_*_time für hms
Zudem wird zwischen der x- und y-Achse unterschieden. Um eine Skalierung für die x-Achse vorzunehmen, wird scale_x_* verwendet, für die y-Achse entsprechend scale_y_*.
Der Einfachheit halber wird in den folgenden Beispielen nur scale_x_date verwendet, aber alle besprochenen Argumente funktionieren gleichermaßen für die anderen genannten Skalierungsfunktionen.
Kleine Anpassungen
Lassen wir es einfach angehen. Mit dem Argument limits kann der darzustellende Bereich von Datums- oder Zeitwerten festgelegt werden. Dafür müssen zwei Werte der entsprechenden Datums- oder Zeitklasse angegeben werden.

base_plot +
scale_x_date(limits = as.Date(c("1980-01-01","2000-01-01"))) +
ggtitle("limits = as.Date(c("1980-01-01","2000-01-01"))")
Das expand-Argument stellt sicher, dass ein Abstand zwischen den dargestellten Daten und den Achsen besteht. Die multiplikative Konstante wird mit dem Wertebereich der dargestellten Daten multipliziert, während die additive Konstante mit einer Einheit der gezeigten Daten multipliziert wird. Die Summe der beiden resultierenden Abstände wird als Puffer zu den Achsengrenzen hinzugefügt. Der dadurch entstehende Leerraum wird an beiden Enden der x-Achse (links und rechts) oder der y-Achse (oben und unten) ergänzt.

base_plot +
scale_x_date(expand = c(0, 5000)) + #5000/365 = 13.69863 years
ggtitle("expand = c(0, 5000)")
Das Argument Position legt fest, wo die Beschriftungen angezeigt werden: Entweder „links“ oder „rechts“ von der y-Achse oder „oben“ oder „unten“ auf der x-Achse.

base_plot +
scale_x_date(position = "top") +
ggtitle("position = "top"")
Achsenticks und Gitterlinien
Wichtiger als die bisher besprochenen kosmetischen Anpassungen sind die Achsenticks. Es gibt mehrere Möglichkeiten, die Achsenticks für Datums- und Zeitangaben zu definieren. Dabei unterscheidet man zwischen den beschrifteten Hauptunterteilungen (major breaks) und den unbeschrifteten Nebenunterteilungen (minor breaks), die lediglich durch Gitterlinien markiert werden. Diese können mit den Argumenten breaks und minor_breaks angepasst werden. Die Unterteilungen können entweder durch einen numerischen Vektor mit exakten Positionen oder durch eine Funktion definiert werden, die die Achsengrenzen als Eingabe nimmt und die Unterteilungen als Ausgabe liefert. Alternativ können die Argumente auf NULL gesetzt werden, um (Neben-)Unterteilungen vollständig auszublenden. Diese Optionen sind besonders nützlich, wenn unregelmäßige Abstände zwischen den Unterteilungen gewünscht sind.

base_plot +
scale_x_date(breaks = as.Date(c("1970-01-01", "2000-01-01")),
minor_breaks = as.Date(c("1975-01-01", "1980-01-01",
"2005-01-01", "2010-01-01"))) +
ggtitle("(minor_)breaks = fixed Dates")

base_plot +
scale_x_date(breaks = function(x) seq.Date(from = min(x),
to = max(x),
by = "12 years"),
minor_breaks = function(x) seq.Date(from = min(x),
to = max(x),
by = "2 years")) +
ggtitle("(minor_)breaks = custom function")

base_plot +
scale_x_date(breaks = NULL,
minor_breaks = NULL) +
ggtitle("(minor_)breaks = NULL")
Eine weitere und sehr bequeme Möglichkeit, regelmäßige Unterteilungen zu definieren, sind die Argumente date_breaks und date_minor_breaks. Beide Argumente nehmen als Eingabe einen Zeichenvektor, der aus einer Zeichenkette zur Angabe der Zeiteinheit („sec“, „min“, „hour“, „day“, „week“, „month“ oder „year“) und einer ganzen Zahl besteht, die die Anzahl der genannten Einheiten zur Festlegung der Intervallunterteilungen angibt.

base_plot +
scale_x_date(date_breaks = "10 years",
date_minor_breaks = "2 years") +
ggtitle("date_(minor_)breaks = "x years"")
Wenn beide Argumente angegeben werden, hat date(_minor)_breaks Vorrang vor (minor_)breaks.
Achsenbeschriftungen
Ähnlich wie bei den Achsenticks kann das Format der angezeigten Beschriftungen entweder über das labels- oder das date_labels-Argument definiert werden. Das labels-Argument kann entweder auf NULL gesetzt werden, wenn keine Beschriftungen angezeigt werden sollen, oder als Funktion verwendet werden, die die Unterteilungen als Eingabe nimmt und die Beschriftungen als Ausgabe liefert. Alternativ kann dem Argument ein Zeichenvektor mit Beschriftungen für alle Unterteilungen übergeben werden. Dies ist besonders nützlich, da auf diese Weise nahezu jeder beliebige Zeichenvektor zur Beschriftung der Unterteilungen verwendet werden kann. Die Anzahl der Beschriftungen muss jedoch mit der Anzahl der Unterteilungen übereinstimmen. Wenn die Unterteilungen durch eine Funktion, durch date_breaks oder standardmäßig definiert werden, müssen die Beschriftungen ebenfalls durch eine Funktion festgelegt werden.

base_plot +
scale_x_date(date_breaks = "15 years",
labels = function(x) paste((x-365), "(+365 days)")) +
ggtitle("labels = custom function")

base_plot +
scale_x_date(breaks = as.Date(c("1970-01-01", "2000-01-01")),
labels = c("~ '70", "~ '00")) +
ggtitle("labels = character vector")
Darüber hinaus kann das Format der Beschriftungen über das Argument date_labels gesteuert werden, das eine Reihe von Formatierungscodes enthält, die die Reihenfolge, das Format und die anzuzeigenden Elemente festlegen:
Source: Wickham 2009 p. 99

base_plot +
scale_x_date(date_labels = "%Y (%b)") +
ggtitle("date_labels = "%Y (%b)"")
Die Wahl der Achsenkreuze und -beschriftungen mag trivial erscheinen. Man sollte jedoch nicht unterschätzen, wie viel Verwirrung durch zu viele, zu wenige oder schlecht positionierte Achsenkreuze und -beschriftungen verursacht werden kann. Darüber hinaus kann eine sparsame, aber klare Beschriftung der Achsenmarkierungen die Lesbarkeit und die visuelle Attraktivität jeder Zeitreihendarstellung immens erhöhen. Da es so einfach ist, die Datums- und Zeitachsen in ggplot2 zu optimieren, gibt es einfach keine Entschuldigung, dies nicht zu tun.
Referenzen
- Wickham, H. (2009). ggplot2: elegant graphics for data analysis. Springer.