web-dev-qa-db-fra.com

Le style de dictionnaire remplace plusieurs éléments

J'ai un grand data.frame de données de caractères que je veux convertir en fonction de ce qu'on appelle communément un dictionnaire dans d'autres langues.

Actuellement, je fais comme ça:

foo <- data.frame(snp1 = c("AA", "AG", "AA", "AA"), snp2 = c("AA", "AT", "AG", "AA"), snp3 = c(NA, "GG", "GG", "GC"), stringsAsFactors=FALSE)
foo <- replace(foo, foo == "AA", "0101")
foo <- replace(foo, foo == "AC", "0102")
foo <- replace(foo, foo == "AG", "0103")

Cela fonctionne bien, mais ce n'est évidemment pas joli et semble idiot de répéter l'instruction replace chaque fois que je veux remplacer un élément dans le data.frame.

Y a-t-il une meilleure façon de le faire puisque j'ai un dictionnaire d'environ 25 paires clé/valeur?

32
Stedy
map = setNames(c("0101", "0102", "0103"), c("AA", "AC", "AG"))
foo[] <- map[unlist(foo)]

en supposant que map couvre tous les cas dans foo. Cela ressemblerait moins à un "hack" et serait plus efficace à la fois dans l'espace et dans le temps si foo était une matrice (de caractère ()), alors

matrix(map[foo], nrow=nrow(foo), dimnames=dimnames(foo))

Les variantes de matrice et de trame de données vont à l'encontre de la limite 2 ^ 31-1 de R sur la taille du vecteur lorsqu'il y a des millions de SNP et des milliers d'échantillons.

27
Martin Morgan

Si vous êtes ouvert à l'utilisation de packages, plyr est très populaire et possède cette fonction pratique mapvalues ​​() qui fera exactement ce que vous recherchez:

foo <- mapvalues(foo, from=c("AA", "AC", "AG"), to=c("0101", "0102", "0103"))

Notez que cela fonctionne pour les types de données de toutes sortes, pas seulement pour les chaînes.

30
c.gutierrez

Voici une solution rapide

dict = list(AA = '0101', AC = '0102', AG = '0103')
foo2 = foo
for (i in 1:3){foo2 <- replace(foo2, foo2 == names(dict[i]), dict[i])}
13
Ramnath

Remarque cette réponse a commencé comme une tentative de résoudre le problème beaucoup plus simple publié dans Comment remplacer toutes les valeurs du bloc de données par un vecteur de valeurs ? . Malheureusement, cette question a été fermée en tant que double de la question réelle. Donc, je vais essayer de suggérer une solution basée sur le remplacement des niveaux de facteur dans les deux cas, ici.


Dans le cas où il n'y a qu'un vecteur (ou une colonne de bloc de données) dont les valeurs doivent être remplacées et il n'y a aucune objection à utiliser le facteur, nous pouvons contraindre le vecteur à factoriser et changer les niveaux de facteur comme obligatoire:

x <- c(1, 1, 4, 4, 5, 5, 1, 1, 2)
x <- factor(x)
x
#[1] 1 1 4 4 5 5 1 1 2
#Levels: 1 2 4 5
replacement_vec <- c("A", "T", "C", "G")
levels(x) <- replacement_vec
x
#[1] A A C C G G A A T
#Levels: A T C G

En utilisant le package forcats, cela peut être fait dans une seule ligne:

x <- c(1, 1, 4, 4, 5, 5, 1, 1, 2)
forcats::lvls_revalue(factor(x), replacement_vec)
#[1] A A C C G G A A T
#Levels: A T C G

Dans le cas où tous les valeurs de plusieurs colonnes d'une trame de données doivent être remplacées, l'approche peut être étendue.

foo <- data.frame(snp1 = c("AA", "AG", "AA", "AA"), 
                  snp2 = c("AA", "AT", "AG", "AA"), 
                  snp3 = c(NA, "GG", "GG", "GC"), 
                  stringsAsFactors=FALSE)

level_vec <- c("AA", "AC", "AG", "AT", "GC", "GG")
replacement_vec <- c("0101", "0102", "0103", "0104", "0302", "0303")
foo[] <- lapply(foo, function(x) forcats::lvls_revalue(factor(x, levels = level_vec), 
                                                       replacement_vec))
foo
#  snp1 snp2 snp3
#1 0101 0101 <NA>
#2 0103 0104 0303
#3 0101 0103 0303
#4 0101 0101 0302

Notez que level_vec Et replacement_vec Doivent avoir des longueurs égales.

Plus important encore, level_vec Devrait être complet, c'est-à-dire inclure toutes les valeurs possibles dans les colonnes affectées du bloc de données d'origine. (Utilisez unique(sort(unlist(foo))) pour vérifier). Sinon, toutes les valeurs manquantes seront contraintes à <NA>. Notez que c'est également une exigence pour réponse de Martin Morgans .

Donc, s'il n'y a que quelques valeurs différentes à remplacer, vous serez probablement mieux avec l'une des autres réponses, par exemple Ramnath's .

5
Uwe

Voici quelque chose de simple qui fera l'affaire:

key <- c('AA','AC','AG')
val <- c('0101','0102','0103')

