将宽数据转换为具有多个变量的长格式

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 中,gatherspread 已弃用并由 pivot_longerpivot_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