Beobachtunge aufgrund von Datumsvariablen zusammenführen

Wie rufe ich R-Funktionen auf, wie selektiere ich Daten, ich weiß nicht genau ....

Moderatoren: EDi, jogo

Jörg
Beiträge: 25
Registriert: Mi Nov 09, 2016 2:58 pm

Beobachtunge aufgrund von Datumsvariablen zusammenführen

Beitrag von Jörg »

Hallo Zusammen,

ich habe mal wieder ein Datenmanagementproblem

Ich habe einen Datensatz, in dem mehrere Beobachtungen pro Person (Id) vorkommen können, nicht müssen. Für jede Beobachtung gibt es ein Start- und ein Stoppdatum. Zusätzlich gibt es weitere Variablen.

Ein vereinfachtes Beispiel:

Code: Alles auswählen

library(tidyverse)
Id<-c(1,1,1,1,2)
Startdatum<-as.Date(c("2020-06-22","2020-06-24","2020-07-28","2020-08-16","2020-06-22"))
Stoppdatum<-as.Date(c("2020-06-24","2020-07-28","2020-08-02","2020-08-19","2020-06-24"))
Var1<-c("a","b","c","D","E")
Var2<-c("g","h","j","k","l")

df<-tibble(Id,Startdatum,Stoppdatum,Var1,Var2)
Bei einer Person im Beispieldatensatz (Id = 1) kommt es vor, dass 3 der 4 Beobachtungen quasi nahtlos ineinander übergehen, sprich das Stoppdatum der chronologisch vorhergehenden Beobachtung liegt ein Tag vor dem Startdatum der nächsten Beobachtung.
Diese drei Beobachtungen sollen in eine beobachtung/ Zeile zusammengefasst werden, wobei als Startdatum der Wert der ursprüngliche ersten Beobachtung und als Stoppdatum der Wert der ursprünglich letzten Beobachtung gewählt werden soll. Die übrigen Variablen sollen unterschiedlich behandelt werden. Im Beispiel soll für Var1 der Wert der chronologisch ersten Beobachtung und für Variable 2 der Wert der chronologisch letzten Beobachtung übernommen werden.

Das Ergebnis sollte für das Beispiel so aussehen:

Code: Alles auswählen

Id2<-c(1,1,2)
Startdatum2<-as.Date(c("2020-06-22","2020-08-16","2020-06-22"))
Stoppdatum2<-as.Date(c("2020-08-02","2020-08-19","2020-06-24"))
Var1_2<-c("c","D","E")
Var2_2<-c("g","k","l")

df2<-tibble(Id2,Startdatum2,Stoppdatum2,Var1_2, Var2_2)
Herzlichen Dank für Eure Unterstützung!

Jörg
bigben
Beiträge: 2771
Registriert: Mi Okt 12, 2016 9:09 am

Re: Beobachtunge aufgrund von Datumsvariablen zusammenführen

Beitrag von bigben »

Hallo Jörg,

ich verstehe noch nicht, was mit Var1 und Var2 passieren soll. Deinen Text verstehe ich so, dass da in der ersten Ergebniszeite "a"-"j" herauskommen sollte, in Deinem Wunschergebnis kommt aber "c"-"g" heraus. Wie lautet denn da die Regel richtig?

LG,
Bernhard
---
Programmiere stets so, dass die Maxime Deines Programmierstils Grundlage allgemeiner Gesetzgebung sein könnte
Jörg
Beiträge: 25
Registriert: Mi Nov 09, 2016 2:58 pm

Re: Beobachtunge aufgrund von Datumsvariablen zusammenführen

Beitrag von Jörg »

Hallo Bernhard,
tut mir leid, du hast Recht. Mein Text und der "Wunschdatensatz" df2 passen nicht zusammen bzw. sind genau in das Gegenteil verkehrt. Welche Regel jetzt im Beispiel gilt, spielt vom Prinzip keine Rolle. Mir geht es um konkreten Anwendungsfall nur um die Möglichkeit für verschiedene Variablen verschiedene Regeln definieeren zu können. Ich orientiere mich bei meinen, allerdings noch sehr rudimentären, Bastelversuchen an dem Wunschdatensatz df2.

Liebe Grüße
Jörg
bigben
Beiträge: 2771
Registriert: Mi Okt 12, 2016 9:09 am

Re: Beobachtunge aufgrund von Datumsvariablen zusammenführen

Beitrag von bigben »

Hallo Jörg,

es gibt bestimmt irgendwo ausgereifte Zeitreihenfunktionen, mit denen man das elegant, verlässlich und schnell machen kann. Ich kenne mich mit Zeitreihen und den entsprechenden Paketen aber wirklich nicht aus. Ich habe mal angefangen, etwas selbst zu programmieren, bin aber noch nicht zum Testen gekommen. Ich schlage vor, dass Du mal ein paar Beispieldatensätze erstellst und ausprobierst, ob das hier funktioniert oder wann es nicht funktioniert.

