J'ai une série de variables avec des valeurs 1,2, 2,5 etc. Je voudrais séparer les nombres par décimal, de sorte que je crée une nouvelle colonne pour le nombre entier et le point décimal, puis attribue un score total.

HT_Q1 <- c(1.2, 2.5, 7.4)
HT_Q2 <- c(2.5, 8.5, 9.5)
AT_Q1 <- c(2.4, 1.2, 1.4)
AT_Q2 <- c(6.5, 1.5, 9.10)
df <- data.frame(HT_Q1, HT_Q2, AT_Q1, AT_Q2)

Je peux le faire en utilisant mutate:

mutate(df,
       HT_Q1_G = trunc(HT_Q1),
       HT_Q1_B = HT_Q1 %% 1 * 10,
       HT_Q1_P = (HT_Q1_G * 6) + HT_Q1_B)

Cependant, je voudrais écrire une fonction pour ne pas avoir à répéter le code ci-dessus pour chaque variable. Est-il possible de passer chaque variable (HT_Q1, HT_Q2 etc.) comme argument à la fonction et de créer les variables correspondantes (par exemple HT_Q2_G, HT_Q2_B, AT_Q2_G etc.)?

J'ai essayé de créer des noms de variables en fonction de l'argument que je passe à la fonction mais cela ne fonctionne pas:

edit_score <- function(var){
  mutate(df,
         paste0(var, "_G") = trunc(var),
         paste0(var, "_B") = var %% 1 * 10,
         paste0(var, "_P") = (paste0(var, "_G") * 6) + paste0(var, "_B"))
}

edit_score(HT_Q1)
edit_score(HT_Q2)
edit_score(AT_Q1)
edit_score(AT_Q2)

Je suis nouveau dans R et je viens d'un fond SAS où j'ai l'habitude d'utiliser le compilateur de macros pour ajuster le texte dans le code avant qu'il ne soit exécuté.

2
mats187 22 août 2020 à 14:24

4 réponses

Meilleure réponse

Vous pouvez utiliser une évaluation non standard ici:

library(dplyr)
library(purrr)
library(rlang)

edit_score <- function(var){
  transmute(df,
         !!paste0(var, "_G") := trunc(!!sym(var)),
         !!paste0(var, "_B") := !!sym(var) %% 1 * 10,
         !!paste0(var, "_P") := !!sym(paste0(var, "_G")) * 6 + 
                                !!sym(paste0(var, "_B")))
}

bind_cols(df, map_dfc(names(df), edit_score))

sym convertit la valeur de caractère du nom de colonne en symbole et !! est utilisé pour l'évaluer.


L'évaluation non standard peut être difficile à comprendre au départ, dans ce cas, vous pouvez également utiliser cette approche de base R:

edit_score <- function(var){
  col1 <- paste0(var, "_G")
  col2 <- paste0(var, "_B")
  col3 <- paste0(var, "_P")
  df[[col1]] <- trunc(df[[var]])
  df[[col2]] <- df[[var]] %% 1 * 10
  df[[col3]] <- df[[col1]] * 6 + df[[col2]]
  df[, c(col1, col2, col3)]
}

cbind(df, do.call(cbind, lapply(names(df), edit_score)))
2
Ronak Shah 22 août 2020 à 11:31

Utiliser uniquement la base R

# python-like string concatenation `+`
`%+%` <- function(str1, str2) { 
  paste0(str1, str2)
}

add_columns <- function(df, col) {
  df[, col %+% "_G"] <- trunc(df[, col])
  df[, col %+% "_B"] <- df[, col] %% 1 * 10
  df[, col %+% "_P"] <- df[,  col %+% "_G"] * 6 + df[, col %+% "_B"]
  df
}

generate_GBP_columns <- function(df) {
  for (col in names(df)) {
    df <- add_columns(df, col)
  }
  df
}

generate_GBP_columns(df)


