Transformer une data frame wide en format long (start/stop) à plusieurs variables

Postez ici vos questions, réponses, commentaires ou suggestions - Les sujets seront ultérieurement répartis dans les archives par les modérateurs

Modérateur : Groupe des modérateurs

matthieu jamme
Messages : 5
Enregistré le : 18 Mar 2019, 09:33

Transformer une data frame wide en format long (start/stop) à plusieurs variables

Messagepar matthieu jamme » 18 Mar 2019, 11:32

Bonjour à tous,

Je me permets de faire ce post car j’ai un petit soucis de datamanning et j'aurais besoin de conseil.

Je cherche à transformer une dataset (format wide) afin de pouvoir faire un cox temps dependant avec plusieurs variables dépendantes du temps. Pour cela, il me faut une dataframe en format long. Le soucis c'est que j’arrive à le faire avec une variable mais pas avec plusieurs variables.

Voici l’exemple afin d’être plus précis :
Voici le format de ma table actuelle :
ID CGR Del_CGR CUP Del_CUP PFC Del_PFC STATUT Del_Hospit
00001 1 2 1 6 1 10 1 15
00002 1 5 0 0 0 0 0 10

J’aimerais la transformer dans le format suivant :
ID Start Stop CGR CUP PFC Event
00001 0 2 0 0 0 0
00001 2 6 1 0 0 0
00001 6 10 1 1 0 0
00001 10 15 1 1 1 1
00002 0 5 0 0 0 0
00002 5 10 1 0 0 1

J’arrive à le faire avec une variable (CGR ou CUP ou PFC) mais pas les 3 en même temps.
Mon script reproductible qui me permet de le faire est le suivant :

baz<-data.frame(ID=c(00001,00002),CGR=c(1,1), Del_CGR=c(2,5),CUP=c(1,0),
Del_CUP=c(6,NA),PFC=c(1,0),Del_PFC=c(10,NA),STATUT=c(1,0),
Del_Hospit=c(15,10))

baz$Del_CGR<-as.numeric(as.character(baz$Del_CGR))
baz$Del_CUP<-as.numeric(as.character(baz$Del_CUP))
baz$Del_PFC<-as.numeric(as.character(baz$Del_PFC))
baz$Del_Hospit<-as.numeric(as.character(baz$Del_Hospit))

baz.A <- subset(baz)
baz.A$Start <- 0
baz.A$Stop <- pmin(baz.A$Del_Hospit, baz.A$Del_CGR)
baz.A$EV <- baz.A$STATUT
baz.A$EV[!is.na(baz.A$Del_CGR)] <- 0
baz.A$CGR <- 0

baz.B <- subset(baz ,!is.na(Del_CGR) )
baz.B$Start <- baz.B$Del_CGR
baz.B$Stop <- baz.B$Del_Hospit
baz.B$EV <- baz.B$STATUT
baz.B$CGR <- 1

baz.TD <- rbind(baz.A ,baz.B)
baz.TD <- baz.TD[order(baz.TD$ID,baz.TD$ Start),]

En vous remerciant,
Matthieu

Pierre-Yves Berrard
Messages : 1029
Enregistré le : 12 Jan 2016, 23:30

Re: Transformer une data frame wide en format long (start/stop) à plusieurs variables

Messagepar Pierre-Yves Berrard » 18 Mar 2019, 11:57

Bonjour,

Pourriez-vous expliquer en français comment sont calculés start et stop ? (je crois deviner, mais mieux vaut être sûr)

Les autres variables du résultat souhaité sont obscures pour moi.
Par exemple, pourquoi CGR vaut 0 en ligne 1, pourquoi CUP vaut 0 en ligne 2 ?
Comment est construit Event ?
PY

matthieu jamme
Messages : 5
Enregistré le : 18 Mar 2019, 09:33

Re: Transformer une data frame wide en format long (start/stop) à plusieurs variables

Messagepar matthieu jamme » 18 Mar 2019, 16:11

Pardonnez mon défaut de clarté concernant les variables.

