Transponer / remodelar el dataframe sin “timevar” de formato largo a ancho

Tengo un dataframe que sigue el patrón a continuación:

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

Y me gustaría llegar debajo (no me importa si puedo hacer que las columnas se llamen de esta manera, solo 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 familiarizado con el paquete reshape / reshape2, y he pasado por varios bashs para intentar que esto funcione, pero hasta ahora he fallado.

Cuando pruebo dcast(dataframe, Name ~ MedName, value.var='MedName') acabo de obtener un grupo de columnas que son banderas de los nombres de los medicamentos (los valores que se transponen son 1 o 0) ejemplo:

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

También probé un dcast(dataset, Name ~ variable) después de fundir el conjunto de datos, sin embargo, esto simplemente escupe lo siguiente (solo cuenta cuántos medicamentos tiene cada persona):

  Name MedName Name1 3 name2 2 

Finalmente, traté de fundir los datos y luego remodelar usando idvar="Name" timevar="variable" (de los cuales todos simplemente son Mednames), sin embargo, esto no parece construido para mi problema, ya que si hay múltiples coincidencias con el idvar, la remodelación simplemente toma el primer MedName e ignora el rest.

¿Alguien sabe cómo hacer esto con 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!

Suponiendo que sus datos están en el dataset objeto:

 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  

Siempre puedes generar un timevar único antes de usar reshape . Aquí utilizo ave para aplicar la función seq_along ‘along’ 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  

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

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

lo que da:

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

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 usando los paquetes dplyr y tidyr :

 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 

Esto parece ser realmente un problema bastante común, así 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 

Como “data.table” se carga junto con “splitstackshape”, tiene acceso a dcast.data.table , por lo que puede continuar 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 una sequence(.N) por los grupos identificados para crear la columna “tiempo”.

La solución de @ thelatemail es similar a esta. Cuando genero la variable de tiempo, utilizo rle en caso de que no esté trabajando de forma interactiva y la variable de Name debe 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 

Aquí hay un camino más corto, aprovechando la forma en que se unlist ofertas 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 #     # 1 name1 atenolol 25mg aspirin 81mg sildenafil 100mg # 2 name2 atenolol 50mg enalapril 20mg