将宽数据转换为具有多个变量的长格式
Transforming wide data to long format with multiple variables
这可能有一个简单的答案,但经过几个小时的搜索后我仍然找不到它。基本上我需要将宽数据集转换为长格式数据集但具有多个变量。我的数据集结构如下所示:
df1 <- data.frame(id = c(1,2,3),
sex = c("M","F","M"),
day0s = c(21,25,15),
day1s = c(20,30,18),
day2s = c(18,18,17),
day0t = c(2,5,7),
day1t = c(3,6,5),
day2t = c(3,8,7))
df1
id sex day0s day1s day2s day0t day1t day2t
1 M 21 20 18 2 3 3
2 F 25 30 18 5 6 8
3 M 15 18 17 7 5 7
基本上3个科目每天做数学测试(s)和历史测试(t),连续3天。
我尝试使用 tidyr 的 gather 将其转换为长格式,但我不知道如何将 mt 和 ht 变量分配给同一天。我还用 day0 = 0、day1 = 1 和 day2 = 2 编写了一个新的变量日。
dfl <- df1 %>%
gather(day, value, - c(id,sex))
dfl
id sex variable value day
1 M day0s 21 0
1 M day1s 20 1
1 M day2s 18 2
1 M day0t 2 0
1 M day1t 3 1
1 M day2t 3 2
2 F day0s 25 0
2 F day1s 30 1
2 F day2s 18 2
2 F day0t 5 0
2 F day1t 6 1
2 F day2t 8 2
3 M day0s 15 0
3 M day1s 18 1
3 M day2s 17 2
3 M day0t 7 0
3 M day1t 5 1
3 M day2t 7 1
理想情况下最终应该是这样的。
id sex day s t
1 M 0 21 2
1 M 1 20 3
1 M 2 18 3
2 F 0 25 5
2 F 1 30 6
2 F 2 18 8
3 M 0 15 7
3 M 1 18 5
3 M 2 17 7
请问您对如何实现这一点有什么建议吗?
您可以在此处使用 {tidyr} 的 pivot_longer
。
如果您的实际变量命名有点不同,您可以根据您的情况调整正则表达式。 Here您可以尝试并相应地进行调整。 (请注意,在 R 中,反斜杠必须转义,因此
\d+
和 \w+
)
中的双反斜杠
通常,names_pattern
参数通过将括号内的正则表达式与 names_to
参数匹配来工作,因此此处:
(\d+)
-> 变成可变日。正则表达式 \d+
匹配 1 个或多个数字。
(\w+)
-> 变成“.value”。正则表达式 \w+
匹配 1 个或多个单词字符。感谢 r2evans for pointing out the ".value" argument that spares one further reshape. The documentation 指出 .value
“告诉 pivot_longer() 列名的那部分指定了正在测量的“值”(它将成为输出中的变量)。”虽然我没有完全理解文档解释,但结果是匹配的正则表达式映射到输出数据中的变量名。
library(dplyr)
library(tidyr)
df1 <- data.frame(id = c(1,2,3),
sex = c("M","F","M"),
day0mt = c(21,25,15),
day1mt = c(20,30,18),
day2mt = c(18,18,17),
day0ht = c(2,5,7),
day1ht = c(3,6,5),
day2ht = c(3,8,7))
df1
#> id sex day0mt day1mt day2mt day0ht day1ht day2ht
#> 1 1 M 21 20 18 2 3 3
#> 2 2 F 25 30 18 5 6 8
#> 3 3 M 15 18 17 7 5 7
df1 %>%
pivot_longer(cols = starts_with("day"),
names_pattern = "day(\d+)(\w+)",
names_to = c("day", ".value"))
#> # A tibble: 9 x 5
#> id sex day mt ht
#> <dbl> <chr> <chr> <dbl> <dbl>
#> 1 1 M 0 21 2
#> 2 1 M 1 20 3
#> 3 1 M 2 18 3
#> 4 2 F 0 25 5
#> 5 2 F 1 30 6
#> 6 2 F 2 18 8
#> 7 3 M 0 15 7
#> 8 3 M 1 18 5
#> 9 3 M 2 17 7
由 reprex package (v2.0.0)
于 2021-06-20 创建
请注意,在较新版本的 tidyr 中,gather
和 spread
已弃用并由 pivot_longer
和 pivot_wider.
取代
这是一个方法。它首先重新整形为长格式,将 day*
列分隔为日期和后缀列,然后重新整形为宽格式。
library(dplyr)
library(tidyr)
library(stringr)
df1 %>%
pivot_longer(cols = starts_with("day")) %>%
mutate(day = str_extract(name, "\d+"),
suffix = str_extract(name, "[^[:digit:]]+$")) %>%
select(-name) %>%
pivot_wider(
id_cols = -c(value, suffix),
names_from = suffix,
values_from = value
)
## A tibble: 9 x 5
# id sex day s t
# <dbl> <chr> <chr> <dbl> <dbl>
#1 1 M 0 21 2
#2 1 M 1 20 3
#3 1 M 2 18 3
#4 2 F 0 25 5
#5 2 F 1 30 6
#6 2 F 2 18 8
#7 3 M 0 15 7
#8 3 M 1 18 5
#9 3 M 2 17 7
使用 data.table
(1.14.1) 的最新开发版本,其中添加了一些很酷的新熔化功能..
使用data.table::update.dev.pkg()
安装开发版本
library(data.table)
# data.table 1.14.1 IN DEVELOPMENT built 2021-06-22 09:38:23 UTC
dcast(
melt(setDT(df1), measure.vars = measure(day, type, pattern="^day(.)(.)")),
... ~ type, value.var = "value")
id sex day s t
1: 1 M 0 21 2
2: 1 M 1 20 3
3: 1 M 2 18 3
4: 2 F 0 25 5
5: 2 F 1 30 6
6: 2 F 2 18 8
7: 3 M 0 15 7
8: 3 M 1 18 5
9: 3 M 2 17 7
这可能有一个简单的答案,但经过几个小时的搜索后我仍然找不到它。基本上我需要将宽数据集转换为长格式数据集但具有多个变量。我的数据集结构如下所示:
df1 <- data.frame(id = c(1,2,3),
sex = c("M","F","M"),
day0s = c(21,25,15),
day1s = c(20,30,18),
day2s = c(18,18,17),
day0t = c(2,5,7),
day1t = c(3,6,5),
day2t = c(3,8,7))
df1
id sex day0s day1s day2s day0t day1t day2t
1 M 21 20 18 2 3 3
2 F 25 30 18 5 6 8
3 M 15 18 17 7 5 7
基本上3个科目每天做数学测试(s)和历史测试(t),连续3天。 我尝试使用 tidyr 的 gather 将其转换为长格式,但我不知道如何将 mt 和 ht 变量分配给同一天。我还用 day0 = 0、day1 = 1 和 day2 = 2 编写了一个新的变量日。
dfl <- df1 %>%
gather(day, value, - c(id,sex))
dfl
id sex variable value day
1 M day0s 21 0
1 M day1s 20 1
1 M day2s 18 2
1 M day0t 2 0
1 M day1t 3 1
1 M day2t 3 2
2 F day0s 25 0
2 F day1s 30 1
2 F day2s 18 2
2 F day0t 5 0
2 F day1t 6 1
2 F day2t 8 2
3 M day0s 15 0
3 M day1s 18 1
3 M day2s 17 2
3 M day0t 7 0
3 M day1t 5 1
3 M day2t 7 1
理想情况下最终应该是这样的。
id sex day s t
1 M 0 21 2
1 M 1 20 3
1 M 2 18 3
2 F 0 25 5
2 F 1 30 6
2 F 2 18 8
3 M 0 15 7
3 M 1 18 5
3 M 2 17 7
请问您对如何实现这一点有什么建议吗?
您可以在此处使用 {tidyr} 的 pivot_longer
。
如果您的实际变量命名有点不同,您可以根据您的情况调整正则表达式。 Here您可以尝试并相应地进行调整。 (请注意,在 R 中,反斜杠必须转义,因此
\d+
和 \w+
)
通常,names_pattern
参数通过将括号内的正则表达式与 names_to
参数匹配来工作,因此此处:
(\d+)
-> 变成可变日。正则表达式\d+
匹配 1 个或多个数字。(\w+)
-> 变成“.value”。正则表达式\w+
匹配 1 个或多个单词字符。感谢 r2evans for pointing out the ".value" argument that spares one further reshape. The documentation 指出.value
“告诉 pivot_longer() 列名的那部分指定了正在测量的“值”(它将成为输出中的变量)。”虽然我没有完全理解文档解释,但结果是匹配的正则表达式映射到输出数据中的变量名。
library(dplyr)
library(tidyr)
df1 <- data.frame(id = c(1,2,3),
sex = c("M","F","M"),
day0mt = c(21,25,15),
day1mt = c(20,30,18),
day2mt = c(18,18,17),
day0ht = c(2,5,7),
day1ht = c(3,6,5),
day2ht = c(3,8,7))
df1
#> id sex day0mt day1mt day2mt day0ht day1ht day2ht
#> 1 1 M 21 20 18 2 3 3
#> 2 2 F 25 30 18 5 6 8
#> 3 3 M 15 18 17 7 5 7
df1 %>%
pivot_longer(cols = starts_with("day"),
names_pattern = "day(\d+)(\w+)",
names_to = c("day", ".value"))
#> # A tibble: 9 x 5
#> id sex day mt ht
#> <dbl> <chr> <chr> <dbl> <dbl>
#> 1 1 M 0 21 2
#> 2 1 M 1 20 3
#> 3 1 M 2 18 3
#> 4 2 F 0 25 5
#> 5 2 F 1 30 6
#> 6 2 F 2 18 8
#> 7 3 M 0 15 7
#> 8 3 M 1 18 5
#> 9 3 M 2 17 7
由 reprex package (v2.0.0)
于 2021-06-20 创建请注意,在较新版本的 tidyr 中,gather
和 spread
已弃用并由 pivot_longer
和 pivot_wider.
这是一个方法。它首先重新整形为长格式,将 day*
列分隔为日期和后缀列,然后重新整形为宽格式。
library(dplyr)
library(tidyr)
library(stringr)
df1 %>%
pivot_longer(cols = starts_with("day")) %>%
mutate(day = str_extract(name, "\d+"),
suffix = str_extract(name, "[^[:digit:]]+$")) %>%
select(-name) %>%
pivot_wider(
id_cols = -c(value, suffix),
names_from = suffix,
values_from = value
)
## A tibble: 9 x 5
# id sex day s t
# <dbl> <chr> <chr> <dbl> <dbl>
#1 1 M 0 21 2
#2 1 M 1 20 3
#3 1 M 2 18 3
#4 2 F 0 25 5
#5 2 F 1 30 6
#6 2 F 2 18 8
#7 3 M 0 15 7
#8 3 M 1 18 5
#9 3 M 2 17 7
使用 data.table
(1.14.1) 的最新开发版本,其中添加了一些很酷的新熔化功能..
使用data.table::update.dev.pkg()
安装开发版本
library(data.table)
# data.table 1.14.1 IN DEVELOPMENT built 2021-06-22 09:38:23 UTC
dcast(
melt(setDT(df1), measure.vars = measure(day, type, pattern="^day(.)(.)")),
... ~ type, value.var = "value")
id sex day s t
1: 1 M 0 21 2
2: 1 M 1 20 3
3: 1 M 2 18 3
4: 2 F 0 25 5
5: 2 F 1 30 6
6: 2 F 2 18 8
7: 3 M 0 15 7
8: 3 M 1 18 5
9: 3 M 2 17 7