Das Ergebnis kommt noch in einer etwas komischen Form, aber das wird man auch noch anpassen/umwandeln können, wenn der Algorithmus sonst funktioniert:

Code: Alles auswählen

# Beispieldaten (davon brauchen wir noch viel mehr zum Testen)

Id<-c(1,1,1,1,2)
Startdatum<-as.Date(c("2020-06-22","2020-06-24","2020-07-28","2020-08-16","2020-06-22"))
Stoppdatum<-as.Date(c("2020-06-24","2020-07-28","2020-08-02","2020-08-19","2020-06-24"))
Var1<-c("a","b","c","D","E")
Var2<-c("g","h","j","k","l")
d <-  data.frame(id = Id, start = Startdatum, stop = Stoppdatum, var1 = Var1, var2 = Var2)
print(d)

# Finde heraus, welche Zeilen zusammengefasst werden sollen

lines <- nrow(d)
join <-  integer(lines)
i <-  1    # markiert die gerade bearbeiteten Zeilen in d
ji <- 1    # zählt die gerade bearbeitete Gruppe zusammengehöriger Zeilen und wird in join eingetragen
while(TRUE){
  join[i] <- ji
  if (i >= lines) break
  i <- i + 1
  if(d$id[i-1] != d$id[i] | d$stop[i-1] != d$start[i])  ji <- ji + 1
}
print(join)  # Das ist ein Vector mit einem Eintrag pro Zeile im Datensatz
             # Zeilen mit gleichem Eintrag in join müssen zusammengefasst werden

# Fasse Zeilen zusammen

by(d, join, FUN = function(ausschnitt){
  r <- ausschnitt[1,]         # r steht für result und ist erstmal eine Kopie der ersten Zeile jedes Blocks
  r$stop <- ausschnitt[nrow(ausschnitt), "stop"]
  #
  # TODO: hier ggf. noch Werte von Var1, Var2, Var3 etc anpassen
  r$var2 <- ausschnitt[nrow(ausschnitt), "var2"]
  #
  r
})
LG,
Bernhard
---
Programmiere stets so, dass die Maxime Deines Programmierstils Grundlage allgemeiner Gesetzgebung sein könnte
Jörg
Beiträge: 25
Registriert: Mi Nov 09, 2016 2:58 pm

Re: Beobachtunge aufgrund von Datumsvariablen zusammenführen

Beitrag von Jörg »

Hallo Bernhard,
das sieht zumindest sehr viel ausgereifter als meine bisherigen Ansätze aus. Vielen Dank. Ich teste und berichte.
Herzliche Grüße
Jörg
Jörg
Beiträge: 25
Registriert: Mi Nov 09, 2016 2:58 pm

Re: Beobachtunge aufgrund von Datumsvariablen zusammenführen

Beitrag von Jörg »

Hallo Bernhard,
ich habe mal getestet, sowohl mit neu kreierten Datensätzen als auch in meinem Originaldatensatz.

Schritt 1 (Finde heraus, welche Zeilen zusammengefasst werden sollen) scheint zuverlässig zu klappen. In den kleinen "künstlichen" Datensätzen hat alles gepasst. Der Originaldatensatz hat 73000 Beobachtungen und nur knapp 400 erfüllen die Kriterien. Ich kann nicht sagen, ob alle einschlägigen Fälle erkannt wurden, aber die Fälle, die erkannt wurden, wurden korrekt erkannt, das habe ich "händisch". Soweit so gut.

Probleme habe ich bei Schritt 2 (Fasse Zeilen zusammen). Das Ergebnis ist eine Liste, in der jede der verbleibenden Beobachtungen ein Element darstellt. Jedes dieser Elemente ist ein Tibble. Sofern die Variablen nicht verändert wurden, sind es normale Spalten des jeweiligen Tibble. Die Spalte der Variablen, die verändert wurden (im Beispiel unten das Stoppdatum und Var2) sind aber widerum selbst Tibble, deren Elemente wiederum die Variablenwerte sind. Ich gestehe, meine Beschreibung ist deswegen so holprig, weil ich die Struktur selbst noch nicht durchblicke. Vielleicht ist das Beispiel hilfreicher:

Ich habe unten das Beispiel aktualisiert. Bitte beachte:
- In meiner ursprünglichen Beschreibung war das Stoppdatum der vorhigen Beobachtung eines Blocks das Startdatum der nächsten Beobachtung. Im Originaldatensatz liegt das Stoppdatum ein Tag vor dem Startdatum. Ich habe dies sowohl in den Beispiel als auch in deinem Code angepasst.
- ich habe deinen Code so überarbeitet, dass die Variablennamen etc zum Beispiel passen.
- Das Ergebnis des Schritt 2 (Fasse Zeilen zusammen) wird als Objekt "neu" gespeichert und dann in einen tibble (df_2) umgewandelt.

