Tag Punkt Monat Punkt Jahr – Ein Datum ist doch eigentlich ziemlich einfach, oder?

In diesem Artikel geht es darum, wie ihr in R mit Datums- und Zeitangaben umgeht. Insbesondere beim Importieren von Daten aus csv- oder Excel-Dateien (siehe meinen Blogpost zum Excel-Import und -Export mit R) gibt es immer wieder komische Datums-Formate, die konvertiert werden müssen.

Dafür hat R drei Datentypen vorgesehen:

  • Date für Datum
  • POSIXct für Zeitstempel, also Datum plus Zeitangabe und fast identisch
  • POSIXlt für Zeitstempel, also Datum plus Zeitangabe (Den Unterschied zwischen POSIXct und POSIXlt kläre ich weiter unten auf)

Reine Zeitangaben, also nur die Uhrzeit ohne Datum, sind eher selten und nicht sehr praktikabel. Gibt es in Euren Daten ein Feld für das Datum und ein Feld für die Uhrzeit, dann solltet ihr diese zu einem Zeitstempel zusammenfassen.

Ich zeige euch, wie ihr Strings oder Zahlen in einen der Datentypen überführt und damit rechnen könnt. Für ein besseres Handling ist das Package lubridate aus dem tidyverse eine große Erleichterung und jedem ans Herz gelegt.

Dazu aber weiter unten mehr, wir fangen mit Standard-R an.

Der Datumstyp in R

Schauen wir uns mal Datum- und Zeitstempel-Variablen an. Mit Sys.Date() bekommt man das aktuelle Datum, mit Sys.time() den aktuellen Zeitstempel des Rechners.
Mit dem R-Befehl typeof wird der Typ zurückgegeben.

heute <- Sys.Date()
print(heute)
## [1] "2019-02-23"
jetzt <- Sys.time()
print(jetzt)
## [1] "2019-02-23 08:34:00 CET"
typeof(heute)
## [1] "double"
typeof(jetzt)
## [1] "double"
str(heute)
## Date[1:1],format: "2019-02-23"
str(jetzt)
## POSIXct[1:1],format: "2019-02-23 08:34:00"

Wir sehen interessanterweise, dass sowohl Datum als auch Zeitstempel intern als Zahl gespeichert werden, nämlich als Anzahl Tage (Date) oder Sekunden (POSIX) seit einem festgelegten Startdatum, nämlich seit dem 1. Januar 1970 00:00 UTC. Als Zusatz wird aber doch noch abgespeichert, dass es sich um ein Datum oder Zeitstempel handelt. Das sehen wir an der Struktur (mit Hilfe des Befehls str). Sonst würde die print-Funktion ja auch nur eine Zahl ausgeben und nicht automatisch in einen String umwandeln.

print(as.numeric(heute))
## [1] 17950
print(as.numeric(jetzt))
## [1] 1550907241
 
# ein Datum kann als Anzahl Tage seit einemUpsprungsdatum angegeben werden
d <- as.Date(1,origin="1970-01-01")
print(as.numeric(d))
## [1] 1

GRATIS 5-Tage Minikurs: Datenanalyse in R

Du kennst Dich noch nicht so gut damit aus, wie man einen Datensatz in R analysiert? Ich bin da, um Dir zu helfen. Hole Dir jetzt meinen kostenlosen Datenanalyse mit R – Kurs und fange an, Dir diese zukunftsweisenden Skills anzueignen.

Datum aus String

Wollen wir ein Datum aus einem String erzeugen, geht das mit der Funktion as.Date(str, format). Dabei können wir ohne Angabe des Parameters format die Standardformate verwenden, die in R YYYY-MM-DD und YYYY/MM/DD sind. D.h. der String muss zuerst die vierstellige Jahreszahl, dann ein Trennzeichen, dann den zweistelligen Monat, wieder das Trennzeichen und dann den zweistelligen Tag beinhalten, also “2021-03-25” oder “2021/03/25”. R versteht Monat und Tag auch ohne die führender Null bei Zahlen unter 10, also “2021-3-5” würde auch gehen.

Diese Form haben wir aber natürlich nicht immer. Dann müssen wir mit dem Parameter format angeben, welches Format wir übergeben. Dabei gibt es eine ganze Menge Kürzel, die alle mit % anfangen. Daraus können wir uns alle möglichen und unmöglichen Datumsformate zusammenbasteln. In der Liste unten findet ihr sie.

