that part multiple gather columns are r reshape tidyr reshape2 r-faq

part - Remodelación de múltiples conjuntos de columnas de medición(formato ancho) en columnas individuales(formato largo)



tidyr::gather (5)

Tengo un marco de datos en un formato amplio, con mediciones repetidas tomadas dentro de diferentes intervalos de fechas. En mi ejemplo, hay tres períodos diferentes, todos con sus valores correspondientes. Por ejemplo, la primera medición ( Value1 ) se midió en el período de DateRange1Start a DateRange1End :

ID DateRange1Start DateRange1End Value1 DateRange2Start DateRange2End Value2 DateRange3Start DateRange3End Value3 1 1/1/90 3/1/90 4.4 4/5/91 6/7/91 6.2 5/5/95 6/6/96 3.3

Estoy buscando reformar los datos a un formato largo para que las columnas DateRangeXStart y DateRangeXEnd estén agrupadas. Por lo tanto, lo que era 1 fila en la tabla original se convierte en 3 filas en la nueva tabla:

ID DateRangeStart DateRangeEnd Value 1 1/1/90 3/1/90 4.4 1 4/5/91 6/7/91 6.2 1 5/5/95 6/6/96 3.3

Sé que debe haber una manera de hacer esto con reshape2 / melt / tidyr / tidyr , pero parece que no puedo resolver cómo mapear los múltiples conjuntos de variables de medida en conjuntos individuales de columnas de valores de esta manera particular.


No necesitas nada lujoso; las funciones R base funcionarán.

a <- read.table(textConnection(" ID DateRange1Start DateRange1End Value1 DateRange2Start DateRange2End Value2 DateRange3Start DateRange3End Value3 1 1/1/90 3/1/90 4.4 4/5/91 6/7/91 6.2 5/5/95 6/6/96 3.3 "),header=TRUE) b1 <- a[,c(1:4)]; b2 <- a[,c(1,5:7)]; b3 <- a[,c(1,8:10)] colnames(b1) <- colnames(b2) <- colnames(b3) <- c("ID","DateRangeStart","DateRangeEnd","Value") b <- rbind(b1,b2,b3)


reshape(dat, idvar="ID", direction="long", varying=list(Start=c(2,5,8), End=c(3,6,9), Value=c(4,7,10)), v.names = c("DateRangeStart", "DateRangeEnd", "Value") ) #------------- ID time DateRangeStart DateRangeEnd Value 1.1 1 1 1/1/90 3/1/90 4.4 1.2 1 2 4/5/91 6/7/91 6.2 1.3 1 3 5/5/95 6/6/96 3.3

(Agregó los v.names según la sugerencia de Josh).


Aquí hay un acercamiento al problema usando tidyr . Este es un caso de uso interesante para su función extract_numeric() , que utilicé para sacar el grupo de los nombres de las columnas

library(dplyr) library(tidyr) a <- read.table(textConnection(" ID DateRange1Start DateRange1End Value1 DateRange2Start DateRange2End Value2 DateRange3Start DateRange3End Value3 1 1/1/90 3/1/90 4.4 4/5/91 6/7/91 6.2 5/5/95 6/6/96 3.3 "),header=TRUE) a %>% gather(variable,value,-ID) %>% mutate(group = extract_numeric(variable)) %>% mutate(variable = gsub("//d","",x = variable)) %>% spread(variable,value) ID group DateRangeEnd DateRangeStart Value 1 1 1 3/1/90 1/1/90 4.4 2 1 2 6/7/91 4/5/91 6.2 3 1 3 6/6/96 5/5/95 3.3


La función de melt data.table puede fundirse en múltiples columnas. Usando eso, podemos simplemente hacer:

require(data.table) melt(setDT(dat), id=1L, measure=patterns("Start$", "End$", "^Value"), value.name=c("DateRangeStart", "DateRangeEnd", "Value")) # ID variable DateRangeStart DateRangeEnd Value # 1: 1 1 1/1/90 3/1/90 4.4 # 2: 1 2 4/5/91 6/7/91 6.2 # 3: 1 3 5/5/95 6/6/96 3.3

Alternativamente, también puede hacer referencia a los tres conjuntos de columnas de medidas por la posición de la columna:

melt(setDT(dat), id = 1L, measure = list(c(2,5,8), c(3,6,9), c(4,7,10)), value.name = c("DateRangeStart", "DateRangeEnd", "Value"))


Dos opciones adicionales (con un marco de datos de ejemplo con más de una fila para mostrar mejor el funcionamiento del código):

1) con base R:

l <- lapply(split.default(d[-1], cumsum(grepl(''Start$'', names(d)[-1]))), setNames, c(''DateRangeStart'',''DateRangeEnd'',''Value'')) data.frame(ID = d[,1], do.call(rbind, l), row.names = NULL)

lo que da:

ID DateRangeStart DateRangeEnd Value 1 1 1/1/90 3/1/90 4.4 2 2 1/2/90 3/2/90 6.1 3 1 4/5/91 6/7/91 6.2 4 2 4/6/91 6/8/91 3.2 5 1 5/5/95 6/6/96 3.3 6 2 5/5/97 6/6/98 1.3

2) con el tidyverse :

library(dplyr) library(purrr) split.default(d[-1], cumsum(grepl(''Start$'', names(d)[-1]))) %>% map_dfr(~set_names(., c(''DateRangeStart'',''DateRangeEnd'',''Value''))) %>% bind_cols(ID = rep(d$ID, nrow(.)/nrow(d)), .)

que da un resultado idéntico.

Si también desea una columna de grupo / tiempo, puede adaptar los enfoques anteriores a:

1) con base R:

l <- lapply(split.default(d[-1], cumsum(grepl(''Start$'', names(d)[-1]))), setNames, c(''DateRangeStart'',''DateRangeEnd'',''Value'')) data.frame(ID = d[,1], group = rep(seq_along(l), each = nrow(d)), do.call(rbind, l), row.names = NULL)

lo que da:

ID group DateRangeStart DateRangeEnd Value 1 1 1 1/1/90 3/1/90 4.4 2 2 1 1/2/90 3/2/90 6.1 3 1 2 4/5/91 6/7/91 6.2 4 2 2 4/6/91 6/8/91 3.2 5 1 3 5/5/95 6/6/96 3.3 6 2 3 5/5/97 6/6/98 1.3

2) con el tidyverse :

split.default(d[-1], cumsum(grepl(''Start$'', names(d)[-1]))) %>% map_dfr(~set_names(., c(''DateRangeStart'',''DateRangeEnd'',''Value''))) %>% bind_cols(ID = rep(d$ID, nrow(.)/nrow(d)), group = rep(1:(nrow(.)/nrow(d)), each = nrow(d)), .)

que da el mismo resultado

Datos usados:

d <- read.table(text = "ID DateRange1Start DateRange1End Value1 DateRange2Start DateRange2End Value2 DateRange3Start DateRange3End Value3 1 1/1/90 3/1/90 4.4 4/5/91 6/7/91 6.2 5/5/95 6/6/96 3.3 2 1/2/90 3/2/90 6.1 4/6/91 6/8/91 3.2 5/5/97 6/6/98 1.3", header = TRUE, stringsAsFactors = FALSE)