valor - seleccionar columnas en r
Remodelación de ancho a largo con columnas de múltiples valores (5)
Esta pregunta ya tiene una respuesta aquí:
Necesito cambiar la forma de mi tabla ancha en formato largo, pero manteniendo varios campos para cada registro, por ejemplo:
dw <- read.table(header=T, text=''
sbj f1.avg f1.sd f2.avg f2.sd blabla
A 10 6 50 10 bA
B 12 5 70 11 bB
C 20 7 20 8 bC
D 22 8 22 9 bD
'')
# Now I want to melt this table, keeping both AVG and SD as separate fields for each measurement, to get something like this:
# sbj var avg sd blabla
# A f1 10 6 bA
# A f2 50 10 bA
# B f1 12 5 bB
# B f2 70 11 bB
# C f1 20 7 bC
# C f2 20 8 bC
# D f1 22 8 bD
# D f2 22 9 bD
Tengo un conocimiento básico del uso de melt
y reshape
, pero no es obvio para mí cómo aplicar este remodelado en mi caso. Agradecería cualquier sugerencia o señalaría otra publicación de SO si ya se hubiera pedido algo similar.
Esto parece hacer lo que quieres, excepto que la f
se elimina de los elementos a time
.
reshape(dw, idvar = "sbj", varying = list(c(2,4),c(3,5)), v.names = c("ave", "sd"), direction = "long")
sbj blabla time ave sd
A.1 A bA 1 10 6
B.1 B bB 1 12 5
C.1 C bC 1 20 7
D.1 D bD 1 22 8
A.2 A bA 2 50 10
B.2 B bB 2 70 11
C.2 C bC 2 20 8
D.2 D bD 2 22 9
Otra opción usando el nuevo paquete tidyr
Hadley.
library(tidyr)
library(dplyr)
dw <- read.table(header=T, text=''
sbj f1.avg f1.sd f2.avg f2.sd blabla
A 10 6 50 10 bA
B 12 5 70 11 bB
C 20 7 20 8 bC
D 22 8 22 9 bD
'')
dw %>%
gather(v, value, f1.avg:f2.sd) %>%
separate(v, c("var", "col")) %>%
arrange(sbj) %>%
spread(col, value)
Para agregar a las opciones disponibles aquí, también puede considerar merged.stack
de mi paquete "splitstackshape":
library(splitstackshape)
merged.stack(dw, var.stubs = c("avg", "sd"), sep = "var.stubs", atStart = FALSE)
# sbj blabla .time_1 avg sd
# 1: A bA f1. 10 6
# 2: A bA f2. 50 10
# 3: B bB f1. 12 5
# 4: B bB f2. 70 11
# 5: C bC f1. 20 7
# 6: C bC f2. 20 8
# 7: D bD f1. 22 8
# 8: D bD f2. 22 9
También puedes hacer un poco más de limpieza en la variable ".time_1"
, como esta.
merged.stack(dw, var.stubs = c("avg", "sd"),
sep = "var.stubs", atStart = FALSE)[, .time_1 := sub(
".", "", .time_1, fixed = TRUE)][]
# sbj blabla .time_1 avg sd
# 1: A bA f1 10 6
# 2: A bA f2 50 10
# 3: B bB f1 12 5
# 4: B bB f2 70 11
# 5: C bC f1 20 7
# 6: C bC f2 20 8
# 7: D bD f1 22 8
# 8: D bD f2 22 9
atStart = FALSE
el uso del argumento atStart = FALSE
. Esto se debe a que sus nombres están en un poco de un orden diferente al que parecen las funciones relacionadas con la reforma. En general, se espera que el "stub" venga primero, y luego los "tiempos", como este:
dw2 <- dw
setnames(dw2, gsub("(.*)//.(.*)", "//2.//1", names(dw2)))
names(dw2)
# [1] "sbj" "avg.f1" "sd.f1" "avg.f2" "sd.f2" "blabla"
Si los nombres estaban en ese formato, tanto la merged.stack
base R como la merged.stack
benefician de una sintaxis más directa:
merged.stack(dw2, var.stubs = c("avg", "sd"), sep = ".")
reshape(dw2, idvar = c("sbj", "blabla"), varying = 2:5,
sep = ".", direction = "long")
melt
desde la versión> = 1.9.6 de data.table
, hace esto especificando el índice de la columna en measure.vars
como una list
.
melt(setDT(dw), measure.vars=list(c(2,4), c(3,5)),
variable.name=''var'', value.name=c(''avg'', ''sd''))[,
var:= paste0(''f'',var)][order(sbj)]
# sbj blabla var avg sd
#1: A bA f1 10 6
#2: A bA f2 50 10
#3: B bB f1 12 5
#4: B bB f2 70 11
#5: C bC f1 20 7
#6: C bC f2 20 8
#7: D bD f1 22 8
#8: D bD f2 22 9
O podrías usar la nueva función de patterns
:
melt(setDT(dw),
measure = patterns("avg", "sd"),
variable.name = ''var'', value.name = c(''avg'', ''sd''))
# sbj blabla var avg sd
# 1: A bA 1 10 6
# 2: B bB 1 12 5
# 3: C bC 1 20 7
# 4: D bD 1 22 8
# 5: A bA 2 50 10
# 6: B bB 2 70 11
# 7: C bC 2 20 8
# 8: D bD 2 22 9
reshape
hace con los argumentos apropiados.
lista varying
las columnas que existen en el formato ancho, pero se dividen en varias filas en el formato largo. v.names
es el formato largo equivalente. Entre los dos, se crea un mapeo.
De ?reshape
:
Además, no se intenta adivinar si v.names se da explícitamente. Observe que el orden de las variables en la variación es como x.1, y.1, x.2, y.2.
Dados estos argumentos varying
y v.names
, reshape
es lo suficientemente inteligente como para ver que he especificado que el índice está antes del punto aquí (es decir, orden 1.x, 1.y, 2.x, 2.y). Tenga en cuenta que los datos originales tienen las columnas en este orden, por lo que podemos especificar varying=2:5
para estos datos de ejemplo, pero eso no es seguro en general.
Dados los valores de times
y v.names
, reshape
divide las columnas varying
en a .
para crear las columnas en la salida.
times
especifica los valores que se utilizarán en la columna var
creada, y v.names
se pegan en estos valores para obtener nombres de columna en el formato ancho para la asignación al resultado.
Finalmente, idvar
se especifica para ser la columna sbj
, que identifica registros individuales en el formato ancho (gracias @thelatemail).
reshape(dw, direction=''long'',
varying=c(''f1.avg'', ''f1.sd'', ''f2.avg'', ''f2.sd''),
timevar=''var'',
times=c(''f1'', ''f2''),
v.names=c(''avg'', ''sd''),
idvar=''sbj'')
## sbj blabla var avg sd
## A.f1 A bA f1 10 6
## B.f1 B bB f1 12 5
## C.f1 C bC f1 20 7
## D.f1 D bD f1 22 8
## A.f2 A bA f2 50 10
## B.f2 B bB f2 70 11
## C.f2 C bC f2 20 8
## D.f2 D bD f2 22 9