我有这样的数据
library(tidyverse)
df = tribble(
~id, ~a1, ~a2, ~a3, ~b1, ~b2, ~b3, ~c1, ~c2, ~c3,
1, 1, 4, 7, 11, 14, 17, 21, 24, 27,
2, 2, 5, 8, 12, 15, 18, 22, 25, 28,
3, 3, 6, 8, 13, 16, 19, 23, 26, 29,
)
我想将其转换为长格式,其中变量名称包含两部分名称(a,b,c)和数字(1,2,3),它们应该成为表格长版本中的新变量,如下所示。
id name nr data
1 1 a 1 1
2 2 a 1 2
3 3 a 1 3
4 1 a 2 4
5 2 a 2 5
6 3 a 2 6
7 1 a 3 7
8 2 a 3 8
9 3 a 3 8
10 1 b 1 11
11 2 b 1 12
12 3 b 1 13
13 1 b 2 14
14 2 b 2 15
15 3 b 2 16
16 1 b 3 17
17 2 b 3 18
18 3 b 3 19
19 1 C 1 21
20 2 C 1 22
21 3 C 1 23
22 1 C 2 24
23 2 C 2 25
24 3 C 2 26
25 1 C 3 27
26 2 C 3 28
27 3 C 3 29
可以简单地使用dplyr
包中的函数来完成吗?我试过pivot_longer
效果令人失望。
欢迎任何提示。
我知道这个问题以前有人问过,但我找不到一个好的重复目标。同时,如果您指定正则表达式来区分列名的name
部分和nr
部分,则可以在一次函数调用中执行此操作:
df %>%
pivot_longer(-id, names_to = c("name", "nr"),
values_to = "data",
names_pattern = "(^[a-z])(\d$)")
#> # A tibble: 27 × 4
#> id name nr data
#> <dbl> <chr> <chr> <dbl>
#> 1 1 a 1 1
#> 2 1 a 2 4
#> 3 1 a 3 7
#> 4 1 b 1 11
#> 5 1 b 2 14
#> 6 1 b 3 17
#> 7 1 c 1 21
#> 8 1 c 2 24
#> 9 1 c 3 27
#> 10 2 a 1 2
#> # … with 17 more rows
如果您在实践中有不同的列名,请根据需要调整正则表达式,但这会将它们分开,以便第一部分来自字符串开头的单个小写字母,第二部分来自字符串末尾的单个数字。
我们可以通过几种方式做到这一点 - 即首先将pivot_longer
排除"id"列重新塑造为"long"格式,然后通过将sep
指定为正则表达式查找separate
"name"列,即(因为只有一个小写字母),在字母第一次出现后拆分((?<=[a-z])
)
library(dplyr)
library(tidyr)
df %>%
pivot_longer(cols = -id, names_to = 'name', values_to = 'data') %>%
separate(name, into = c("name", 'nr'), sep = "(?<=[a-z])")
-输出
A tibble: 27 × 4
id name nr data
<dbl> <chr> <chr> <dbl>
1 1 a 1 1
2 1 a 2 4
3 1 a 3 7
4 1 b 1 11
5 1 b 2 14
6 1 b 3 17
7 1 c 1 21
8 1 c 2 24
9 1 c 3 27
10 2 a 1 2
# … with 17 more rows
或者另一种选择是在列名中附加后缀,然后使用pivot_longer
library(stringr)
df %>%
rename_with(~ str_c(., "_data"), -id) %>%
pivot_longer(cols = -id, names_to = c("name", "nr", ".value"),
names_pattern = "^(.)(.)_(.*)")