2012-07-04 18 views
11

tengo una trama de datos que sigue el siguiente largo Patrón:transposición/remodelar trama de datos sin "timevar" de largo a gran formato

Name   MedName 
    Name1 atenolol 25mg 
    Name1  aspirin 81mg 
    Name1 sildenafil 100mg 
    Name2 atenolol 50mg 
    Name2 enalapril 20mg 

Y quisiera conseguir a continuación (no me importa si puedo conseguir las columnas a ser nombrados de esta manera, sólo quiero los datos en este formato):

Name medication1 medication2  medication3 
    Name1 atenolol 25mg aspirin 81mg sildenafil 100mg 
    Name2 atenolol 50mg enalapril 20mg    NA 

a través de este mismo sitio me he vuelto familiarish con el paquete de remodelar/reshape2, y se fue a través de varios intentos para tratar de conseguir este para trabajar, pero hasta ahora han fallado.

Cuando intento dcast(dataframe, Name ~ MedName, value.var='MedName') acabo de conseguir un montón de columnas que son banderas de los nombres de los medicamentos (los valores que consiguen transponerse son 1 o 0) ejemplo:

Name atenolol 25mg aspirin 81mg 
Name1    1    1 
Name2    0    0 

También probamos un dcast(dataset, Name ~ variable) después de que derritió la conjunto de datos, sin embargo esto sólo escupe la siguiente (solo cuenta el número de medicamentos que cada persona tiene):

Name MedName 
Name1  3 
name2  2 

Por último, he tratado de fundir los datos y luego formar de nuevo usando idvar="Name"timevar="variable" (de los cuales son todos sólo Mednam es), sin embargo, esto no parece construido para mi problema, ya que si hay múltiples coincidencias con el idvar, la nueva forma solo toma el primer MedName e ignora el resto.

¿Alguien sabe cómo hacer esto mediante la remodelación u otra función R? Me doy cuenta de que probablemente haya una manera de hacerlo de una manera más desordenada con algunos bucles y condicionales para básicamente dividir y volver a pegar los datos, pero esperaba que hubiera una solución más simple. Muchas gracias!

Respuesta

13

Suponiendo que los datos están en el objeto dataset

library(plyr) 
## Add a medication index 
data_with_index <- ddply(dataset, .(Name), mutate, 
         index = paste0('medication', 1:length(Name)))  
dcast(data_with_index, Name~ index, value.var = 'MedName') 

## Name medication1 medication2  medication3 
## 1 Name1 atenolol 25mg aspirin 81mg sildenafil 100mg 
## 2 Name2 atenolol 50mg enalapril 20mg    <NA> 
11

Siempre se puede generar una única timevar antes de usar reshape. Aquí utilizo ave para aplicar la función seq_along 'along' a cada "Nombre".

test <- data.frame(
Name=c(rep("name1",3),rep("name2",2)), 
MedName=c("atenolol 25mg","aspirin 81mg","sildenafil 100mg", 
      "atenolol 50mg","enalapril 20mg") 
) 

# generate the 'timevar' 
test$uniqid <- with(test, ave(as.character(Name), Name, FUN = seq_along)) 

# reshape! 
reshape(test, idvar = "Name", timevar = "uniqid", direction = "wide") 

Resultado:

Name  MedName.1  MedName.2  MedName.3 
1 name1 atenolol 25mg aspirin 81mg sildenafil 100mg 
4 name2 atenolol 50mg enalapril 20mg    <NA> 
+0

Gracias por la ayuda, esto funcionó. Mi única preocupación sobre las columnas, es que en mi conjunto de datos actual tengo un número siempre cambiante y nombres de medicamentos, por lo que declarar el MedName = c (Todos los nombres) probablemente sería un poco demasiado, pero agradezco la ayuda, y lo haré. probablemente use este método en otros problemas. – Hotamd6

+0

@ Hotamd6 - no es necesario especificar manualmente todos los nombres - puede hacer un buscar y reemplazar en los nombres de los conjuntos de datos como 'gsub (" MedName. "," Medicamento ", nombres (reshapedtestdata), fixed = TRUE)' para obtener el mismo resultado que @mnel anterior. – thelatemail

3