lapply(1:3,FUN = function(i){foo[foo == key[i]] <<- val[i]})
foo

 snp1 snp2 snp3
1 0101 0101 <NA>
2 0103   AT   GG
3 0101 0103   GG
4 0101 0101   GC

lapply affichera une liste dans ce cas qui nous importe peu. Vous pouvez affecter le résultat à quelque chose si vous le souhaitez, puis le jeter. Je répète les indices ici, mais vous pouvez tout aussi facilement placer les clés/valeurs dans une liste et les parcourir directement. Notez l'utilisation de l'affectation globale avec <<-.

J'ai bricolé un moyen de le faire avec mapply mais ma première tentative n'a pas fonctionné, alors j'ai changé. Je soupçonne cependant qu'une solution avec mapply est possible.

5
joran

Utilisé la réponse de @ Ramnath ci-dessus, mais l'a fait lire (ce qui doit être remplacé et ce qui doit être remplacé) à partir d'un fichier et utiliser gsub plutôt que remplacer.

hrw <- read.csv("hgWords.txt", header=T, stringsAsFactor=FALSE, encoding="UTF-8", sep="\t") 

for (i in nrow(hrw)) 
{
document <- gsub(hrw$from[i], hrw$to[i], document, ignore.case=TRUE)
}

hgword.txt contient l'onglet suivant séparé

"from"  "to"
"AA"    "0101"
"AC"    "0102"
"AG"    "0103" 
1
Fathi Alwosaibi

Nous pouvons également utiliser dplyr::case_when

library(dplyr)

foo %>%
   mutate_all(~case_when(. == "AA" ~ "0101", 
                         . == "AC" ~ "0102", 
                         . == "AG" ~ "0103", 
                         TRUE ~ .))

#  snp1 snp2 snp3
#1 0101 0101 <NA>
#2 0103   AT   GG
#3 0101 0103   GG
#4 0101 0101   GC

Il vérifie la condition et la remplace par la valeur correspondante si la condition est TRUE. Nous pouvons ajouter plus de conditions si nécessaire et avec TRUE ~ . nous conservons les valeurs telles quelles si aucune condition n'est remplie. Si nous voulons les changer en NA à la place, nous pouvons supprimer la dernière ligne.

foo %>%
  mutate_all(~case_when(. == "AA" ~ "0101", 
                        . == "AC" ~ "0102", 
                        . == "AG" ~ "0103"))

#  snp1 snp2 snp3
#1 0101 0101 <NA>
#2 0103 <NA> <NA>
#3 0101 0103 <NA>
#4 0101 0101 <NA>

Cela changera les valeurs en NA si aucune des conditions ci-dessus n'est satisfaite.


Une autre option utilisant uniquement la base R est de créer un cadre de données lookup avec des valeurs anciennes et nouvelles, unlist le cadre de données, match les avec des valeurs anciennes, obtenir les nouvelles valeurs correspondantes et remplacer .

lookup <- data.frame(old_val = c("AA", "AC", "AG"), 
                     new_val = c("0101", "0102", "0103"))

foo[] <- lookup$new_val[match(unlist(foo), lookup$old_val)]
1
Ronak Shah

Comme cela fait quelques années que la dernière réponse, et une nouvelle question est apparue ce soir sur ce sujet et un modérateur l'a clôturée, je vais l'ajouter ici. L'affiche possède un grand bloc de données contenant 0, 1 et 2 et souhaite les changer respectivement en AA, AB et BB.

Utilisez plyr:

> df <- data.frame(matrix(sample(c(NA, c("0","1","2")), 100, replace = TRUE), 10))
> df
     X1   X2   X3 X4   X5   X6   X7   X8   X9  X10
1     1    2 <NA>  2    1    2    0    2    0    2
2     0    2    1  1    2    1    1    0    0    1
3     1    0    2  2    1    0 <NA>    0    1 <NA>
4     1    2 <NA>  2    2    2    1    1    0    1
... to 10th row

> df[] <- lapply(df, as.character)

Créez une fonction sur le bloc de données à l'aide de revalue pour remplacer plusieurs termes:

> library(plyr)
> apply(df, 2, function(x) {x <- revalue(x, c("0"="AA","1"="AB","2"="BB")); x})
      X1   X2   X3   X4   X5   X6   X7   X8   X9   X10 
 [1,] "AB" "BB" NA   "BB" "AB" "BB" "AA" "BB" "AA" "BB"
 [2,] "AA" "BB" "AB" "AB" "BB" "AB" "AB" "AA" "AA" "AB"
 [3,] "AB" "AA" "BB" "BB" "AB" "AA" NA   "AA" "AB" NA  
 [4,] "AB" "BB" NA   "BB" "BB" "BB" "AB" "AB" "AA" "AB"
... and so on
1
mysteRious

Utilisation de dplyr :: recode:

library(dplyr)

mutate_all(foo, funs(recode(., "AA" = "0101", "AC" = "0102", "AG" = "0103",
                            .default = NA_character_)))

#   snp1 snp2 snp3
# 1 0101 0101 <NA>
# 2 0103 <NA> <NA>
# 3 0101 0103 <NA>
# 4 0101 0101 <NA>
0
zx8754