Code: Alles auswählen

# 0. Erzeuge Beispieldatensatz
library(tidyverse)
Id<-c(1,1,1,1,2,3,3,3)
Startdatum<-as.Date(c("2020-06-22","2020-06-25","2020-07-29","2020-08-16","2020-06-22","2020-09-22","2020-09-24","2020-10-22"))
Stoppdatum<-as.Date(c("2020-06-24","2020-07-28","2020-08-02","2020-08-19","2020-06-24","2020-09-23","2020-09-28","2020-10-22"))
Var1<-c("a","b","c","D","E","g","H","J")
Var2<-c("g","h","j","k","l","K","L","B")
df<-tibble(Id,Startdatum,Stoppdatum,Var1,Var2)

# 1. Finde heraus, welche Zeilen zusammengefasst werden sollen

lines <- nrow(df)
join <-  integer(lines)
i <-  1    # markiert die gerade bearbeiteten Zeilen in d
ji <- 1    # zählt die gerade bearbeitete Gruppe zusammengehöriger Zeilen und wird in join eingetragen
while(TRUE){
  join[i] <- ji
  if (i >= lines) break
  i <- i + 1
  if(df$Id[i-1] != df$Id[i] | (df$Stoppdatum[i-1] + 1) != df$Startdatum[i])  ji <- ji + 1
}

print(join)  # Das ist ein Vector mit einem Eintrag pro Zeile im Datensatz
             # Zeilen mit gleichem Eintrag in join müssen zusammengefasst werden


# 2. Fasse Zeilen zusammen

neu<-by(df, join, FUN = function(ausschnitt){
  r <- ausschnitt[1,]         # r steht für result und ist erstmal eine Kopie der ersten Zeile jedes Blocks
  r$Stoppdatum <- ausschnitt[nrow(ausschnitt), "Stoppdatum"]
 
   # TODO: hier ggf. noch Werte von Var1, Var2, Var3 etc anpassen
  r$Var2 <- ausschnitt[nrow(ausschnitt), "Var2"] 
  r
})

# 3. wandle "neu" in einen normalen tibble um:
df_2<-  do.call("rbind", neu) 

# ==> das ist leider noch kein "normaler" tibble. Die einzelnen Zelleinträge in den Spalten Stoppdatum und Var2 sind selbst widerum tibbles.

str(df_2$Stoppdatum)
Kurz, meine Frage wäre: wie wandle ich das Objekt "neu" in einen herkömmlichen Tibble um?

Herzliche Grüße
Jörg
bigben
Beiträge: 2771
Registriert: Mi Okt 12, 2016 9:09 am

Re: Beobachtunge aufgrund von Datumsvariablen zusammenführen

Beitrag von bigben »

Hallo Jörg,

das ganze Problem ist hausgemacht: Wenn Du das Code-Stück mal ent-tibblest und mit richtigen data.frames rechnest, dann funktioniert alles supi und Du kriegst ein sauberes Ergebnis.

Wäre es denn sehr schlimm, diesen einen Schritt mal ohne tidyverse zu machen, wenn er dafür fertig ist?

Du musst es ja keinem erzählen. Verpack einfach den tidyverse-freien Kram in eine Funktion und wandle df_2 danach in ein tibble um.

Code: Alles auswählen

Id <- c(1,1,1,1,2,3,3,3)
Startdatum <- as.Date(c("2020-06-22","2020-06-25","2020-07-29","2020-08-16","2020-06-22","2020-09-22","2020-09-24","2020-10-22"))
Stoppdatum <- as.Date(c("2020-06-24","2020-07-28","2020-08-02","2020-08-19","2020-06-24","2020-09-23","2020-09-28","2020-10-22"))
Var1 <- c("a","b","c","D","E","g","H","J")
Var2 <- c("g","h","j","k","l","K","L","B")
df <- data.frame(Id,Startdatum,Stoppdatum,Var1,Var2)

# 1. Finde heraus, welche Zeilen zusammengefasst werden sollen

lines <- nrow(df)
join <-  integer(lines)
i <-  1    # markiert die gerade bearbeiteten Zeilen in d
ji <- 1    # zählt die gerade bearbeitete Gruppe zusammengehöriger Zeilen und wird in join eingetragen
while(TRUE){
  join[i] <- ji
  if (i >= lines) break
  i <- i + 1
  if(df$Id[i-1] != df$Id[i] | (df$Stoppdatum[i-1] + 1) != df$Startdatum[i])  ji <- ji + 1
}

print(join)  # Das ist ein Vector mit einem Eintrag pro Zeile im Datensatz
# Zeilen mit gleichem Eintrag in join müssen zusammengefasst werden


