Remplacer une expression par son résultat dans un "call"

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 faron
Messages : 586
Enregistré le : 16 Fév 2011, 11:23

Remplacer une expression par son résultat dans un "call"

Messagepar matthieu faron » 09 Oct 2017, 07:41

Bonjour à tous,

J'essaye de changer un petit peu ma façon de programmer en utilisant tidyverse et notamment la possibilité de "nester" des data.frame ou des list dans une colonne d'un data.frame. L’intérêt et de pouvoir garder ensemble toutes les étapes d'un développement. Par exemple en utilisant le jeu de données factice suivant :

Code : Tout sélectionner

library(tidyverse)
library(survival)

d <- data.frame(delai = runif(n = 1000, min = 1, max = 100),
                status = sample(c(0,1), size = 1000, replace = TRUE),
                var_a = sample(c("A", "B"), size = 1000, replace = TRUE),
                var_b = sample(c("C", "D"), size = 1000, replace = TRUE)
                )


Nous voulons faire l'analyse de plusieurs (en vrai > 10) modèles différents qui peuvent dans le vrai exemple contenir plusieurs variables. On crée donc un tibble qui contient le nom du modèle et la formule que l'on va utiliser pour la survie :

Code : Tout sélectionner

tout <- tibble(variable = c("var_a", "var_b"))

tout <- tout %>% mutate(formule = map_chr(variable, ~ paste("Surv(delai, status) ~", .)))


Ce qui donne :

Code : Tout sélectionner

# A tibble: 2 x 2
  variable                     formule
     <chr>                       <chr>
1    var_a Surv(delai, status) ~ var_a
2    var_b Surv(delai, status) ~ var_b


On ne transforme pas tout de suite l'expression en formule car après c'est plus difficile de travailler avec car le tibble reconnait la formule comme une liste...

Ensuite on fait le survfit dessus :

Code : Tout sélectionner

tout <- tout %>% mutate(survie = map(formule, ~ survfit(as.formula(.), data = d)))


Ce qui donne :

Code : Tout sélectionner

# A tibble: 2 x 3
  variable                     formule        survie
     <chr>                       <chr>        <list>
1    var_a Surv(delai, status) ~ var_a <S3: survfit>
2    var_b Surv(delai, status) ~ var_b <S3: survfit>


Jusque là tout va bien. Le problème provient du fait que le "call" à la fonction survfit ne contient pas la formule que nous avons crée mais :

Code : Tout sélectionner

> tout$survie[[1]]$call
survfit(formula = as.formula(.), data = d)

Alors que l'on aimerait obtenir la vrai formule (le résultat) à la place de "as.formula(.)"


Ceci devient problématique par la suite car quand on essaye de les plotter via survminer::ggsurvival() la fonction ne retrouve pas (à juste titre) la formule qui doit être utilisée.

Il y a-t-il un moyen de remplacer le as.formula(.) dans le call par la valeur à laquelle il correspond ? Je pensais avoir déjà vu quelque chose de similaire avec substitute() sans réussir à le refaire....

Évidemment on peut le faire facilement avec lapply mais on trouve les avantages de tout mettre dans un tibble non négligeable...

Merci par avance de vos lumières
Matthieu FARON

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

Re: Remplacer une expression par son résultat dans un "call"

Messagepar Pierre-Yves Berrard » 10 Oct 2017, 07:24

Bonjour,
Tu ne peux pas utiliser formule sous forme texte que tu as créé à la première étape ?
PY

matthieu faron
Messages : 586
Enregistré le : 16 Fév 2011, 11:23

Re: Remplacer une expression par son résultat dans un "call"

Messagepar matthieu faron » 10 Oct 2017, 13:59

Bonjour, Merci de votre réponse,

Je n'arrive pas à utiliser formule sous forme texte.

J'ai essayé de remplacer dans le "call" par la formule sous forme texte avec quelque chose un peu comme ça :

Code : Tout sélectionner

tout <- tout %>% mutate(survie2 = map2(formule, survie, function(x, y) {y$call <- as.call(gsub("as.formula(.)", x, as.character(y$call), fixed = TRUE)) ; return(y)}))


Mais ça ne marche pas.... je ne connaissais pas les objets de type "call" mais ils sont assez difficile à travailler....

Mon problème ne survient que en utilisant survminer::ggsurvplot mais pas d'autres :

Code : Tout sélectionner

> walk(tout$survie, ggsurvplot, data = d) ## ne marche pas
Error in as.formula(.) : object '.' not found

