Die folgende Notation wird in diesem Dokument benutzt:
ein_paar_R_befehle() #ein Kommentar dazu, wird von R ignoriert
## [1] "Die Ausgabe eines R-Befehls"
Aufgaben
In dieser Übung geht es um Datenformate. Anders als vielleicht der Titel verspricht, werden wir uns dabei nicht mit den unterschiedlichen Datentypen (float, integer, character, …) beschäftigen, sondern uns mit der Vielfalt der Daten- und Dateiformate auseinandersetzen, die uns in der täglichen Arbeit mit Umweltdaten immer wieder begegnen wird.
In dieser Lerneinheit wirst Du also lernen, wie man mit einer Auswahl üblicher Datenformate umgeht und wie man diese in R einliest und verarbeitet.
Viele Daten werden in Form von Textdateien gespeichert und verteilt. Meist erkennt man die Dateien bereits an ihrer Dateiendung, die oftmals .txt, .csv, .tab, .asc lautet. Der Vorteil dieser Dateien ist, dass man sie problemlos in einem Texteditor wie notepad oder Notepad++ (den ich für Windows-Nutzer nur empfehlen kann) betrachten kann. Dass man sich diese Daten vorher mal anschaut, bevor man sie in die Software einliest, wird ab und an sinnvoll sein, um die Datenstruktur einzusehen und zu begreifen.
Manchmal werden diese Textdateien auch als ASCII-Dateien bezeichnet, wobei ASCII American Standard Code for Information Interchange bedeutet. ASCII bezieht sich dabei auf eine Zeichenkodierung, mit der man mittels Nullen und Einsen 128 verschiedene Zeichen darstellen kann. Da diese Anzahl an Zeichen jedoch nicht ausreicht, um die Vielzahl von Zeichen (z.B. Umlaute) abzubilden, wird Text meist als Unicodezeichensatz bzw. dessen Kodierung durch den Unicode Transformation Format (oft mit der Version UTF-8) dargestellt und interpretiert. Meist wird man sich nicht mit diesem Thema herumschlagen müssen. Aber wenn ihr das Problem habt, dass Umlaute in Euren Rmd-Dateien plötzlich als Fragezeichen dargestellt werden, dann schaut mal in RStudio unter Tools, Code, Saving, welche Art der Textkodierung eingestellt ist.
Aber nun zurück zum Thema. Textdateien im Tabellenformat haben wir
bereits kennengelernt, als es um Dataframes ging. Die tabellarische
Struktur bedeutet, dass die Daten in Spalten und Zeilen geordnet sind.
Die Zeilen sind dabei durch ein Zeichen, das einen Zeilenumbruch
anordnet, und Spalten durch ein Symbol für einen Spaltenwechsel
voneinander getrennt. Die Dateiendung gibt dabei oft schon an, um
welches Spaltentrennzeichen es sich handelt. CSV-Dateien (“Character
Separated Values”), z.B. verwenden meist ein Komma oder auch Semikolon
als Trennzeichen. In R behndeln wir solche Dateien mit
read.table()
, das standardmäßig Leerzeichen oder
Tabulatoren erwartet (d.h. sep=" "
oder
sep="\t"
). Für eine Kommagetrennte Datei muss
dementsprechend sep=","
eingestellt werden.
Aufgabe 1: Webcam-Daten Hohenpeißenberg
Lies die Daten in der Datei
Hohenpeissenberg-S_20210904_1830.txt
ein und speichere sie in einem Dataframe. Die Daten sind hier im Original erhältlich.dateipfad <- "../data/Hohenpeissenberg-S_20210904_1830.txt" df <- read.table(dateipfad, sep = "|") df
## V1 V2 ## 1 Model Canon EOS 1300D ## 2 Exposure Time 8.0 sec. ## 3 FNumber f/5.6 ## 4 ISO 400 ## 5 Exposure Bias 0.3 eV ## 6 Focal Length 14mm ## 7 MeasuredEV -4.6 eV ## 8 Lens Name 11-16mm ## 9 Serial Number ME1404168 ## 10 Luminance 46.4 %
Weil’s so schön ist, hier gleich noch eine weitere Aufgabe.
Aufgabe 2: Brooms barn
Öffne die Datei
brooms_barn.txt
in einem Texteditor. Lies die Datei mit korrekten Spaltennamen in R ein, ohne sie vorher zu ändern. Was erschwert das Einlesen der Datei?dateipfad <- "../data/brooms_barn.txt" df = read.table(dateipfad, skip=20, header=FALSE) colnames(df) <- c("x", "y", "K", "log10K", "pH","P", "log10P") head(df)
## x y K log10K pH P log10P ## 1 1 24 26 1.41497 7.2 5.5 0.74036 ## 2 1 25 22 1.34242 7.2 5.2 0.71600 ## 3 1 26 18 1.25527 6.8 2.6 0.41497 ## 4 1 27 19 1.27875 6.4 1.3 0.11394 ## 5 1 28 26 1.41497 6.1 1.3 0.11394 ## 6 1 29 23 1.36173 6.6 6.9 0.83885
Schwieriger wird das Einlesen von Textdateien, deren Struktur von dem
einheitlichen Zeilen-Spalten-Schema abweicht. Wenn man diese Struktur
aber kennt, so sind diese Dateien genauso les- und verarbeitbar. Ein
Beispiel sind die Hurrican Best
Track Data (HURDAT2). Die Daten findest Du auch im data Verzeichnis
des heutigen Kurses unter hurdat2-1851-2020-052921.txt
.
Eine Beschreibung der Daten in der Datei
hurdat2-format-nov2019.pdf
angefügt. Im späteren Verlauf
dieser Veranstaltung werden wir uns vielleicht noch mit dieser Datei
eingehender beschäftigen.
Wenn man Glück hat, gibt es bereits jemanden, der ein Tool für das Einlesen derartiger “spezieller” Daten geschrieben hat. In der Tat, für die HURDAT-Daten gibt es ein R-Paket, das aber zum Zeitpunkt des Schreibens für R 4.0.0 und höher nicht funktioniert. In diesem Fall müsste man also am besten selber eine Funktion schreiben.
Wir wollen das aber zunächst an einem leichteren Beispiel durchgehen.
Betrachte dazu erst mal die Datei miniexample.txt
in einem
Texteditor.
Du kannst eine Datei einfach auch über den Befehl
file.show("../data/miniexample.txt")
öffnen.
## X
## 12.5 14.7 13.2 14.1
## Y
## 20.1 9.3 12.2 15.3
## Corg
## 1.5 2.2 2.1 1.0
Die Datei besteht aus 6 Zeilen, welche offensichtlich drei
verschiedene Variablen und vier Beobachtungen beinhalten. Die erste,
dritte, und fünfte Zeile geben dabei die Variablennamen, die übrigen
Zeilen die Beobachtungswerte an. Ziel ist es, einen Dataframe mit drei
Spalten und 4 Zeilen zu erzeugen. Dazu verwenden wir die Funktion
readLines()
, welche eine Datei zeilenweise einliest und
diese als Character-Array zurückliefert.
data <- readLines("../data/miniexample.txt")
data
## [1] "X" "12.5 14.7 13.2 14.1" "Y"
## [4] "20.1 9.3 12.2 15.3" "Corg" "1.5 2.2 2.1 1.0"
Ok, das ist doch schon mal nicht schlecht. Die einzelnen Variablennamen bekommen wir, indem wir uns jedes zweite Element aus diesem Array herausnehmen.
varnames <- data[seq(1, length(data), by = 2)]
varnames
## [1] "X" "Y" "Corg"
Das gleiche machen wir für die Beobachtungen selbst
obs <- data[seq(2, length(data), by = 2)]
obs
## [1] "12.5 14.7 13.2 14.1" "20.1 9.3 12.2 15.3" "1.5 2.2 2.1 1.0"
Die vier Beobachtungswerte je Variable sind jedoch noch in einer
Zeile und jeweils durch Leerzeichen getrennt. Das ändern wir mit der
Funktion strsplit()
.
obs <- strsplit(obs," ")
obs
## [[1]]
## [1] "12.5" "14.7" "13.2" "14.1"
##
## [[2]]
## [1] "20.1" "9.3" "12.2" "15.3"
##
## [[3]]
## [1] "1.5" "2.2" "2.1" "1.0"
Jetzt haben wir eine Liste mit drei Elementen mit jeweils vier
Einträgen. Im nächsten Schritt machen wir aus dieser Liste einen array
und wandeln die Zeichenfolge in ein numerisches Format mit der Funktion
as.numeric()
um.
obs <- unlist(obs)
obs <- as.numeric(obs)
obs
## [1] 12.5 14.7 13.2 14.1 20.1 9.3 12.2 15.3 1.5 2.2 2.1 1.0
Die Werte liegen nun als Zeilenvektor vor. Im nächsten Schritt formen wir diesen Vektor in eine 4x3-Matrix um.
obs <- matrix(obs, ncol = length(varnames))
obs
## [,1] [,2] [,3]
## [1,] 12.5 20.1 1.5
## [2,] 14.7 9.3 2.2
## [3,] 13.2 12.2 2.1
## [4,] 14.1 15.3 1.0
Super, jetzt kommt das Ganze noch in einen Dataframe und wir sind fertig.
df <- as.data.frame(obs)
colnames(df) <- varnames
df
## X Y Corg
## 1 12.5 20.1 1.5
## 2 14.7 9.3 2.2
## 3 13.2 12.2 2.1
## 4 14.1 15.3 1.0
Ok, das ganze hat doch etwas länger gedauert. Aber ich denke, ihr versteht das System dahinter. Wichtig ist, die Struktur der Datei zu begreifen. Die HURDAT2-Datei ist in einer Extradatei beschrieben. Diese Beschreibung sollte man sich genau anschauen, bevor man ein Programm schreibt, um die Dateien zu lesen. Anschließend geht man Schritt für Schritt das Einleseprozedere durch. Ihr könnt Euch sicherlich vorstellen, dass das ziemlich kompliziert werden kann, wenn der Datensatz bzw. das Format viele Besonderheiten aufweist (fehlende Werte, Strings und numerische Daten, etc.). Wenn das aber alles klappt, dann könnt Ihr ohne weiteres auch so große Datensätze wie den HURDAT-Datensatz einlesen. Das manuell zu machen, solltet ihr tunlichst vermeiden, denn mit einem effizienten Code könnt Ihr auch Hunderte oder Tausende solcher Dateien einlesen.
Natürlich gibt es auch gänzlich unstrukturierte Daten wie z.B. Freitext. Bei dem sogenannten Textmining wird versucht, aus diesen unstrukturierten Daten Informationen zu erhalten. Einer der ersten Schritte ist dabei, diese unstrukturierten Daten zu strukturieren. Das werden wir hier aber nicht machen.
Eine weitere Möglichkeit, Daten als Textdateien zu speichern und auszutauschen, bietet die Verwendung erweiterter Auszeichnungssprachen (Markup-Formate). XML (Extensible Markup Language), HTML, KML oder GPX folgen spezifischen Realisierungen solcher Formate. Markup-Formate ermöglichen, Daten entlang einer Baumstruktur zu speichern, die es erlaubt, Daten zu verschachteln. Das kann sehr hilfreich sein, wenn Daten nicht oder nur schlecht in tabellarischer Form organisiert werden können. Abwandlungen dieser Dateiformate werden darum häufig für den Austausch geographischer Daten verwendet (z.B. in Form von GPX, KML oder geojson), aber auch für Vektorgrafiken (SVG) oder auch wenn Programme Einstellungen speichern.
Markup-Daten werden gewöhnlich über eine spezielle Programmschnittstelle verarbeitet (z.B. XML-Parser). Diese Schnittstellen werden in R anhand der Pakete verfügbar gemacht und Funktionen definiert, die die Daten in eine geignete Datenstruktur überführen.
Im folgenden Beispiel schauen wir uns eine JSON-Datei an. JSON folgt
der gleichen Philosphie, welche den Markup-Formaten zugrunde liegt, wird
aber vorallem von JavaScript zum Übertragen von Daten benötigt. Um mit
json
Dateien zu arbeiten benötigen wir das Paket
rjson
, und der folgende Befehl installiert dieses.
install.packages("rjson")
Nun das Paket laden:
library(rjson)
Im nächsten Schritt laden wir eine json-Datei vom opendata-Server des DWD. Die Datei beinhaltet Gefahrenindizes für Wetterfühlige. Diese Informationen werden jeden Tag neu zur Verfügung gestellt und für verschiedene medizinische Formkreise (Herzkreislauferkrankungen, etc.) für den heutigen und zwei folgende Tage vorhergesagt. Die Daten werden für 11 verschiedene räumliche Zonen angegeben, die hier gezeigt werden.
Um die neueste Datei herunterzuladen, benötigt Ihr eine
Internetverbindung. Alternativ findet Ihr im data
-Ordner
eine ältere Datei.
# Variante mit Internetverbindung
# result <- fromJSON(file = "https://opendata.dwd.de/climate_environment/health/alerts/biowetter.json")
# Variante ohne Internetverbindung aus lokaler Datei
result <- fromJSON(file = "../data/biowetter.json")
Die Variable result
hat das Format
Large list
. Wir werden die Daten nicht im Detail ansehen.
In der Environment-Ansicht könnt Ihr die Inhalte erkunden. Nach einigen
Informationen zu Autor, Datum etc. folgt eine verschachtelte Liste mit
den Daten innerhalb der einzelnen Zonen. Schauen wir uns mal die
Situation für Potsdam (Zone 5) genauer an. Diese umfasst folgende
Regionen:
result$zone[[5]]$name
## [1] "Berlin, Brandenburg und im nördlichen Sachsen-Anhalt"
Nun schauen wir mal, ob eine Erklärung für eine heutige, wetter-bedingte unzureichende Leistungsfähigkeit im Kurs vorliegt.
result$zone[[5]]$today_afternoon$effect[[1]]$subeffect[[1]]
## $name
## [1] "psychisch-geistige Leistungsfähigkeit"
##
## $value
## [1] "geringe Gefährdung"
Was empfiehlt uns der DWD?
result$zone[[5]]$today_afternoon$recomms[[1]]
## $name
## [1] "Wettereinfluss auf das allgemeine Befinden"
##
## $value
## [1] "Aufenthalt im Freien in den kühleren Morgen- und Abendstunden bevorzugen, ausreichende Flüssigkeitszufuhr, - Vorsicht im Straßenverkehr"
Ihr seht, dass die JSON-Daten (und ähnlich XML-Daten) über eine sehr strukturierte Weise umfängliche Informationen zur Verfügung stellt. Diese Daten können, wenn man das Format gut kennt, maschinell verarbeitet werden.
Wenn Ihr eine Exceldatei statt mit Excel mit einem Texteditor öffnet, werdet Ihr sehen, dass der angezeigte Text nicht direkt lesbar ist. Das liegt daran, dass die Datei eine Binärdatei ist, die nur von spezieller Software gelesen werden kann. Wenn eine Software die Struktur und Kodierung der Binärdatei nicht kennt, wird sie damit auch nichts anzufangen wissen. Binärdateien sind z.B. Bilddateien, Audiodateien, oder Dateien von Tabellenkalkulationsprogrammen und Datenbanksystemen (z.B. GIS), aber auch ausführbare Programme. Für R gibt es für viele Formate Packages und Funktionen, die solche Dateien interpretieren und einlesen können.
Ein Beispiel einer binären Datei ist eine Datei mit der Erweiterung .RData. Diese Datei speichert Variablen, die während einer Sitzung in R erstellt wurden. So kann man die Arbeitsumgebung speichern und später wieder laden, erweitern oder verändern.
Eine Rdata-Datei kann man über den Befehl save
speichern. Dazu erstellen wir eine Variable, speichern sie und entfernen
sie aus der Arbeitsumgebung.
VAR <- 1:10
save('VAR', file = '../data/test.Rdata')
rm('VAR')
Anschließend können wir die Datei wieder laden:
load('../data/test.Rdata')
VAR
## [1] 1 2 3 4 5 6 7 8 9 10
Und danach löschen wir die Datei einfach wieder (Achtung, mit dieser Funktion muss man aufpassen, da man den Löschvorgang nicht rückgängig machen kann).
file.remove('../data/test.Rdata')
## [1] TRUE
Wer liebt es nicht: Microsoft Excel. Wenn jemand mit Daten am Computer das erste Mal konfrontiert wird, dann sicherlich anhand dieses Tabellenkalkulationsprogramms oder eines seiner quelloffenen und freien Varianten. Excel ist dabei besser als sein Ruf, und wenn man es beherrscht, kann man damit einiges machen. Es kommt aber niemals an die Vielseitigkeit und Flexibilität höherer Programmiersprachen heran. Auch sollte man möglichst vermeiden, Daten anhand Excel-Dateien (Dateiendungen xls, xlsx, xlsm, …) auszutauschen, da nicht jede(r) über eine Microsoft Office Lizenz verfügt. Nichtsdestotrotz wird man hier und dort nicht an diesem Dateiformat vorbeikommen.
Das xlsx-Format (wie auch seine Geschwister docx und pptx und freien Cousinen ods, odt und odp) ist eigentlich auch nur eine Sammlung gepackter XML (und ggf. Bilddateien). Wenn Du die Erweiterung einer solchen Datei in zip änderst, kannst Du Dir dessen innere Struktur ansehen. Versuche z.B. mal, auf diesem Weg ein Foto, dass in einem Textdokument eingebunden ist, zu extrahieren.
Für das Lesen und Schreiben von Excel-Dateien bietet R zahlreiche
Pakete. Diese haben unterschiedliche Funktionalitäten. Zum Einlesen von
tabellarisch strukturierten Daten ist beispielsweise das Paket
readxl
geeignet. Das Paket xlsx
ermöglicht
aber auch unstrukturierte Daten zu lesen und bietet zudem Schreib- und
Lesemöglichkeiten von Formatierung (z.B. Hintergrundfarben, Schriftart).
Da Excel weiterhin und vielfach eingesetzt wird, sind diese Tools
besonders hilfreich, um Analysen in R mit Excel-Anwendungen bzw. deren
Daten zu verknüpfen.
Digitale Bilder werden in einer Vielfalt von digitalen Bildformaten abgespeichert. Du hast vielleicht schon von folgenden Formaten gehört: JPG, GIF, TIF, PNG, SVG, EPS, oder BMP. Es gibt aber noch viele weitere oftmals spezialisierte Formate, die z.B. im GIS oder in der medizinischen Informatik eingesetzt werden. Bilddateien sind ebenso meist Binärdateien und werden in zwei wesentliche Arten unterschieden: Rasterbilder und Vektorbilder. Wir werden uns hier nur mit den Rasterbildern beschäftigen.
Wieso gibt es verschiedene Formate von Rasterbildern? Nun, unterschiedliche Formate haben z.B. verschiedene Möglichkeiten, Metadaten (z.B. EXIF-Einträge, die Informationen zur Kamera, Blende, Belichtungszeit beinhalten) abzuspeichern, sie verfügen über unterschiedliche Kompressionsalgorithmen, die es ermöglichen mit oder ohne Verlust die Größen der Bilddateien zu reduzieren, oder sie sind in der Lage (so wie z.B. BigTiff), riesige Dateien abzuspeichern, was insbesondere bei Satellitenbildern von Vorteil ist.
Um digitale Bilder mit R zu lesen und zu schreiben, verwenden wir
hier das Paket imager
.
install.packages("imager")
Ist das Paket installiert, fügen wir die Library zu unserer Environment hinzu.
## Warning: Paket 'imager' wurde unter R Version 4.4.2 erstellt
## Lade nötiges Paket: magrittr
##
## Attache Paket: 'imager'
## Das folgende Objekt ist maskiert 'package:magrittr':
##
## add
## Die folgenden Objekte sind maskiert von 'package:stats':
##
## convolve, spectrum
## Das folgende Objekt ist maskiert 'package:graphics':
##
## frame
## Das folgende Objekt ist maskiert 'package:base':
##
## save.image
Nun können wir ein Bild über die Funktion load.image
laden.
im <- load.image("../data/06_Matjpg.jpg")
Wir haben nun eine Variable der Klasse cimg
, die wir mit
der Funktion plot
darstellen können.
plot(im)
Da die Datei doch recht groß ist, verkleinern wir das Bild um die Hälfte. Zudem rotieren wir das Bild um 45°.
im <- imresize(im, scale = 0.5, interpolation = 3)
im <- imrotate(im,45)
plot(im)
An dieser Stelle wollen wir gar nicht im Detail in die digitale
Bildverarbeitung einsteigen. Wenn Du aber Interesse an den
Funktionalitäten von imager
hast, so schau Dir doch mal die
dazugehörige Website
an.
Ein gebräuchliches Datenformat für Umweltdaten ist NetCDF. Insbesondere Daten aus Klimamodellen werden meist über dieses Format gespeichert, da eine Vielzahl von räumlich und zeitlich variierenden Variablen unterschiedlicher Struktur unterstützt werden. Klimamodelle geben beispielsweise Rasterdaten zu verschiedenen Variablen (z.B Temperatur oder Luftfeuchte) in verschiedenen Druckniveaus aus. Diese 3D-Variablen variieren in der Zeit, so dass wir es meist mit 4D-Daten zu tun haben. Wir werden diesem Datenformat eine extra Stunde widmen.
Du hast in dieser Stunde einige verschiedene Dateiformate kennengelernt. Oftmals sind Daten in tabellarischer Struktur. Das macht das Einlesen dieser Daten einfach. Schwieriger wird es, wenn die Datenstruktur von der tabellarischen Struktur abweicht. Gegenenfalls musst Du dann eigenen Programmcode schreiben, um diese Daten einzulesen und weiter zu verarbeiten. Schreibe hierbei Deinen Code so generisch - d.h. allgemeingültig - wie möglich. Bei binären Dateien sollte man wissen, um welche Daten es sich handelt, und mit welchem Programm diese erstellt wurden. Das kann manchmal schwierig sein. Im Gegensatz zu einer Dateiendung .xlsx oder .png ist es bei der Endung .bin etwas schwieriger herauszufinden, wie man diese Dateien am besten einliest, wenn es dafür noch keine verfügbaren Pakete gibt. Einige der Dateiformate werden wir in den kommenden Sessions noch näher betrachten.
Die Datei pvm_6457010.txt beinhaltet Abflussdaten der Oder am Pegel Gozdowice. Die Datei stammt vom “Global Data Runoff Center” (GRDC). Schreibe eine möglichst allgemeingültige Funktion, mit der Du aus der Datei folgende Daten in einen DataFrame überführst.
Dir könnten dabei folgende Funktionen für den Vergleich von Zeichenfolgen helfen: startsWith, endsWith, identical.
Teste, ob Dein Code auch mit einer anderen Datei (pvm_6457100.txt) funktioniert.
Viele Programme für die Bildbetrachtung bieten auch
Stapelverarbeitungsmöglichkeiten an (batch processing). Viele dieser
Funktionen kann man sich aber schnell auch selbst programmieren. Lade
für diese Aufgabe einige (10-1000) deiner Bilder (z.B. vom Smartphone)
in einen Ordner auf deiner Festplatte. Nun schreibe ein Programm, dass
die Bilder einliest, um die Hälfte verkleinert, und wieder in einem
anderen Ordner abspeichert. Die Kontrollanweisungen aus der letzten
Session sowie die oben gezeigten imager
-Funktionen sollten
dabei helfen. Die Funktion save.image()
schreibt Bilder aus
R wieder zurück auf die Festplatte.