@ solución de thelatemail es similar a éste. Cuando genero la variable de tiempo, utilizo rle en caso de que no esté trabajando de forma interactiva y la variable Name tenga que ser dinámica.

# start with your example data 
x <- 
    data.frame(
     Name=c(rep("name1",3),rep("name2",2)), 
     MedName=c("atenolol 25mg","aspirin 81mg","sildenafil 100mg", 
      "atenolol 50mg","enalapril 20mg") 
    ) 

# pick the id variable 
id <- 'Name' 

# sort the data.frame by that variable 
x <- x[ order(x[ , id ]) , ] 

# construct a `time` variable on the fly 
x$time <- unlist(lapply(rle(as.character(x[ , id ]))$lengths , seq_len)) 

# `reshape` uses that new `time` column by default 
y <- reshape(x , idvar = id , direction = 'wide') 

# done 
y 
+0

No estoy seguro de entender su comentario sobre el uso de 'rle' cuando la variable" Nombre "debe ser dinámica. ¿La solución de @ thelatemail tampoco permitiría tal flexibilidad (y sin tener que ordenar primero los datos)? – A5C1D2H2I1M1N2O1R2T1

+0

@AnandaMahto tal vez tengas razón ... supongo que podrías usar 'id <- 'Name'' y luego' as.character (get (id)) 'en esa segunda línea y luego el resto es dinámico. –

7

Ésta parece ser en realidad un problema bastante común, por lo que he incluido una función llamada getanID en mi paquete "splitstackshape".

Esto es lo que hace:

library(splitstackshape) 
getanID(test, "Name") 
#  Name   MedName .id 
# 1: name1 atenolol 25mg 1 
# 2: name1  aspirin 81mg 2 
# 3: name1 sildenafil 100mg 3 
# 4: name2 atenolol 50mg 1 
# 5: name2 enalapril 20mg 2 

Desde "data.table" se carga junto con "splitstackshape", se tiene acceso a dcast.data.table, para que pueda proceder como con el ejemplo de @ mnel.

dcast.data.table(getanID(test, "Name"), Name ~ .id, value.var = "MedName") 
#  Name    1    2    3 
# 1: name1 atenolol 25mg aspirin 81mg sildenafil 100mg 
# 2: name2 atenolol 50mg enalapril 20mg    NA 

La función implementa esencialmente un sequence(.N) por los grupos identificados para crear la columna de "tiempo".

6

Con el paquete data.table, esto fácilmente podrían resolverse con la nueva función rowid:

library(data.table) 
dcast(setDT(d1), 
     Name ~ rowid(Name, prefix = "medication"), 
     value.var = "MedName") 

que da:

Name medication1  medication2  medication3 
1 Name1 atenolol 25mg aspirin 81mg sildenafil 100mg 
2 Name2 atenolol 50mg enalapril 20mg    <NA> 

Otro método (comúnmente utilizado antes de la versión 1.9. 7):

dcast(setDT(d1)[, rn := 1:.N, by = Name], 
     Name ~ paste0("medication",rn), 
     value.var = "MedName") 

dando el mismo resultado


Un enfoque similar, pero ahora con el dplyr y tidyr paquetes:

library(dplyr) 
library(tidyr) 
d1 %>% 
    group_by(Name) %>% 
    mutate(rn = paste0("medication",row_number())) %>% 
    spread(rn, MedName) 

lo que da:

Source: local data frame [2 x 4] 
Groups: Name [2] 

    Name medication1 medication2  medication3 
    (fctr)   (chr)   (chr)   (chr) 
1 Name1 atenolol 25mg aspirin 81mg sildenafil 100mg 
2 Name2 atenolol 50mg enalapril 20mg    NA 
0

Aquí está una manera más corto, aprovechando la manera unlist trata de nombres:

library(dplyr) 
df1 %>% group_by(Name) %>% do(as_tibble(t(unlist(.[2])))) 
# # A tibble: 2 x 4 
# # Groups: Name [2] 
#  Name  MedName1  MedName2   MedName3 
#  <chr>   <chr>   <chr>   <chr> 
# 1 name1 atenolol 25mg aspirin 81mg sildenafil 100mg 
# 2 name2 atenolol 50mg enalapril 20mg    <NA> 
Cuestiones relacionadas