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?
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.
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.
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])}
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 .
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.
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"
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)]
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
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>