さまざまな日付範囲内で繰り返し測定されたワイドフォーマットのデータフレームがあります。私の例では、3つの異なる期間があり、それぞれに対応する値があります。例えば。最初の測定(Value1
)は、DateRange1Start
から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
DateRangeXStart列とDateRangeXEnd列がグループ化されるように、データを長い形式に再形成しようとしています。したがって、元のテーブルの1行は新しいテーブルの3行になります。
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
reshape2
/melt
/recast
/tidyr
を使用してこれを行う方法があるはずですが、方法を理解できないようですこの特定の方法で、メジャー変数の複数のセットを単一の値列のセットにマップします。
バージョンtidyrパッケージの関数pivot_longer()
を使用すると、バージョン1.0.0。
これはgather()
よりも以前のspread()
のtidyr戦略よりも優れています(@AndrewMacDonaldによる回答を参照)。属性が削除されなくなったためです(日付は日付のままで、数値は例のままの数値のままです)未満)。
_library("tidyr")
library("magrittr")
a <- structure(list(ID = 1L,
DateRange1Start = structure(7305, class = "Date"),
DateRange1End = structure(7307, class = "Date"),
Value1 = 4.4,
DateRange2Start = structure(7793, class = "Date"),
DateRange2End = structure(7856, class = "Date"),
Value2 = 6.2,
DateRange3Start = structure(9255, class = "Date"),
DateRange3End = structure(9653, class = "Date"),
Value3 = 3.3),
row.names = c(NA, -1L), class = c("tbl_df", "tbl", "data.frame"))
_
pivot_longer()
(対応:pivot_wider()
)はgather()
と同様に機能します。ただし、複数の値列などの追加機能を提供します。値列が1つだけの場合、ワイドデータセットのすべてのcolnameは、_names_to
_で指定された名前を持つ1つの長い列に入ります。複数の値の列の場合、_names_to
_は複数の新しい名前を受け取る場合があります。
これは、すべての列名が_Start_1
_、_End_1
_、_Start_2
_などの特定のパターンに従う場合に最も簡単です。したがって、最初のステップで列の名前を変更しました。
_(names(a) <- sub("(\\d)(\\w*)", "\\2_\\1", names(a)))
#> [1] "ID" "DateRangeStart_1" "DateRangeEnd_1"
#> [4] "Value_1" "DateRangeStart_2" "DateRangeEnd_2"
#> [7] "Value_2" "DateRangeStart_3" "DateRangeEnd_3"
#> [10] "Value_3"
pivot_longer(a,
cols = -ID,
names_to = c(".value", "group"),
# names_prefix = "DateRange",
names_sep = "_")
#> # A tibble: 3 x 5
#> ID group DateRangeEnd DateRangeStart Value
#> <int> <chr> <date> <date> <dbl>
#> 1 1 1 1990-01-03 1990-01-01 4.4
#> 2 1 2 1991-07-06 1991-05-04 6.2
#> 3 1 3 1996-06-06 1995-05-05 3.3
_
または、より細かな制御を提供するpivot specを使用して形状を変更することもできます(以下のリンクを参照)。
_spec <- a %>%
build_longer_spec(cols = -ID) %>%
dplyr::transmute(.name = .name,
group = readr::parse_number(name),
.value = stringr::str_extract(name, "Start|End|Value"))
pivot_longer(a, spec = spec)
_
2019-03-26に reprexパッケージ (v0.2.1)によって作成されました
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
(Joshの提案に従ってv.namesを追加しました。)
data.table
のmelt
関数は、複数の列に溶けることができます。それを使用して、簡単に行うことができます:
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
または、列の位置で3つのメジャー列セットを参照することもできます。
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"))
これは、tidyr
を使用した問題へのアプローチです。これは、関数extract_numeric()
の興味深い使用例です。これを使用して、列名からグループを引き出しました
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
2つの追加オプション(コードの動作をわかりやすくするために複数の行を含むサンプルデータフレームを使用):
1)ベース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)
それは与える:
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)と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)), .)
)とsjmisc
- package:
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]
グループ/時間列も必要な場合は、上記のアプローチを次のように調整できます。
1)ベース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)
それは与える:
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)と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)), .)
)とsjmisc
- package:
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'))
使用データ:
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)
リサイクルの使用:
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)]))
特別なことは何も必要ありません。 base R
関数が行います。
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)