Für die ganz Eiligen unter Euch hier die Umwandlung des deutschen Datumformats:

as.Date(str, "%d.%m.%Y")

String aus Datum in R

Um aus einem Datum wieder einen String zu machen, könnt ihr einfach die Funktion format(datum, format) benutzen. Dabei funktioniert der Parameter format genauso wie bei as.Date(), nur das eben der R-interne Datumtyp in einen String mit entsprechender Formatierung umgewandelt wird.

So bekommen wir mit as.Date(Sys.Date(), "%B %Y") den aktuellen Monatsnamen und Jahr, also z.B. “April 2021”. Hier einige weitere Beispiele:

d1 <- as.Date("2018-02-22")
d2 <- as.Date("20.02.2018",format="%d.%m.%Y")
d3 <- as.Date("02/18/18",format="%m/%d/%y")
 
format(d1,"%d.%m.%Y")
## [1] "22.02.2018"
format(d2,"%d.%m.%Y")
## [1] "20.02.2018"
LERNE DATA SCIENCE mit R

Ein Data Science Experte ist in der heutigen datengetriebenen Welt viel gefragt. Mit der entsprechenden Erfahrung kann man sich den gutbezahlten, interessanten Job aussuchen. In meinem Onlinekurs Data Science mit R lernst Du die Grundlagen.

Liste der Datumsformate in R

Und nun zu der versprochenen Liste mit den Format-Kürzeln.

Kürzel Beschreibung Beispiel
%m Monat (01-12) 05
%b or %h 3-stelliger Monatsname Feb
%B Monatsname Februar
%d Tag des Monats (01-31) 27
%j Tag vom Jahr (001-366) 148
%y 2-stelliges Jahr (00-99) 18
%Y 4-stelliges Jahr 2018
%C Jahrhundert 20
%x Datum (System)
%D Datum MM/TT/JJJJ 05/27/84
%U Kalenderwoche, Sonntag=erster Tag der Woche
(01-53)
22
%W Kalenderwoche, Montag=erster Tag der Woche
(00-53)
22
%u Wochentag, Montag=1 (1-7) 7
%w Wochentag, Sonntag=0 (0-6) 0
%a 3-stelliger Wochentag So
%A Wochentag Donnerstag

 

Und so sieht das dann im Code aus:

d <- as.Date("2018-02-22")
print(paste("Monat:",format(d,"%m")))
## [1] "Monat: 02"
print(paste("3-stelliger Monatsname:",format(d,"%b")))
## [1] "3-stelliger Monatsname: Feb"
print(paste("Monatsname:",format(d,"%B")))
## [1] "Monatsname: Februar"
print(paste("Tag:",format(d,"%d")))
## [1] "Tag: 22"
print(paste("Tag vom Jahr:",format(d,"%j")))
## [1] "Tag vom Jahr: 053"
print(paste("2-stelliges Jahr:",format(d,"%y")))
## [1] "2-stelliges Jahr: 18"
print(paste("4-stelliges Jahr:",format(d,"%Y")))
## [1] "4-stelliges Jahr: 2018"
print(paste("Jahrhundert:",format(d,"%C")))
## [1] "Jahrhundert: 20"
print(paste("Kalenderwoche (US):",format(d,"%U")))
## [1] "Kalenderwoche (US): 07"
print(paste("Kalenderwoche (DE):",format(d,"%W")))
## [1] "Kalenderwoche (DE): 08"
print(paste("Datum (Systemeinstellung):",format(d,"%x")))
## [1] "Datum (Systemeinstellung): 22.02.2018"
print(paste("Datum MM/TT/JJJJ:",format(d,"%D")))
## [1] "Datum MM/TT/JJJJ: 02/22/18"
print(paste("Wochentag (Montag=1):",format(d,"%u")))
## [1] "Wochentag (Montag=1): 4"
print(paste("Wochentag (Sonntag=0):",format(d,"%w")))
## [1] "Wochentag (Sonntag=0): 4"
print(paste("3-stelliger Tagesname:",format(d,"%a")))
## [1] "3-stelliger Tagesname: Do"
print(paste("Tagesname:",format(d,"%A")))
## [1] "Tagesname: Donnerstag"