#   HT_Q1 HT_Q2 AT_Q1 AT_Q2 HT_Q1_G HT_Q1_B HT_Q1_P HT_Q2_G HT_Q2_B HT_Q2_P
# 1   1.2   2.5   2.4   6.5       1       2       8       2       5      17
# 2   2.5   8.5   1.2   1.5       2       5      17       8       5      53
# 3   7.4   9.5   1.4   9.1       7       4      46       9       5      59
#   AT_Q1_G AT_Q1_B AT_Q1_P AT_Q2_G AT_Q2_B AT_Q2_P
# 1       2       4      16       6       5      41
# 2       1       2       8       1       5      11
# 3       1       4      10       9       1      55
0
Gwang-Jin Kim 22 août 2020 à 13:25

Vous pouvez utiliser across() avec la fonctionnalité de lst() qui fait référence aux composants créés précédemment.

library(dplyr)

df %>%
  mutate(across(.fns = lst( G = function(x) trunc(x),
                            B = function(x) x %% 1 * 10,
                            P = ~ (G(.) * 6) + B(.) )))

< gagnantOutput

across() crée automatiquement de nouveaux noms de colonnes séparés par "_" comme vous le souhaitez. Vous pouvez également personnaliser un nouveau modèle de nom avec l'argument .names.

#   HT_Q1 HT_Q2 AT_Q1 AT_Q2 HT_Q1_G HT_Q1_B HT_Q1_P
# 1   1.2   2.5   2.4   6.5       1       2       8
# 2   2.5   8.5   1.2   1.5       2       5      17
# 3   7.4   9.5   1.4   9.1       7       4      46
# 
#   HT_Q2_G HT_Q2_B HT_Q2_P AT_Q1_G AT_Q1_B AT_Q1_P
# 1       2       5      17       2       4      16
# 2       8       5      53       1       2       8
# 3       9       5      59       1       4      10
# 
#   AT_Q2_G AT_Q2_B AT_Q2_P
# 1       6       5      41
# 2       1       5      11
# 3       9       1      55
1
Darren Tsai 23 août 2020 à 07:10

Lorsque vous utilisez tidyverse, il est important d'avoir vos données dans un format long et ordonné. Cela rend l'utilisation des fonctions tidyverse beaucoup plus facile. En utilisant la fonction de collecte, nous pouvons convertir vos données dans un format long et muter appliquera les fonctions à toutes les valeurs.

HT_Q1 <- c(1.2, 2.5, 7.4)
HT_Q2 <- c(2.5, 8.5, 9.5)
AT_Q1 <- c(2.4, 1.2, 1.4)
AT_Q2 <- c(6.5, 1.5, 9.10)
df <- data.frame(HT_Q1, HT_Q2, AT_Q1, AT_Q2)

df <- df %>%
  gather() %>%
  mutate(G = trunc(value), 
         B = value %% 1 * 10,
         P = G*6 + B)

#     key value G B  P
#1  HT_Q1   1.2 1 2  8
#2  HT_Q1   2.5 2 5 17
#3  HT_Q1   7.4 7 4 46
#4  HT_Q2   2.5 2 5 17
#5  HT_Q2   8.5 8 5 53
#6  HT_Q2   9.5 9 5 59
#7  AT_Q1   2.4 2 4 16
#8  AT_Q1   1.2 1 2  8
#9  AT_Q1   1.4 1 4 10
#10 AT_Q2   6.5 6 5 41
#11 AT_Q2   1.5 1 5 11
#12 AT_Q2   9.1 9 1 55

Si vous voulez vraiment revenir au format large, bien que cela ne soit pas recommandé, vous pouvez revenir en arrière avec les éléments suivants:

df <- df %>%
  pivot_wider(id_cols = key, names_from = key, values_from = value:P, values_fn=list, , names_glue = "{key}_{.value}") %>%
  unnest(cols=everything())
colnames(df) = gsub("_value", "", colnames(df))

#  HT_Q1 HT_Q2 AT_Q1 AT_Q2 HT_Q1_G HT_Q2_G AT_Q1_G AT_Q2_G HT_Q1_B HT_Q2_B AT_Q1_B AT_Q2_B HT_Q1_P HT_Q2_P AT_Q1_P AT_Q2_P
#1   1.2   2.5   2.4   6.5       1       2       2       6       2       5       4       5       8      17      16      41
#2   2.5   8.5   1.2   1.5       2       8       1       1       5       5       2       5      17      53       8      11
#3   7.4   9.5   1.4   9.1       7       9       1       9       4       5       4       1      46      59      10      55
1
Jumble 22 août 2020 à 12:57