/ R

Komplexe CSV Files mit R einlesen

Natürlich gibt es einfachere Dinge, als Daten in R einzulesen. Und trotzdem ist es zumindest seit der Einführung des tidyverse und den damit propagierten tidy data principles sehr viel einfacher geworden saubere Daten zu laden. Leider hat nicht jeder Sensor-Hersteller das Paper von Hadley Wickem gelesen ...

Dieser Tage ist eine sehr spannende Aufgabe auf meinem Schreibtisch gelandet. Es klang zunächst so einfach: Wir haben da eine Maschine. Die misst etwas und schreibt ihre Ergebnisse in eine Textdatei. Diese müsste dann in R eingelesen werden. Ich dachte, dass sich das relativ leicht mit read_delim() lösen lassen können müsste. Dachte ich.

Die Sache hatte natürlich einen Haken. Den Vorteil, dass die Maschine ihre Messwerte in eine CSV-Datei (und kein proprietäres Format) schrieb, machte sie damit zunichte, dass sie in einer Datei mehrere Header einfügte. "Wie meinen?", denkt ihr vielleicht jetzt.

Stellen wir uns dazu vor, dass mit der Maschine die Inhaltsstoffe verschiedener Burger gemessen werden sollen. Man zielt also auf einen Burger und bekommt als Ergebnis die Anteile an Bun, Patty, Salat, etc. die in diesem Burger verwurstelt wurden. Das Problem ist nun, dass die Maschine beispielsweise bei einem vegetarischen Burger den Eintrag für Patty nicht einfach leer lässt sondern gar nicht erst notiert. Je nach Anzahl der Zutaten im Burger hat die CSV also unterschiedlich viele Spalten.

Immernoch unklar? Dann werft doch mal einen Blick auf das folgende Beispiel.

burger01

Unschön, nicht? Und obwohl ich in den vergangen Jahren wirklich einige R-Tricks gelernt habe, hat mich das einige Arbeit gekostet. Meine Lösung ist sicher nicht die beste, aber funktioniert und hilft vielleicht der ein oder anderen, die ein ähnliches Problem hat. Den Code gibt es natürlich auch zum downloaden direkt in einem Git-Repository.

Doch, der Reihe nach

Zunächst speichere ich mir den String, der den Start eines neuen Headers identifiziert (erster Spaltenname im Datensatz) und lese das gesamte File mittels readLines() ein.

# load packages -----------------------------------------------------------
require("tidyverse")

# define header identification --------------------------------------------
header_id <- "ID"

# read raw data -----------------------------------------------------------
RAW <- readLines(file.choose())
head(RAW)

Dann baue ich einen Datensatz, der genausoviele Zeilen wie das originale CSV-File hat und welche davon eine Header-Zeile ist. Mittels split()fülle ich die restlichen Zeilen mit der dazugehörigen Header-Nummer auf.

# find lines to split at --------------------------------------------------
header_lines <- which(substr(RAW, 1, nchar(header_id)) == header_id)

# create a list of all datasets within the CSV file -----------------------
DATASETS <- tibble(
  Row = 1:length(RAW),
  Data = RAW,
  Split = ifelse(Row %in% header_lines, header_lines, NA)
) %>%
  fill(Split) %>% 
  split(f = .$Split)

Schließlich schleife ich einmal über die jeweiligen Headernummern und picke mir aus den Rohdaten alle Informationen zusammen.

# extract relevant data ---------------------------------------------------
BURGERS <- tibble(
  ID = character(),
  Bun = numeric(), 
  Patty = numeric(),
  Salat = numeric(),
  Tomate = numeric()
)

for(i in 1:length(DATASETS)){
  # print loop ............................................................
  print(i)
  
  # extract data ..........................................................
  tmp_str <- DATASETS[[i]] %>% select(Data) %>% unlist()
  tmp_data <- read_delim(paste(tmp_str, collapse="\n"), delim = ";", na = "<LOD") %>% 
    select(
      ID
      matches(names(BURGERS))
      ) %>%
    filter(!is.na(ID))
  
  # append to results .....................................................
  BURGERS <- BURGERS %>% bind_rows(tmp_data) 
}

Et voilà, wir haben einen Datensatz mit dem man arbeiten kann :)


Das Titelbild stammt heute von Patrick Tomasso

Komplexe CSV Files mit R einlesen
Share this