## alors que
walk(tout$survie, plot) ## marche mais le plot est tout moche


J'ai pensé à une autre possibilité (c'est un peu moins élégant tout de même) :

Code : Tout sélectionner

tout <- tibble(variable = c("var_a", "var_b"))

tout <- tout %>% mutate(formule = map(variable, ~ as.formula(paste("Surv(delai, status) ~", .))))

tout <- tout %>% mutate(survie = map(formule, ~ survfit(unlist(.), data = d)))

## ce qui donne :
# A tibble: 2 x 3
  variable       formule        survie
     <chr>        <list>        <list>
1    var_a <S3: formula> <S3: survfit>
2    var_b <S3: formula> <S3: survfit>


On peut voire que le problème du "call" n'est pas réglé :

Code : Tout sélectionner

> tout$survie[[1]]$call
survfit(formula = unlist(.), data = d)


Par contre on peut utiliser une autre option de survminer qui est de faire les courbes sur une liste de formule et de tout plotter d'un coup :

Code : Tout sélectionner

ggsurvplot(surv_fit(tout$formule, data = d), combine = TRUE)


Ou tout simplement :

Code : Tout sélectionner

tout <- tout %>% mutate(figure = ggsurvplot(surv_fit(formule, data = d)))


Et comme ça on y est presque ! Il y auara juste deux appels différents à la fonction surv_fit... c'est plus qu'acceptable

Si quelqu'un a plus l'habitude de manipuler les objets de type call...
Matthieu FARON

Logez Maxime
Messages : 3138
Enregistré le : 26 Sep 2006, 11:35

Re: Remplacer une expression par son résultat dans un "call"

Messagepar Logez Maxime » 10 Oct 2017, 14:58

Bonjour,

une possibilité :

Code : Tout sélectionner

tout <- tout %>% mutate(survie2 = map(formule, ~ eval(as.call(list(survfit, as.formula(.), quote(d))))))
tout$survie2
[[1]]
Call: survfit(formula = Surv(delai, status) ~ var_a, data = d)

          n events median 0.95LCL 0.95UCL
var_a=A 502    250   79.4    75.6    84.3
var_a=B 498    244   77.4    72.4    81.7

[[2]]
Call: survfit(formula = Surv(delai, status) ~ var_b, data = d)

          n events median 0.95LCL 0.95UCL
var_b=C 544    269   75.6    72.1    81.6
var_b=D 456    225   79.7    76.3    85.8

# ou
tout <- tout %>% mutate(survie3 = map(formule, ~ do.call(survfit, list(as.formula(.), quote(d)))))
tout$survie3
[[1]]
Call: survfit(formula = Surv(delai, status) ~ var_a, data = d)

          n events median 0.95LCL 0.95UCL
var_a=A 502    250   79.4    75.6    84.3
var_a=B 498    244   77.4    72.4    81.7

[[2]]
Call: survfit(formula = Surv(delai, status) ~ var_b, data = d)

          n events median 0.95LCL 0.95UCL
var_b=C 544    269   75.6    72.1    81.6
var_b=D 456    225   79.7    76.3    85.8
Cordialement,
Maxime

matthieu faron
Messages : 586
Enregistré le : 16 Fév 2011, 11:23

Re: Remplacer une expression par son résultat dans un "call"

Messagepar matthieu faron » 11 Oct 2017, 12:44

Superbe !!

Merci Maxime ça marche super bien, je vais utiliser celle avec le do.call.... (même si je ne comprends pas très bien pourquoi ça marche parfaitement bien en créant le call à la fonction survfit comme tu le proposes et pas dans la version de base de dply...). Le problème est résolu.

Bonne journée

P.S. : on se doutait que la solution (pas facile à trouver) viendrait de quelqu'un comme toi... merci
Matthieu FARON

Logez Maxime
Messages : 3138
Enregistré le : 26 Sep 2006, 11:35

Re: Remplacer une expression par son résultat dans un "call"

Messagepar Logez Maxime » 11 Oct 2017, 13:19

Bonjour,

une autre alternative :

Code : Tout sélectionner

tout %>% mutate(formule = paste0("Surv(delai, status) ~ ", variable)) %>%
  group_by(variable) %>%
  mutate(survie = list(do.call(survfit, list(as.formula(formule), quote(d)))))
Cordialement,
Maxime


Retourner vers « Questions en cours »

Qui est en ligne

Utilisateurs parcourant ce forum : Aucun utilisateur enregistré et 1 invité