2 votes

Fonction définie par l'utilisateur avec mutate et case_when en R

Je voudrais savoir si/comment je peux transformer l'appel ci-dessous en une fonction qui peut être utilisée dans une tâche que je fais assez souvent avec mes données. Malheureusement, je n'arrive pas à trouver comment concevoir une fonction à partir de l'appel qui implique mutate y case_when ces deux fonctions reposent sur dplyr et nécessite un certain nombre d'arguments supplémentaires.

Alternativement, l'appel lui-même me semble redondant avec tant de case_when il est peut-être possible de réduire le nombre de fois où il est utilisé.

Toute aide et information sur des approches alternatives est la bienvenue.

L'appel ressemble à ceci :

library(dplyr)
library(stringr)

test_data %>%
  mutate(
    multipleoptions_o1 = case_when(
      str_detect(multipleoptions, "option1") ~ 1,
      is.na(multipleoptions) ~ NA_real_,
      TRUE ~ 0),
    multipleoptions_o2 = case_when(
      str_detect(multipleoptions, "option2") ~ 1,
      is.na(multipleoptions) ~ NA_real_,
      TRUE ~ 0),
    multipleoptions_o3 = case_when(
      str_detect(multipleoptions, "option3") ~ 1,
      is.na(multipleoptions) ~ NA_real_,
      TRUE ~ 0),
    multipleoptions_o4 = case_when(
      str_detect(multipleoptions, "option4") ~ 1,
      is.na(multipleoptions) ~ NA_real_,
      TRUE ~ 0)
  )

Les données de l'échantillon :

structure(list(multipleoptions = c("option1", "option2", "option3", 
NA, "option2,option3", "option4")), row.names = c(NA, -6L), class = c("tbl_df", 
"tbl", "data.frame"))

Sortie souhaitée de la fonction :

structure(list(multipleoptions = c("option1", "option2", "option3", 
NA, "option2,option3", "option4"), multipleoptions_o1 = c(1, 
0, 0, NA, 0, 0), multipleoptions_o2 = c(0, 1, 0, NA, 1, 0), multipleoptions_o3 = c(0, 
0, 1, NA, 1, 0), multipleoptions_o4 = c(0, 0, 0, NA, 0, 1)), class = c("tbl_df", 
"tbl", "data.frame"), row.names = c(NA, -6L))

