2011-09-25 12 views
17

Tengo un gran data.frame de datos de caracteres que quiero convertir en base a lo que comúnmente se llama un diccionario en otros idiomas.Estilo de diccionario reemplazar varios elementos

Voy actualmente en ello de esta manera:

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") 

Esto funciona bien, pero obviamente no es bastante y parece tonto a repetir la declaración replace cada vez que quiera sustituir un elemento de los datos. marco.

¿Hay una manera mejor de hacerlo ya que tengo un diccionario de aproximadamente 25 pares de clave/valor?

+1

¿Es su diccionario una lista R? – Mark

+0

Actualmente no, pero sería fácil convertirlo en uno. – Stedy

+0

Quizás estas preguntas podrían ser útiles: [Case Statement Equivalent] (http://stackoverflow.com/q/4622060/168747), [Cómo agregar una columna en un 'data.frame'] (http: // stackoverflow. com/q/4562547/168747), [Limpieza de datos en hojas de Excel] (http://stackoverflow.com/q/7374314/168747). – Marek

Respuesta

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

suponiendo que map cubre todos los casos en los foo. Esto se sentiría menos como un 'piratear' y ser más eficientes en espacio y tiempo si foo eran una matriz (de carácter()), entonces

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

Tanto marco de la matriz y los datos de las variantes ignorara el R de 2^31-1 límite en el tamaño del vector cuando hay millones de SNP y miles de muestras.

3

Aquí es algo simple que hará el trabajo:

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 dará salida a una lista en este caso que no lo hacemos realmente importa. Podría asignar el resultado a algo si lo desea y luego simplemente descartarlo. Estoy iterando sobre los índices aquí, pero también podría colocar fácilmente la clave/val en una lista e iterar sobre ellos directamente. Tenga en cuenta el uso de la asignación global con <<-.

He jugado con una forma de hacer esto con mapply pero mi primer intento no funcionó, así que cambié. Sospecho que es posible una solución con mapply.

+0

No aconsejaría el uso del operador de asignación global '<< -'. – Ramnath

+1

@Ramnath De acuerdo, '<< -' puede ser arriesgado, pero no es intrínsecamente malo. – joran

11

Aquí es una solución rápida

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

¿alguien puede explicar el voto en silencio? – Ramnath

+3

Me gusta esta respuesta porque mantiene juntas las claves y los valores. Tener las claves y los valores en vectores de caracteres separados significa que si obtiene el orden de uno de los vectores incorrecto, su diccionario etiqueta incorrectamente todas las entradas incorrectamente ordenadas. – mgriebe

+1

La única diferencia que sugiero es usar la notación vectorizada de R en la 3ra línea, por ejemplo: sapply (1: 3, función (i) reemplazar (foo2, foo2 == nombres (dict [i]), dict [i])) –

17

Si usted está abierto a la utilización de paquetes, plyr es muy popular y tiene este mapvalues() práctica función que va a hacer justo lo que está buscando:

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

Tenga en cuenta que funciona para tipos de datos de todo tipo, no solo cadenas.

+1

Desafortunadamente, esto arroja un _Error en plyr :: mapvalues ​​(foo, from = c ("AA", "AC", "AG"), a = c ("0101",: 'x' debe ser un vector atómico. _ Esto también está documentado en '? Mapvalues'. – Uwe

1

Se utilizó la respuesta de @ Ramnath anterior, pero se hizo leer (qué reemplazar y qué reemplazar) de un archivo y usar gsub en lugar de reemplazar.

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 contiene la siguiente ficha separada

"from" "to" 
"AA" "0101" 
"AC" "0102" 
"AG" "0103" 
4

Nota esta respuesta comenzó como un intento de resolver el problema mucho más simple publicado en How to replace all values in data frame with a vector of values?. Lamentablemente, esta pregunta se cerró como duplicado de la pregunta real. Entonces, intentaré sugerir una solución basada en reemplazar los niveles de factor para ambos casos, aquí.


En caso de que sólo es un vector (o columna marco de uno de datos) cuyos valores necesitan ser reemplazados y no hay objeciones para el uso de factores podemos coaccionar el vector de factor y cambiar los niveles de los factores según sea necesario:

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 

Usando el paquete forcats esto se puede hacer en una sola línea:

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 

En el caso todos los valores de varias columnas de un marco de datos necesitan ser reemplazados, el enfoque se puede ampliar.

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 

Tenga en cuenta que level_vecreplacement_vec y debe tener la misma longitud.

Más importante aún, level_vec debe ser completo, es decir, incluir todos los valores posibles en las columnas afectadas del marco de datos original. (Use unique(sort(unlist(foo))) para verificar). De lo contrario, cualquier valor faltante se forzará al <NA>. Tenga en cuenta que esto también es un requisito para Martin Morgans's answer.

Por lo tanto, si solo hay unos pocos valores diferentes que reemplazar, probablemente estará mejor con una de las otras respuestas, por ejemplo, Ramnath's.

Cuestiones relacionadas