# 2. Fasse Zeilen zusammen

neu<-by(df, join, FUN = function(ausschnitt){
  r <- ausschnitt[1,]         # r steht für result und ist erstmal eine Kopie der ersten Zeile jedes Blocks
  r$Stoppdatum <- ausschnitt[nrow(ausschnitt), "Stoppdatum"]
  
  # TODO: hier ggf. noch Werte von Var1, Var2, Var3 etc anpassen
  r$Var2 <- ausschnitt[nrow(ausschnitt), "Var2"] 
  r
})

df_2 <- do.call("rbind", neu)

str(df_2)
str(df_2$Stoppdatum)
library(magrittr)
df_2 %<>% as.tibble()
df_2 %>% str()
Als entgegenkommen habe ich die Umwandlung in ein tibble schon drin und mit einer Pipe aufgerufen. Man sieht ihm danach gar nicht mehr an, dass er mal ein schnödes Nicht-tibble war.

LG,
Bernhard
---
Programmiere stets so, dass die Maxime Deines Programmierstils Grundlage allgemeiner Gesetzgebung sein könnte
Jörg
Beiträge: 25
Registriert: Mi Nov 09, 2016 2:58 pm

Re: Beobachtunge aufgrund von Datumsvariablen zusammenführen

Beitrag von Jörg »

Hallo Bernhard,
super, vielen Dank. Und nein, das ist gar kein Problem. Zwar arbeite ich bei den normalen Schritten der Datenaufbereitung mittlerweile meist einheitlich mit tidyverse, aber ich habe schon einige Einzelprobleme gehabt, wo ich schneller zu Lösungen außerhalb von tidyverse gekommen bin. Aber es ist mal wieder lehrreich zu sehen, dass in diesen unterschiedlichen Formaten auch die Ursache für Probleme liegen kann. Sowas hatte ich schon mal und ich hätte diesmal selbst drauf kommen können. Naja, das nächste Mal :-)

Liebe Grüße
Jörg
bigben
Beiträge: 2771
Registriert: Mi Okt 12, 2016 9:09 am

Re: Beobachtunge aufgrund von Datumsvariablen zusammenführen

Beitrag von bigben »

Jörg hat geschrieben: Di Sep 01, 2020 4:55 pm Aber es ist mal wieder lehrreich zu sehen, dass in diesen unterschiedlichen Formaten auch die Ursache für Probleme liegen kann.
Tja, die Giganten auf deren Schultern wir stehen, haben sich schon auch was gedacht, als sie den data.frame erfunden haben. Wenn ich aus einem Dataframe einen einzelnen Wert auslese, dann werde ich wohl einen einzelnen Wert haben wollen. Das macht ein data.frame dann auch so:

Code: Alles auswählen

bsp <- data.frame(a = 1:3, b = 1:3)
str(bsp[2,2])
Die Popstars die heute R umschreiben wollen halten das für inkonsistent. Für die gehören alle Daten immer in ein Tibble und wenn ich einen Wert aus einem Tibble auslesen will, dann will ich bestimmt ein Tibble mit nur einem Wert drin

Code: Alles auswählen

library(tibble)
bsp <- tibble(a = 1:3, b = 1:3)
str(bsp[2,2])
Aber es gibt bestimmt eine eigene Funktion, deren Namen man sich merken muss, der Dir und mir aber gerade nicht einfällt. Ich auch nicht so wichtig, hauptsache er nimmt die Daten als erstes Argument an und wird mit Unterstrich geschrieben, damit das alles konsistent bleibt.. :P

Na dann,
viel Erfolg noch mit Deiner Auswertung,
Bernhard
---
Programmiere stets so, dass die Maxime Deines Programmierstils Grundlage allgemeiner Gesetzgebung sein könnte
Benutzeravatar
EDi
Beiträge: 1599
Registriert: Sa Okt 08, 2016 3:39 pm

Re: Beobachtunge aufgrund von Datumsvariablen zusammenführen

Beitrag von EDi »

Aber es gibt bestimmt eine eigene Funktion, deren Namen man sich merken muss, der Dir und mir aber gerade nicht einfällt.
Wäre dann pull() im tidyverse.
Wenn ich aus einem Dataframe einen einzelnen Wert auslese, dann werde ich wohl einen einzelnen Wert haben wollen. Das macht ein data.frame dann auch so:
Nicht unbedingt und mich hat es auch schonmal umgekehrt gebissen (ungewollte Klassenänderung), aber dafür gibt es das drop=FALSE argument in ?`[`...
Bitte immer ein reproduzierbares Minimalbeispiel angeben. Meinungen gehören mir und geben nicht die meines Brötchengebers wieder.

Dieser Beitrag ist lizensiert unter einer CC BY 4.0 Lizenz
Bild.
Antworten