Il s'agit d'une base de données de patients admis à l'hôpital qui vont durant leur séjour recevoir ou non une transfusion de globules rouges (CGR), plaquettes (CUP) ou de plasma (PFC). A chaque transfusion, je connais son délai de réalisation par rapport à l'admission (Del_CGR, Del_CUP ou Del_PFC). Le patient est hospitalisé pour une durée totale correspondant à la variable Del_Hospit. A sa sortie d'hospitalisation, le patient est soit sortis vivant (STATUT = 0) ou décédé (STATUT = 1).

Cette base initiale est constituée avec 1 ligne par patient. Par exemple, si je reprend mon exemple, mon 1er patient a été hospitalisé 15 jours au total et est malheureusement décédé. Durant son séjour, il a été transfusé à la fois en globules rouges, plaquettes et plasma mais à des temps différents (J2, J6 et J10). Mon 2e patient n'a lui été transfusé qu'en globule rouge à J5 et est sorti vivant au bout de 10 jours.

J'aimerais obtenir une base avec n lignes par patients, n correspondant aux transfusions au cours du temps. Le Start et Stop correspondant aux délais durant laquelle le patient n'a pas eu de nouvelle transfusion ou changé de Statut.

Par exemple, pour mon 1er malade, la 1ère ligne correspond à la période entre son admission et la réalisation de la première transfusion (Entre J0(=Start) et J2(=STOP)) : les variables de transfusion (CGR, CUP ou PFC) sont donc nulles car il n'a pendant cette première période jamais été transfusé et l'évènement correspondant au statut du patient est lui aussi nul puisque le patient est toujours vivant. Puis la 2e ligne correspond à la période entre sa première transfusion (ici de CGR) à J2(=Start) et sa deuxième transfusion (ici de CUP) à J6 (=Stop). Sur cette ligne, la variable CGR vaut 1 car le patient a déjà été transfusé en globules rouges mais la variable CUP vaut encore 0 puisqu'il n'a pas encore reçu la transfusion. La 3e ligne correspond à la période entre la 2e transfusion (de CUP) puis la 3e transfusion (de PFC) entre J6 (= Start) et J10 (= Stop). Sur cette ligne, la variable CGR et CUP valent 1 puisque le patient a déjà été transfusé sur ces 3 types de produits mais encore en PFC donc la variable PFC vaut 0. Enfin la dernière ligne correspond à la période entre la dernière transfusion et la sortie d'hospitalisation. La variable EVENT correspond au STATUT du patient à sa sortie d'hôpital.

Pour le 2e malade qui n'a finalement été transfusé qu'en globules rouges, je vais avoir 2 lignes : 1 ligne entre son admission et sa transfusion puis 1 ligne entre sa transfusion et la sortie du patient.

J'espère que cela est désormais plus claire ?

Pierre-Yves Berrard
Messages : 1029
Enregistré le : 12 Jan 2016, 23:30

Re: Transformer une data frame wide en format long (start/stop) à plusieurs variables

Messagepar Pierre-Yves Berrard » 18 Mar 2019, 22:44

Merci c'est parfaitement clair maintenant.

Une solution (il y a peut-être plus simple) :

Code : Tout sélectionner

library(dplyr)
library(tidyr)

res0 <- baz
names(res0)[c(3, 5, 7, 9)] <- "Stop"

res1 <- bind_rows(
  res0[c(1, 2, 3)],
  res0[c(1, 4, 5)],
  res0[c(1, 6, 7)],
  res0[c(1, 8, 9)]
)

res <-
  res1 %>%
  filter(!is.na(Stop)) %>%
  mutate(Start = Stop) %>%
  group_by(ID) %>%                   # tous les traitements suivants pour chaque ID
  mutate_at(
    c("Start", "CGR", "CUP", "PFC"),
    function(x) c(0, x[-length(x)])  # fonction qui décale d'un cran les valeurs
  ) %>%
  fill(CGR, CUP, PFC)                # remplit ces colonnes avec la dernière valeur non vide
PY

Mickael Canouil
Messages : 1315
Enregistré le : 04 Avr 2011, 08:53
Contact :

Re: Transformer une data frame wide en format long (start/stop) à plusieurs variables

Messagepar Mickael Canouil » 19 Mar 2019, 10:06

Bonjour,

voici une proposition avec le tidyverse, par forcément plus simple.
Au passage, votre exemple du format souhaité n'est pas cohérent. Un coup votre "événement" survient strictement dans l'intervalle, un coup dans l'intervalle ouvert à droite, ...

