Przekształcanie wielu zestawów kolumn pomiarowych (szeroki format) w pojedyncze kolumny (długi format)
Mam ramkę danych w szerokim formacie, z powtarzanymi pomiarami w różnych zakresach dat. W moim przykładzie są trzy różne okresy, wszystkie z odpowiadającymi im wartościami. Np. pierwszy pomiar (Value1
) mierzony był w okresie od DateRange1Start
do 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
Chcę przekształcić dane do długiego formatu, tak aby kolumny DateRangeXStart i DateRangeXEnd były pogrupowane,. Tak więc to, co było 1 wierszem w oryginalnej tabeli, staje się 3 wierszami w nowej Tabela:
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
Wiem, że musi być sposób, aby to zrobić z reshape2
/melt
/recast
/tidyr
, ale nie mogę zrozumieć, jak mapować wiele zestawów zmiennych miary na pojedyncze zestawy kolumn wartości w ten konkretny sposób.
6 answers
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
(Dodano imiona v. zgodnie z sugestią Josha.)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-17 21:55:30
data.table
'funkcja S melt
może stopić się na wiele kolumn. Używając tego, możemy po prostu zrobić:
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
Alternatywnie można również odwoływać się do trzech zestawów kolumn miary według pozycji kolumny:
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"))
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-01-25 21:48:26
Oto podejście do problemu za pomocą tidyr
. Jest to interesujący przypadek użycia funkcji extract_numeric()
, której użyłem do wyciągnięcia grupy z nazw kolumn
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
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-23 20:05:20
Dwie dodatkowe opcje (z przykładową ramką danych z więcej niż jednym wierszem, aby lepiej pokazać działanie kodu):
1) z podstawą 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)
Co daje:
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) z 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)), .)
3) z sjmisc
-pakiet:
library(sjmisc)
to_long(d, keys = 'group',
values = c('DateRangeStart','DateRangeEnd','Value'),
c('DateRange1Start','DateRange2Start','DateRange3Start'),
c('DateRange1End','DateRange2End','DateRange3End'),
c('Value1','Value2','Value3'))[,-2]
Jeśli chcesz również kolumny grupy / czasu, możesz dostosować powyższe podejścia do:
1) z podstawą 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)
Które daje:
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) z 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)), .)
3) z sjmisc
-pakiet:
library(sjmisc)
to_long(d, keys = 'group', recode.key = TRUE,
values = c('DateRangeStart','DateRangeEnd','Value'),
c('DateRange1Start','DateRange2Start','DateRange3Start'),
c('DateRange1End','DateRange2End','DateRange3End'),
c('Value1','Value2','Value3'))
Użyte dane:
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)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-29 22:05:46
Wykorzystanie recyklingu:
data.frame(ID = d[, 1],
DateRangeStart = unlist(d[, -1][, c(TRUE, FALSE, FALSE)]),
DateRangeEnd = unlist(d[, -1][, c(FALSE, TRUE, FALSE)]),
Value = unlist(d[, -1][, c(FALSE, FALSE, TRUE)]))
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-10 11:00:01
Nie potrzebujesz niczego wyszukanego; funkcje bazowe R
wystarczą.
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)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-17 20:29:28