0%

R中数据的tidy规整函数

本文总结R中利用tidyverse包中的函数来对数据进行规整。

1. tidy的数据

在R中数据需要是tidy的,即每个变量单独为一列,一行代表一次观察。
但是实际很多时候我们的数据并不是tidy的,比如,以下两个表格:
表1(宽表格):

name rep1 rep2 rep3
name1 1 2 3
name2 4 5 6
name3 7 8 9

表2(长表格):

name exp rep
name1 1 rep1
name1 2 rep2
name1 3 rep3
name2 4 rep1
name2 5 rep2
name2 6 rep3
name3 7 rep1
name3 8 rep2
name3 9 rep3

在绘制ggplot2时,明显需要将表1(非tidy的)转化为表2(tidy的),但是每次手动转换很麻烦,所以tidyverse包提供了转换的函数,使用以下函数转换很方便:

pivot_longer() 能够将宽表格变成长表格。
pivot_wider() 将长表格变成宽表格。

2. tidyr::pivot_longer()

参数:

  • cols:哪些列需要转换
  • name_to:用于分组的列,即选取的需要转换的列的列名,构成新的一列,新的这一列的名字
  • values_to:选取的需要转换的列的值,构成新的一列,新的列的名字
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
data <- data.frame(
name = paste0(rep("name",5),1:5),
A = c(0.7, 1.0, 1.5, 1.8, 2.2),
B = c(0.5, 0.7, 0.9, 1.3, 1.8),
C = c(0.3, 0.6, 1.0, 1.2, 2.2),
D = c(0.4, 0.7, 1.2, 1.5, 3.2)
)

long_data <- data %>%
pivot_longer(
# cols=-name, 多种写法
cols=A:D,
names_to="rep",
values_to="exp"
)

long_data
# A tibble: 20 x 3
name rep exp
<fct> <chr> <dbl>
1 name1 A 0.7
2 name1 B 0.5
3 name1 C 0.3
4 name1 D 0.4
5 name2 A 1
6 name2 B 0.7
7 name2 C 0.6
8 name2 D 0.7
9 name3 A 1.5
10 name3 B 0.9
11 name3 C 1
12 name3 D 1.2
13 name4 A 1.8
14 name4 B 1.3
15 name4 C 1.2
16 name4 D 1.5
17 name5 A 2.2
18 name5 B 1.8
19 name5 C 2.2
20 name5 D 3.2

3. tidyr::pivot_wider()

将长表格变回宽表格,用pivot_wider()。
参数:

  • names_from:转换之后列名所在的列的列名
  • values_from:转换之后列中值所在的列名
1
2
3
4
5
6
7
8
9
10
11
long_data %>% 
pivot_wider(
names_from="rep",
values_from="exp")
name A B C D
<fct> <dbl> <dbl> <dbl> <dbl>
1 name1 0.7 0.5 0.3 0.4
2 name2 1 0.7 0.6 0.7
3 name3 1.5 0.9 1 1.2
4 name4 1.8 1.3 1.2 1.5
5 name5 2.2 1.8 2.2 3.2

4. 列名转换成多个变量

当想要将原始的数据框的列名转换为多个变量时,怎么办?如下例,将A、B、C成为物种变量,其他的para1、para2、para3成为parameter变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
df <- data.frame(
day = c(1L, 2L, 3L, 4L, 5L),
A_para1 = c(1.1, 1.2, 1.3, 1.4, 1.5),
A_para2 = c(2.1, 2.2, 2.3, 2.4, 2.5),
A_para3 = c(3.1, 3.2, 3.3, 3.4, 3.5),
B_para1 = c(4.1, 4.2, 4.3, 4.4, 4.5),
B_para2 = c(5.1, 5.2, 5.3, 5.4, 5.5),
B_para3 = c(6.1, 6.2, 6.3, 6.4, 6.5),
C_para1 = c(7.1, 7.2, 7.3, 7.4, 7.5),
C_para2 = c(8.1, 8.2, 8.3, 8.4, 8.5),
C_para3 = c(9.1, 9.2, 9.3, 9.4, 9.5)
)

df %>%
pivot_longer(
cols = !day,
names_to = c("species", "parameter"),
names_pattern = "(.*)_(.*)",
values_to = "value"
)
# A tibble: 45 x 4
day species parameter value
<int> <chr> <chr> <dbl>
1 1 A para1 1.1
2 1 A para2 2.1
3 1 A para3 3.1
4 1 B para1 4.1
5 1 B para2 5.1
6 1 B para3 6.1
7 1 C para1 7.1
8 1 C para2 8.1
9 1 C para3 9.1
10 2 A para1 1.2
# ... with 35 more rows

5. 复杂的情形

希望原始数据框的列名中,一部分进入变量,一部分保持原来的列名,比如,从表1到表2:
表1:

name A_para1 A_para2 A_para3 B_para1 B_para2 B_para3
name1 1 2 3 4 5 6
name2 7 8 9 10 1 2
name3 3 4 5 6 7 8

表2:

name rep para1 para2 para3
name1 A 1 2 3
name2 A 7 8 9
name3 A 3 4 5
name1 B 4 5 6
name2 B 10 1 2
name3 B 6 7 8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
df_part_longer <- df %>% 
tidyr::pivot_longer(
cols = !day,
names_to = c("species", ".value"),
names_pattern = "(.*)_(.*)"
)

df_part_longer
# A tibble: 15 x 5
day species para1 para2 para3
<int> <chr> <dbl> <dbl> <dbl>
1 1 A 1.1 2.1 3.1
2 1 B 4.1 5.1 6.1
3 1 C 7.1 8.1 9.1
4 2 A 1.2 2.2 3.2
5 2 B 4.2 5.2 6.2
6 2 C 7.2 8.2 9.2
7 3 A 1.3 2.3 3.3
8 3 B 4.3 5.3 6.3
9 3 C 7.3 8.3 9.3
10 4 A 1.4 2.4 3.4
11 4 B 4.4 5.4 6.4
12 4 C 7.4 8.4 9.4
13 5 A 1.5 2.5 3.5
14 5 B 4.5 5.5 6.5
15 5 C 7.5 8.5 9.5

反过来:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
df_part_longer %>% 
tidyr::pivot_wider(
names_from = species,
values_from = c(para1, para2, para3),
names_glue = "{species}_{.value}"
)
# A tibble: 5 x 10
day A_para1 B_para1 C_para1 A_para2 B_para2 C_para2 A_para3 B_para3 C_para3
<int> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 1 1.1 4.1 7.1 2.1 5.1 8.1 3.1 6.1 9.1
2 2 1.2 4.2 7.2 2.2 5.2 8.2 3.2 6.2 9.2
3 3 1.3 4.3 7.3 2.3 5.3 8.3 3.3 6.3 9.3
4 4 1.4 4.4 7.4 2.4 5.4 8.4 3.4 6.4 9.4
5 5 1.5 4.5 7.5 2.5 5.5 8.5 3.5 6.5 9.5

参考资料:

  1. https://bookdown.org/wangminjie/R4DS/tidyverse-tidyr.html