"Hospit" est votre variable de statut à al sortie de l’hôpital.
Comme la table ne contient que des événements, ça n'avait pas vraiment de sens de nommer "Event" la variable de statut en sortie de l’hôpital.

Code : Tout sélectionner

library(tidyverse)
dta <- read.table(header = TRUE, text = "ID CGR Del_CGR CUP Del_CUP PFC Del_PFC STATUT Del_Hospit
00001 1 2 1 6 1 10 1 15
00002 1 5 0 0 0 0 0 10")

dta %>%
  unite(col = "CGR", contains("CGR")) %>%
  unite(col = "CUP", contains("CUP")) %>%
  unite(col = "PFC", contains("PFC")) %>%
  unite(col = "Hospit", STATUT, Del_Hospit, sep = "_") %>%
  gather(key = "Var", value = "Value", -ID) %>%
  separate(col = "Value", into = c("Event", "Event_Time"), sep = "_", convert = TRUE) %>%
  filter(Event_Time>0) %>%
  group_by(ID) %>%
  mutate(Time = list(sort(unique(c(0, Event_Time))))) %>%
  unnest() %>%
  mutate(Event = ifelse(Time >= Event_Time, Event, 0)) %>%
  select(-Event_Time) %>%
  spread(key = "Var", value = "Event", fill = 0) %>%
  rename(Start = Time) %>%
  mutate(
    Stop = c(Start[-1], Start[length(Start)])
  ) %>%
  select(ID, Start, Stop, Hospit, everything()) %>%
  ungroup() %>%
  mutate(Interval = paste0("[", Start, ";", Stop, "["))
#> # A tibble: 8 x 8
#>      ID Start  Stop Hospit   CGR   CUP   PFC Interval
#>   <int> <dbl> <dbl>  <dbl> <dbl> <dbl> <dbl> <chr>   
#> 1     1     0     2      0     0     0     0 [0;2[   
#> 2     1     2     6      0     1     0     0 [2;6[   
#> 3     1     6    10      0     1     1     0 [6;10[ 
#> 4     1    10    15      0     1     1     1 [10;15[
#> 5     1    15    15      1     1     1     1 [15;15[
#> 6     2     0     5      0     0     0     0 [0;5[   
#> 7     2     5    10      0     1     0     0 [5;10[ 
#> 8     2    10    10      0     1     0     0 [10;10[


Cordialement,
Mickaël
mickael.canouil.fr | rlille.fr

matthieu jamme
Messages : 5
Enregistré le : 18 Mar 2019, 09:33

Re: Transformer une data frame wide en format long (start/stop) à plusieurs variables

Messagepar matthieu jamme » 19 Mar 2019, 10:33

Mille mercis,
Ca marche très bien et c'est assez simple.
Je ne connais pas très bien tidyverse, je vais chercher des cours et mooc, ça me semble très utile,
mj

Mickael Canouil
Messages : 1315
Enregistré le : 04 Avr 2011, 08:53
Contact :

Re: Transformer une data frame wide en format long (start/stop) à plusieurs variables

Messagepar Mickael Canouil » 21 Mar 2019, 14:42

Hadley Wickham vient de déployer des fonctions pivot_* dans la version développement de tidyr => https://tidyr.tidyverse.org/dev/articles/pivot.html
Des fonctions qui viennent en complément de gather et spread.
Mickaël
mickael.canouil.fr | rlille.fr

Serge Rapenne
Messages : 1426
Enregistré le : 20 Aoû 2007, 15:17
Contact :

Re: Transformer une data frame wide en format long (start/stop) à plusieurs variables

Messagepar Serge Rapenne » 21 Mar 2019, 16:24

Hello,

pour les fonctions pivot_, si j'ai bien compris elles ne viennent pas en complément de gather et spread mais plutôt en remplacement même si les secondes ne seront pas supprimées :

En réponse à la question :
will gather() and spread() be deprecated

Hadley Wickham a répondu :
Sort of. We'll stop encouraging people to use them and i won't fix bugs but they'll continue to exist in their present form


Et aujourd'hui, les "pivot_" ne sont pour le moment que dans la version de dev de tidyr

Serge


Retourner vers « Questions en cours »

Qui est en ligne

Utilisateurs parcourant ce forum : Google [Bot] et 1 invité