使用多个变量将宽格式数据转换为长格式数据

4

这可能有一个简单的答案,但是经过几个小时的搜索后,我仍然找不到。基本上,我需要将一个宽格式数据集转换为长格式数据集,但是包含多个变量。我的数据集结构如下:

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

基本上,有三个科目进行了数学考试(s)和历史考试(t),每天进行3天。我尝试使用tidyr中的gather函数将其转换为长格式,但我不知道如何将mt和ht变量分配到同一天。我还编写了一个新变量day,只包括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

您有关于如何实现这一目标的任何建议吗?


我建议删除变量列的日期和数字后缀。然后使用pivot_wider创建mt和ht列。 - writer_typer
发布的数据具有带有后缀st的列day不是 mtht - Rui Barradas
3个回答

6
你可以在这里使用 {tidyr} 的 pivot_longer
如果你的实际变量名称有些不同,你可以根据自己的情况调整正则表达式。在这里你可以尝试并相应地进行调整。(请注意,在R中反斜杠必须转义,因此\\d+\\w+中有双重反斜杠)
一般来说,names_pattern参数通过将括号内的正则表达式与names_to参数匹配来工作,因此在这里:
  • (\\d+) -> 变成变量 day。正则表达式 \d+ 匹配一个或多个数字。
  • (\\w+) -> 变成 ".value"。正则表达式 \w+ 匹配一个或多个单词字符。感谢 r2evans 指出的 ".value" 参数,避免了进一步的重塑。文档 表示 .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

本文于2021-06-20由reprex package (v2.0.0)创建

请注意,在较新的tidyr版本中,gatherspread已被弃用,取而代之的是pivot_longerpivot_wider.


4
使用 names_to = c("day", ".value"),并删除 pivot_wider :-) - r2evans
谢谢,这个很有效,我能够修改命名规则以适应我的实际变量。 - Phate
太好了!很高兴它起作用了。感谢r2evans指出了“.value”特殊值。现在我终于对这个解决方案感到满意 :) - Marcelo Avila

2

使用最新的开发版 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

0

这里有一种方法。首先将其重塑为长格式,将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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接