Transponer/remodelar el dataframe sin "timevar" de formato largo a ancho
melt in r (6)
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 <NA>
Tengo un marco de datos 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 intentos 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 resto.
¿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!
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".
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 <NA>
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
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 <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 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
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
# <chr> <chr> <chr> <chr>
# 1 name1 atenolol 25mg aspirin 81mg sildenafil 100mg
# 2 name2 atenolol 50mg enalapril 20mg <NA>