Les arguments de la fonction devraient probablement être : data (c'est-à-dire l'ensemble des données d'entrée), multipleoptions (c'est-à-dire la colonne des données contenant les options de réponse, toujours une colonne), patterns_to_look_for (i.e., str_detecter les motifs à rechercher dans les options multiples), number_of_options Dans l'idéal, le nombre d'options peut être supérieur ou inférieur à 4 (je ne suis pas sûr que ce soit réalisable), output_columns (c'est-à-dire les noms des nouvelles colonnes, c'est toujours le nom de la colonne d'origine suivi du numéro de l'option ou du nom de l'option).

4voto

eipi10 Points 3549

Vous pouvez éviter les longues case_when en divisant les options en éléments distincts, en tirant parti de l'imbrication/la désimbrication pour obtenir une seule colonne d'options, puis en étalant pour obtenir une colonne distincte pour chaque option.

Réponse actualisée

library(tidyverse)

# Arguments
# data     A data frame
# patterns Regular expression giving the pattern(s) at which to split the options strings
# ...      Grouping columns, the first of which must be the "options" column.
#           If options has repeated values, then there must be a second grouping 
#           column (an "ID" column) to differentiate these repeated values.
fnc = function(data, patterns, ...) {
  col = quos(...)

  data %>% 
    mutate(option=str_split(!!!col[[1]], patterns)) %>% 
    unnest %>% 
    mutate(value=1) %>% 
    group_by(!!!col) %>% 
    mutate(num_chosen = ifelse(is.na(!!!col[[1]]), 0, sum(value))) %>% 
    spread(option, value, fill=0) %>%
    select_at(vars(-matches("NA")))
}

fnc(test_data, ",", multipleoptions)
  multipleoptions num_chosen option1 option2 option3 option4
1         option1          1       1       0       0       0
2         option2          1       0       1       0       0
3 option2,option3          2       0       1       1       0
4         option3          1       0       0       1       0
5         option4          1       0       0       0       1
6            <NA>          0       0       0       0       0
# Fake data
ops = paste0("option",1:4)

set.seed(2)
d = data_frame(var=replicate(20, paste(sample(ops, sample(1:4,1, prob=c(10,8,5,1))), collapse=","))) 
# Add missing values
d = bind_rows(d[1:5,], data.frame(var=rep(NA,3)), d[6:nrow(d),])

fnc(d %>% mutate(ID=1:n()), ",", var, ID)
                               var ID num_chosen option1 option2 option3 option4
1                          option1 17          1       1       0       0       0
2                  option1,option2 12          2       1       1       0       0
3          option1,option2,option3  5          3       1       1       1       0
4  option1,option2,option4,option3  9          4       1       1       1       1
5                  option1,option3  2          2       1       0       1       0
6          option1,option3,option4  3          3       1       0       1       1
7          option1,option4,option2 20          3       1       1       0       1
8  option1,option4,option3,option2 13          4       1       1       1       1
9                          option2 11          1       0       1       0       0
10                 option2,option3 23          2       0       1       1       0
11         option2,option3,option4 21          3       0       1       1       1
12                         option3  1          1       0       0       1       0
13                         option3 15          1       0       0       1       0
14                 option3,option1  4          2       1       0       1       0
15         option3,option2,option4 14          3       0       1       1       1
16 option3,option4,option2,option1 22          4       1       1       1       1
17                         option4 10          1       0       0       0       1
18                         option4 16          1       0       0       0       1
19                         option4 18          1       0       0       0       1
20         option4,option2,option3 19          3       0       1       1       1
21                            <NA>  6          0       0       0       0       0
22                            <NA>  7          0       0       0       0       0
23                            <NA>  8          0       0       0       0       0

Réponse originale

test_data %>% 
  filter(!is.na(multipleoptions)) %>% 
  mutate(option=str_split(multipleoptions, ",")) %>% 
  unnest %>% 
  mutate(value=1) %>% 
  spread(option, value)
  multipleoptions option1 option2 option3 option4
  <chr>             <dbl>   <dbl>   <dbl>   <dbl>
1 option1               1      NA      NA      NA
2 option2              NA       1      NA      NA
3 option2,option3      NA       1       1      NA
4 option3              NA      NA       1      NA
5 option4              NA      NA      NA       1

Emballer cela dans une fonction :

fnc = function(data, col, patterns) {
  col = enquo(col)

  data %>% 
    filter(!is.na(!!col)) %>% 
    mutate(option=str_split(!!col, patterns)) %>% 
    unnest %>% 
    mutate(value=1) %>% 
    spread(option, value)
}

fnc(test_data, multipleoptions, ",")

Si vos données réelles comportent plus d'une ligne avec la même valeur de multipleoptons alors ce code ne fonctionnera que s'il y a également un fichier de type ID qui distingue les différentes lignes ayant la même valeur de multipleoptions . Par exemple :

# Fake data
ops = paste0("option",1:4)

set.seed(2)
d = data.frame(var=replicate(20, paste(sample(ops, sample(1:4,1, prob=c(10,8,5,1))), collapse=",")))

fnc(d, var, ",")

Erreur : Identificateurs en double pour les lignes (1, 27), (16, 28, 30)

# Add unique row identifier
fnc(d %>% mutate(ID = 1:n()), var, ",")
                               var ID option1 option2 option3 option4
1                          option1 14       1      NA      NA      NA
2                  option1,option2  9       1       1      NA      NA
3          option1,option2,option3  5       1       1       1      NA
4  option1,option2,option4,option3  6       1       1       1       1
5                  option1,option3  2       1      NA       1      NA
6          option1,option3,option4  3       1      NA       1       1
7          option1,option4,option2 17       1       1      NA       1
8  option1,option4,option3,option2 10       1       1       1       1
9                          option2  8      NA       1      NA      NA
10                 option2,option3 20      NA       1       1      NA
11         option2,option3,option4 18      NA       1       1       1
12                         option3  1      NA      NA       1      NA
13                         option3 12      NA      NA       1      NA
14                 option3,option1  4       1      NA       1      NA
15         option3,option2,option4 11      NA       1       1       1
16 option3,option4,option2,option1 19       1       1       1       1
17                         option4  7      NA      NA      NA       1
18                         option4 13      NA      NA      NA       1
19                         option4 15      NA      NA      NA       1
20         option4,option2,option3 16      NA       1       1       1

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X