J'ai une trame de données structurée comme suit, avec le nombre de paires x_L et x_R pouvant aller jusqu'à 100.

ID  Side  A_L   A_R   B_L   B_R
1    0     7     5     6     3
2    1     3     2     3     1
3    0     6     3     4     5

Je sais que je veux créer une nouvelle colonne A_ratio pour chaque paire A_L et A_R. La valeur de cette nouvelle colonne pour chaque ID doit être soit A_L / A_R si Side==0, soit A_R / A_L si Side==1.

Ma tentative consistait à implémenter une boucle for avec un incrément de 2, pour couvrir chaque paire une seule fois, et à utiliser ifelse pour différencier les deux valeurs possibles de Side

for (i in seq(from = 3, to = length(df), by = 2)) {
  df[sub("_L", "_ratio",(names(df[i]))] <-ifelse(df[2]==0, df[i] / df[i+1], df[i+1] / df[i])
}

Cela conduit cependant à l'avertissement suivant :

In `[<-.data.frame`(`*tmp*`, sub("_L", "_ratio", names(df[i])),  : provided 3 variables to replace 1 variables

Les valeurs résultantes semblent être A_L / A_R pour tous les cas, même pour les ID avec Side==1. Quelqu'un pourrait me signaler mon erreur ? Je sais qu'il devrait également y avoir une approche dplyr plus simple, mais je ne la connais malheureusement pas encore. Merci d'avance!

2
Adrian Mak 24 mars 2021 à 16:23

2 réponses

Meilleure réponse

Voici une approche dplyr.

Il repose uniquement sur les colonnes en mutation qui se terminent par "L". Ensuite, en utilisant cur_column() pour obtenir le nom de la colonne sur laquelle on travaille, nous pouvons créer le nom de la colonne correspondante avec str_replace et get() sa valeur.

Nous pouvons combiner cette approche avec case_when pour considérer l'effet de Side.

Enfin, nous pouvons utiliser rename_with pour corriger les noms de colonnes car ils seront basés sur le nom de colonne d'origine L.

library(dplyr) 
library(stringr)
df %>%
  mutate(across(ends_with("L"), ~case_when(Side == 1 ~ get(str_replace(cur_column(),"L$","R"))/.,
                                           Side == 0 ~ ./get(str_replace(cur_column(),"L$","R"))),
                .names = "{.col}_Ratio")) %>%
  rename_with(~str_replace(.,"_L",""),contains("_L_"))
  ID Side A_L A_R B_L B_R   A_Ratio   B_Ratio
1  1    0   7   5   6   3 1.4000000 2.0000000
2  2    1   3   2   3   1 0.6666667 0.3333333
3  3    0   6   3   4   5 2.0000000 0.8000000
3
Ian Campbell 24 mars 2021 à 14:00

Une option de base R

cols <- c("A", "B")
cbind(
    df,
    `colnames<-`(
        sapply(
            cols,
            function(x) {
                with(
                    df,
                    do.call("/", df[startsWith(names(df), x)])^(1 - 2 * Side)
                )
            }
        ), paste0(cols, "_ratio")
    )
)

Donne

  ID Side A_L A_R B_L B_R   A_ratio   B_ratio
1  1    0   7   5   6   3 1.4000000 2.0000000
2  2    1   3   2   3   1 0.6666667 0.3333333
3  3    0   6   3   4   5 2.0000000 0.8000000
3
ThomasIsCoding 24 mars 2021 à 13:40