Rechnen mit Daten

d1 <- as.Date("2018-01-23")
d2 <- as.Date("2018-02-22")
delta <- d2 - d1
delta
## Time difference of 30 days
str(delta)
## 'difftime' num30
## - attr(*,"units")= chr "days"attributes(delta)
## $class
## [1] "difftime"
## 
## $units
## [1] "days"difftime(d2,d1,units="weeks")
## Time difference of 4.285714 weeks
 
# erster Tag des Monats
Tage <- as.numeric(format(d1,"%d")) - 1
as.Date(-Tage,d1)
## [1] "2018-01-01"
ersterTagDesMonats <- function(Datum) {
    return(as.Date(-as.numeric(format(Datum,"%d")) + 1,Datum))
}
ersterTagDesMonats(d2)
## [1] "2018-02-01"

Datum mit dem Package lubridate

Viel einfacher geht das alles mit dem Package lubridate. Wie bei jedem Packages muss es einmal installiert werden install.packages(“lubridate”). Danach kann es mit library(lubridate) oder require(lubridate) im Skript geladen werden.

Lubridate hat einige Funktionen, um schnell ein Datum zu erzeugen, an die einzelnen Teile heranzukommen und auch diese abzuändern

library(lubridate)
## 
## Attaching package: 'lubridate'
## The following object is masked from 'package:base':
## 
##      date
 
# Ein Datum erstellen
d <- ymd(20180514)
d2 <- mdy("4/1/17")
# Abruf der einzelnen Teile
year(d)
## [1] 2018
month(d)
## [1] 5
day(d)
## [1] 14
wday(d)
## [1] 2
wday(d,label=TRUE)
## [1] Mo
## Levels: So < Mo < Di < Mi < Do < Fr < Sa
wday(d,label=TRUE,abbr=FALSE)
## [1] Montag
## 7 Levels: Sonntag < Montag < Dienstag < Mittwoch < ... < Samstag
wday(d,label=TRUE,abbr=FALSE,week_start=1)
## [1] Montag
## 7 Levels: Montag < Dienstag < Mittwoch < Donnerstag < ... < Sonntag
yday(d)
## [1] 134
 
# Modifikation
month(d) <- 2
day(d) <- 5
d
## [1] "2018-02-05"

Als nützlich haben sich auch die Rundungsfunktionen in lubridate erwiesen. Mit diesen kann zum Beispiel der Monatsanfang leicht bestimmt werden.

# springt zum Anfang des Monats
floor_date(d,unit="month")
## [1] "2018-02-01"
# springt zum Anfang des nächsten Monats
ceiling_date(d,unit="month")
## [1] "2018-03-01"
# rundet auf Monate, also bis zum 14. zum Monatsanfang, ab dem 15. zum nächsten Monatsanfang
round_date(d,unit="month")
## [1] "2018-02-01"
round_date(d+1,unit="month")
## [1] "2018-02-01"

Zeitstempel in R

Ein Zeitstempel setzt sich aus Datum und Uhrzeit zusammen. Für eine richtige Uhrzeit ist noch die Zeitzone angegeben, damit es eindeutig wird. Der entsprechende Datentyp in R ist POSIXct bzw. POSIXlt. Diese beiden unterscheiden sich in der internen Darstellung. POSIXct speichert die Anzahl Sekunden seit dem Origin (UNIX Epoch, nämlich der 01.01.1970 00:00:00), POSIXlt speichert eine Liste mit Tag, Monat, Jahr, Stunde, Minute, etc.

Damit kann man mittels Attributen auf Teile eines POSIXlt zugreifen, beim POSIXct muss man über format den entsprechenden Wert extrahieren und dann gegebenenfalls noch in eine Zahl umwandeln. Mit dem Package lubridate läßt sich das vereinfachen, in dem per Funktion direkt auf die einzelnen Teile zugreifen kann, dazu weiter unten mehr.

# die aktuelle Systemzeit wird als POSIXct zurückgegeben
jetzt <- Sys.time()
str(jetzt)
##  POSIXct[1:1], format: "2019-02-23 08:34:01"
attributes(jetzt)
## $class
## [1] "POSIXct" "POSIXt"
 
#gibt die Minuten aus
as.numeric(format(jetzt,"%M"))
## [1] 34
 
# mit as.POSIXlt kann sie in ein POSIXlt umgewandelt werden
jetzt_lt <- as.POSIXlt(jetzt)
str(jetzt_lt)
##  POSIXlt[1:1],format: "2019-02-23 08:34:01"
 
attributes(jetzt_lt)
## $names
## [1] "sec"   "min"   "hour"  "mday"  "mon"  "year"  "wday"## [8] "yday"  "isdst" "zone"  "gmtoff"
## 
## $class
## [1] "POSIXlt" "POSIXt"
## 
## $tzone
## [1] ""   "CET"   "CEST"
 
jetzt_lt$min
## [1] 34
 
# origin und tz (timezone) sind optional, also nur nötig, wenn nicht der Standardwert genommen werden soll
ts <- as.POSIXct("20190222105519", format="%Y%m%d%H%M%S", origin="1970-01-01", tz="CET")
ts
## [1] "2019-02-22 10:55:19 CET"
ts2 <- as.POSIXct("22.02.2019 10:55:19", format="%d.%m.%Y %H:%M:%S")
ts2
## [1] "2019-02-22 10:55:19 CET"

Neben den Datumskürzelns aus der obigen Tabelle können wir mit den folgenden Kürzeln noch die Zeit definieren. Das funktioniert sowohl bei der Erzeugung aus einem String als auch der umgekehrte Weg mittels format.

Kürzel Beschreibung Beispiel
%c Zeitstempel im System-Format Fr Feb 22 10:55:19 2019
%H Stunden (00-24) 23
%I Stunden (00-12) 11
%M Minuten 55
%p AM/PM (sofern verwendet) AM
%S Decimal second 19
%X Zeit (Systemeinstellungen) HH:MM:SS
%z Abweichung von GMT +0100
%Z Zeitzone CET

format(ts,"%c")
## [1] "Fr Feb 22 10:55:19 2019"
format(ts,"%H")
## [1] "10"
format(ts,"%I")
## [1] "10"
format(ts,"%M")
## [1] "55"
format(ts,"%p")
## [1] ""
format(ts,"%S")
## [1] "19"
format(ts,"%X")
## [1] "10:55:19"
format(ts,"%z")
## [1] "+0100"
format(ts,"%Z")
## [1] "CET"

Um die Zeitspanne zwischen zwei Zeitstempeln zu bestimmen, können wir sie einfach voneinander abziehen. Das Problem ist aber, dass wir dann die Einheit (Tage, Stunden, …) nicht selber wählen können. Daher ist es besser, dass über difftime zu machen und den
Parameter units explizit zu setzen.

delta <- Sys.time() - ts
str(delta)
##  'difftime' num 21.6450349113676
##  - attr(*,"units")= chr "hours"
delta <- difftime(Sys.time() , ts, units="days")

Zeitstempel mit lubridate

Zeittempel mit lubridate funktionieren ganz genauso wie wir das oben schon beim Datum gesehen haben. Es gibt Erzeuger-Funktionen wie ymd_hms und
Funktionen, die die einzelnen Teile des Zeitstempels extrahieren. Ordnet man
diesen Extraktionsfunktionen einen Wert zu, wird der Zeitstempel geändert.

library(lubridate)
ts <- ymd_hms("2011-06-04 12:00:00")
year(ts)
## [1] 2011
hour(ts)
## [1] 12
second(ts)
## [1] 0
second(ts) <- 25
ts
## [1] "2011-06-04 12:00:25 UTC"

So, ich hoffe, jetzt seid ihr zum Master of Date and Time geworden.

Happy Coding,
Euer Holger

GRATIS 5-Tage Minikurs: Datenanalyse in R

Du kennst Dich noch nicht so gut damit aus, wie man einen Datensatz in R analysiert? Ich bin da, um Dir zu helfen. Hole Dir jetzt meinen kostenlosen Datenanalyse mit R – Kurs und fange an, Dir diese zukunftsweisenden Skills anzueignen.

LERNE DATA SCIENCE mit R

Ein Data Science Experte ist in der heutigen datengetriebenen Welt viel gefragt. Mit der entsprechenden Erfahrung kann man sich den gutbezahlten, interessanten Job aussuchen. In meinem Onlinekurs Data Science mit R lernst Du die Grundlagen.