Data Summarisation
数据整理
用select()选择列子集
select(d.class, name, age) %>% head(5) %>% knitr::kable()
|
name |
age |
Alice |
13 |
Becka |
13 |
Gail |
14 |
Karen |
12 |
Kathy |
12 |
select(d.class, age:weight) %>% head(5) %>% knitr::kable()
|
age |
height |
weight |
13 |
56.5 |
84.0 |
13 |
65.3 |
98.0 |
14 |
64.3 |
90.0 |
12 |
56.3 |
77.0 |
12 |
59.8 |
84.5 |
select(d.class, 3:5) %>% head(5) %>% knitr::kable()
|
age |
height |
weight |
13 |
56.5 |
84.0 |
13 |
65.3 |
98.0 |
14 |
64.3 |
90.0 |
12 |
56.3 |
77.0 |
12 |
59.8 |
84.5 |
参数中前面写负号表示扣除
d.class %>% select(-name, -age) %>% head(3) %>% knitr::kable()
|
sex |
height |
weight |
F |
56.5 |
84 |
F |
65.3 |
98 |
F |
64.3 |
90 |
如果要选择的变量名已经保存为一个字符型向量, 可以用one_of()函数引入,如
vars <- c("name", "sex") d.class %>% select(one_of(vars)) %>% head(3) %>% knitr::kable()
|
name |
sex |
Alice |
F |
Becka |
F |
Gail |
F |
head(select(d.class, c("name", "sex")),3)
|
R的字符串函数(如paste()
)和正则表达式函数可以用来生成变量名子集, 然后在select
中配合one_of
使用。
select()
有若干个配套函数可以按名字的模式选择变量列, 如
starts_with("se"): 选择名字以
“se”`开头的变量列;
ends_with("ght"): 选择名字以
“ght”`结尾的变量列;
contains("no"): 选择名字中含有子串
“no”`的变量列;
matches("^[[:alpha:]]+[[:digit:]]+$")
, 选择列名匹配某个正则表达式模式的变量列, 这里匹配前一部分是字母,后一部分是数字的变量名,如abc12
。
num_range("x", 1:3)
,选择x1
, x2
, x3
。
everything()
: 代指所有选中的变量, 这可以用来将指定的变量次序提前, 其它变量排在后面。
R函数subset也能对数据框选取列子集和行子集。
取出单个变量为向量
如果需要选择单个变量并使得结果为普通向量, 可以用dplyr包的pull()函数,
d.class %>% head(3) %>% pull(1) %>% paste(collapse = ':')
|
pull()可以指定单个变量名, 也可以指定变量序号, 负的变量序号从最后一个变量数起。 缺省变量名和序号时取出最后一个变量。
varname <- "name" d.class %>% head(3) %>% select(one_of(varname)) %>% pull() %>% paste(collapse = ":")
|
用arrange()排序
dplyr包的arrange()按照数据框的某一列或某几列排序, 返回排序后的结果,如
d.class %>% arrange(sex,age) %>% knitr::kable()
|
name |
sex |
age |
height |
weight |
Sandy |
F |
11 |
51.3 |
50.5 |
Karen |
F |
12 |
56.3 |
77.0 |
Kathy |
F |
12 |
59.8 |
84.5 |
Alice |
F |
13 |
56.5 |
84.0 |
Becka |
F |
13 |
65.3 |
98.0 |
Gail |
F |
14 |
64.3 |
90.0 |
Tammy |
F |
14 |
62.8 |
102.5 |
Mary |
F |
15 |
66.5 |
112.0 |
Sharon |
F |
15 |
62.5 |
112.5 |
Thomas |
M |
11 |
57.5 |
85.0 |
James |
M |
12 |
57.3 |
83.0 |
John |
M |
12 |
59.0 |
99.5 |
Robert |
M |
12 |
64.8 |
128.0 |
Jeffrey |
M |
13 |
62.5 |
84.0 |
Alfred |
M |
14 |
69.0 |
112.5 |
Duke |
M |
14 |
63.5 |
102.5 |
Guido |
M |
15 |
67.0 |
133.0 |
William |
M |
15 |
66.5 |
112.0 |
Philip |
M |
16 |
72.0 |
150.0 |
用desc()包裹想要降序排列的变量,如
d.class %>% arrange(sex, desc(age)) %>% knitr::kable()
|
name |
sex |
age |
height |
weight |
Mary |
F |
15 |
66.5 |
112.0 |
Sharon |
F |
15 |
62.5 |
112.5 |
Gail |
F |
14 |
64.3 |
90.0 |
Tammy |
F |
14 |
62.8 |
102.5 |
Alice |
F |
13 |
56.5 |
84.0 |
Becka |
F |
13 |
65.3 |
98.0 |
Karen |
F |
12 |
56.3 |
77.0 |
Kathy |
F |
12 |
59.8 |
84.5 |
Sandy |
F |
11 |
51.3 |
50.5 |
Philip |
M |
16 |
72.0 |
150.0 |
Guido |
M |
15 |
67.0 |
133.0 |
William |
M |
15 |
66.5 |
112.0 |
Alfred |
M |
14 |
69.0 |
112.5 |
Duke |
M |
14 |
63.5 |
102.5 |
Jeffrey |
M |
13 |
62.5 |
84.0 |
James |
M |
12 |
57.3 |
83.0 |
John |
M |
12 |
59.0 |
99.5 |
Robert |
M |
12 |
64.8 |
128.0 |
Thomas |
M |
11 |
57.5 |
85.0 |
arrange默认从低到高排列
arrange(d.class, desc(weight))
|
R函数order()可以用来给出数据框的排序次序, 然后以其输出为数据框行下标, 可以将数据框排序。
用rename()修改变量名
在dplyr包的rename()中用“新名字=旧名字”格式修改变量名, 如
d2.class <- d.class %>% dplyr::rename(h=height, w = weight, n=name, a=age, s=sex)
|
注意这样改名字不是对原始数据框修改而是返回改了名字后的新数据框。
rename()这个函数可能出现在其它包中, 保险起见写成dplyr::rename()。
用mutate()计算新变量
dplyr包的mutate()可以为数据框计算新变量, 返回含有新变量以及原变量的新数据框。 如
d.class %>% mutate( rwh=weight/height, sexc=ifelse(sex=="F", "女", "男")) %>% head(3) %>% knitr::kable()
|
name |
sex |
age |
height |
weight |
rwh |
sexc |
Alice |
F |
13 |
56.5 |
84 |
1.486726 |
女 |
Becka |
F |
13 |
65.3 |
98 |
1.500766 |
女 |
Gail |
F |
14 |
64.3 |
90 |
1.399689 |
女 |
用mutate()计算新变量时如果计算比较复杂, 也可以用多个语句组成复合语句,如:
d.class %>% mutate( sexc = { x <- rep("男", length(sex)) x[sex == "F"] <- "女" x } ) %>% head(3) %>% knitr::kable()
|
name |
sex |
age |
height |
weight |
sexc |
Alice |
F |
13 |
56.5 |
84 |
女 |
Becka |
F |
13 |
65.3 |
98 |
女 |
Gail |
F |
14 |
64.3 |
90 |
女 |
d.class %>% mutate( sexp = { x <- rep("nv", length(sex)) x[sex == "M"] <- "nan" x } ) %>% knitr::kable()
|
name |
sex |
age |
height |
weight |
sexp |
Alice |
F |
13 |
56.5 |
84.0 |
nv |
Becka |
F |
13 |
65.3 |
98.0 |
nv |
Gail |
F |
14 |
64.3 |
90.0 |
nv |
Karen |
F |
12 |
56.3 |
77.0 |
nv |
Kathy |
F |
12 |
59.8 |
84.5 |
nv |
Mary |
F |
15 |
66.5 |
112.0 |
nv |
Sandy |
F |
11 |
51.3 |
50.5 |
nv |
Sharon |
F |
15 |
62.5 |
112.5 |
nv |
Tammy |
F |
14 |
62.8 |
102.5 |
nv |
Alfred |
M |
14 |
69.0 |
112.5 |
nan |
Duke |
M |
14 |
63.5 |
102.5 |
nan |
Guido |
M |
15 |
67.0 |
133.0 |
nan |
James |
M |
12 |
57.3 |
83.0 |
nan |
Jeffrey |
M |
13 |
62.5 |
84.0 |
nan |
John |
M |
12 |
59.0 |
99.5 |
nan |
Philip |
M |
16 |
72.0 |
150.0 |
nan |
Robert |
M |
12 |
64.8 |
128.0 |
nan |
Thomas |
M |
11 |
57.5 |
85.0 |
nan |
William |
M |
15 |
66.5 |
112.0 |
nan |
注意这样生成新变量不是在原来的数据框中添加, 原来的数据框没有被修改, 而是返回添加了新变量的新数据框。R软件的巧妙设计保证了这样虽然是生成了新数据框, 但是与原来数据框重复的列并不会重复保存。
计算公式中可以包含对数据框中变量的统计函数结果,如:
d.class %>% mutate( cheight = height - mean(height)) %>% knitr::kable()
|
name |
sex |
age |
height |
weight |
cheight |
Alice |
F |
13 |
56.5 |
84.0 |
-5.8368421 |
Becka |
F |
13 |
65.3 |
98.0 |
2.9631579 |
Gail |
F |
14 |
64.3 |
90.0 |
1.9631579 |
Karen |
F |
12 |
56.3 |
77.0 |
-6.0368421 |
Kathy |
F |
12 |
59.8 |
84.5 |
-2.5368421 |
Mary |
F |
15 |
66.5 |
112.0 |
4.1631579 |
Sandy |
F |
11 |
51.3 |
50.5 |
-11.0368421 |
Sharon |
F |
15 |
62.5 |
112.5 |
0.1631579 |
Tammy |
F |
14 |
62.8 |
102.5 |
0.4631579 |
Alfred |
M |
14 |
69.0 |
112.5 |
6.6631579 |
Duke |
M |
14 |
63.5 |
102.5 |
1.1631579 |
Guido |
M |
15 |
67.0 |
133.0 |
4.6631579 |
James |
M |
12 |
57.3 |
83.0 |
-5.0368421 |
Jeffrey |
M |
13 |
62.5 |
84.0 |
0.1631579 |
John |
M |
12 |
59.0 |
99.5 |
-3.3368421 |
Philip |
M |
16 |
72.0 |
150.0 |
9.6631579 |
Robert |
M |
12 |
64.8 |
128.0 |
2.4631579 |
Thomas |
M |
11 |
57.5 |
85.0 |
-4.8368421 |
William |
M |
15 |
66.5 |
112.0 |
4.1631579 |
新变量可以与老变量名相同, 这样就在输出中修改了老变量。
用tranmute()生成新变量的数据框
函数transmute()用法与mutate()类似, 但是仅保留新定义的变量, 不保留原来的所有变量。 如:
d.class %>% transmute( height_cm = round(height*2.54), weight_kg = round(weight*0.4535924), bmi = weight_kg / (height_cm / 100)^2) %>% head(n=3) %>% knitr::kable()
|
height_cm |
weight_kg |
bmi |
144 |
38 |
18.32562 |
166 |
44 |
15.96748 |
163 |
41 |
15.43152 |
d.class[["rwh"]] <- d.class[["weight"]] / d.class[["height"]]
|
用管道连接多次操作
管道运算符特别适用于对同一数据集进行多次操作。 例如,对d.class数据,先选出所有女生, 再去掉性别和age变量:
d.class %>% filter(sex=="F") %>% select(-sex, -age) %>% knitr::kable()
|
name |
height |
weight |
rwh |
Alice |
56.5 |
84.0 |
1.4867257 |
Becka |
65.3 |
98.0 |
1.5007657 |
Gail |
64.3 |
90.0 |
1.3996890 |
Karen |
56.3 |
77.0 |
1.3676732 |
Kathy |
59.8 |
84.5 |
1.4130435 |
Mary |
66.5 |
112.0 |
1.6842105 |
Sandy |
51.3 |
50.5 |
0.9844055 |
Sharon |
62.5 |
112.5 |
1.8000000 |
Tammy |
62.8 |
102.5 |
1.6321656 |
管道操作的结果可以保存为新的tibble,如:
class_F <- d.class %>% filter(sex=="F") %>% select(-sex)
|
也可以将赋值用->写在最后,如:
d.class %>% filter(sex=="F") %>% select(-sex) -> class_F
|
如果管道传递的变量在下一层调用中不是第一自变量, 可以用.代表,如:
d.class %>% lm(weight ~ height, data=.) %>% coef()
|
t1 <- c(0,1,2) t2 <- c(4,6,8) lm(t2 ~ t1)
|
为了明确表示不使用管道输入作为第一自变量, 可以将管道操作的那一层加上大括号,如:
d.class %>% { lm(weight ~ height, data=.) } %>% coef()
|
宽表转换为长表
pivot_longer函数
tidyr的pivot_longer()函数可以将横向的多次观测堆叠在一列中。 例如, 下面的数据:
显示没有对象dwide1,根据教程的输出结果,创建一个名为dwid
一样的tibble
dwid <- tibble( subject = c(1,2,3,4), "1" = c(1 , NA, 5, NA), '2' = c(NA, 7, 10, NA), '3' = c(NA, NA, NA, 9), "4" = c(NA, 4, NA, NA) )
|
subject是受试者编号, 每个受试者有4次随访, NA表示缺失。 数据分析和绘图用的函数一般不能直接使用这样的数据, 一般需要将测量值作为变量名, 将4次测量合并在一列中, 将随访序号单独放在另外一列中。 用pivot_longer()函数实现:
dwid %>% pivot_longer('1':'4', names_to = 'time', values_to = "response") %>% knitr::kable()
|
subject |
time |
response |
1 |
1 |
1 |
1 |
2 |
NA |
1 |
3 |
NA |
1 |
4 |
NA |
2 |
1 |
NA |
2 |
2 |
7 |
2 |
3 |
NA |
2 |
4 |
4 |
3 |
1 |
5 |
3 |
2 |
10 |
3 |
3 |
NA |
3 |
4 |
NA |
4 |
1 |
NA |
4 |
2 |
NA |
4 |
3 |
9 |
4 |
4 |
NA |
选项names_to
指定一个新变量名, 将原来的列标题转换为该变量的值; 选项values_to
指定一个新变量名, 将原来的各个列对应的测量值保存在该变量名的列中。
注意原来的变量名不是合法R变量名, 所以在pivot_longer()
中用反单撇号保护(也可以使用单引号或双引号), 并用了冒号来表示变量范围, 也可以仿照select
函数中指定变量名的方法将1`:`4
写成:
1`, `2`, `3`, `4
-subject
cols = one_of(vars)
, 其中vars
是保存了1到4的字符串的字符型向量。
如果转换结果中不希望保留那些NA, 可以加values_drop_na=TRUE
:
dwid %>% pivot_longer('1':'4', names_to = "time", values_to = "response", values_drop_na = T) %>% knitr::kable()
|
subject |
time |
response |
1 |
1 |
1 |
2 |
2 |
7 |
2 |
4 |
4 |
3 |
1 |
5 |
3 |
2 |
10 |
4 |
3 |
9 |
从列名中提取数值
有时要合并的列名中带有数值, 需要将这些数值部分提取出来, 这时可以用names_prefix
指定要去掉的非数值前缀, 用names_ptypes
指定将列名转换为值时, 转换的类型, names_ptypes
是一个列表, 实现列表元素名到转换类型的映射。 例如,上述的dwid数据框变成这样:
subject |
FU1 |
FU2 |
FU3 |
FU4 |
1 |
1 |
NA |
NA |
NA |
2 |
NA |
7 |
NA |
4 |
3 |
5 |
10 |
NA |
NA |
4 |
NA |
NA |
9 |
NA |
可以用如下程序将随访编号变成整数值存入一列:
dwide2 %>% pivot_longer(cols = paste0("FU", 1:4), names_to = "time", values_to = "response", names_prefix = "FU", names_ptype = list(time = integer()), values_drop_na = TRUE) %>% knitr::kable()
|
subject |
time |
response |
1 |
1 |
1 |
2 |
2 |
7 |
2 |
4 |
4 |
3 |
1 |
5 |
3 |
2 |
10 |
4 |
3 |
9 |
其中的cols = paste0(“FU”, 1:4)也可以写成cols = starts_with(“FU”)。
一行中有多个属性的多次观测的情形
dwide <- anscombe[1:3,] dwide[["id"]] <- seq(3) knitr::kable(dwide)
|
x1 |
x2 |
x3 |
x4 |
y1 |
y2 |
y3 |
y4 |
id |
10 |
10 |
10 |
8 |
8.04 |
9.14 |
7.46 |
6.58 |
1 |
8 |
8 |
8 |
8 |
6.95 |
8.14 |
6.77 |
5.76 |
2 |
13 |
13 |
13 |
8 |
7.58 |
8.74 |
12.74 |
7.71 |
3 |
用names_pattern指定切分变量名和随访号的模式, 在对应的names_to中用特殊的".value"名字表示切分出来的那一部分实际是变量名, 这时不需要values_to选项。
dwide %>% pivot_longer( -id, names_pattern = "(x|y)([[:digit:]])", names_to = c(".value", "time") ) %>% knitr::kable()
|
id |
time |
x |
y |
1 |
1 |
10 |
8.04 |
1 |
2 |
10 |
9.14 |
1 |
3 |
10 |
7.46 |
1 |
4 |
8 |
6.58 |
2 |
1 |
8 |
6.95 |
2 |
2 |
8 |
8.14 |
2 |
3 |
8 |
6.77 |
2 |
4 |
8 |
5.76 |
3 |
1 |
13 |
7.58 |
3 |
2 |
13 |
8.74 |
3 |
3 |
13 |
12.74 |
3 |
4 |
8 |
7.71 |
长表转换为宽表
将多个混在一起的变量归类
tidyr包的pivot_wider函数可以将长表变成宽表。 这适用于将多个变量保存到了一列的情况。 例如,下面的长表将变量x和y放在了同一列中:
比如先创建一个数据集:
trl <- tibble( id = c(1,1,2,2,3,3), bl = c('x','y','x','y','x','y'), vl = c(23,32,4,5,67,89) )
|
用pivot_wider函数将两个变量放到各自的列中, 用names_from选项指定区分不同变量的列,用values_from指定保存实际变量值的列:
trl %>% pivot_wider( names_from = "bl", values_from = "vl" ) %>% knitr::kable()
|
id |
x |
y |
1 |
23 |
32 |
2 |
4 |
5 |
3 |
67 |
89 |
trl %>% pivot_wider( names_from = "id", values_from = 'vl' ) %>% knitr::kable() -> trlxy
|
bl |
1 |
2 |
3 |
x |
23 |
4 |
67 |
y |
32 |
5 |
89 |
数据拆分时,有的数据没有收集到,如下面这个数据:
trl1 <- tibble( id = c(1,1,2,3), bl = c('x','y','x','y'), vl = c(23,32,4,5) ) knitr::kable(trl1)
|
id |
bl |
vl |
1 |
x |
23 |
1 |
y |
32 |
2 |
x |
4 |
3 |
y |
5 |
这里2号id缺少y,3号id缺少x。 直接转换为宽表:
trl1 %>% pivot_wider( names_from = bl, values_from = vl ) %>% knitr::kable()
|
id |
x |
y |
1 |
23 |
32 |
2 |
4 |
NA |
3 |
NA |
5 |
产生了缺失值。 如果知道缺失值实际等于0, 可以用选项values_fill=选项指定,如:
trl1 %>% pivot_wider( names_from = bl, values_from = vl, values_fill = list( vl = 0 ) ) %>% knitr::kable()
|
id |
x |
y |
1 |
23 |
32 |
2 |
4 |
0 |
3 |
0 |
5 |
将多个类别合并到一个观测
dlong3 %>% pivot_wider( names_from = time, values_from = x, names_prefix = "x") %>% knitr::kable()
|
改tibble列名操作:
trl2 <- trl names(trl2)
names(trl2)[3] <- "y1" names(trl2)
|
将多个类别合并到一个观测
trl3 <- tibble( id = c(1,1,2,2,3,3), time = c(1,2,1,2,1,2), x = c(11,2,3,4,56,67) ) knitr::kable(trl3)
|
id |
time |
x |
1 |
1 |
11 |
1 |
2 |
2 |
2 |
1 |
3 |
2 |
2 |
4 |
3 |
1 |
56 |
3 |
2 |
67 |
将x的两次测量变成变量x1和x2:
trl3 %>% pivot_wider( names_from = "time", values_from = "x", names_prefix = "x" ) %>% knitr::kable()
|
id |
x1 |
x2 |
1 |
11 |
2 |
2 |
3 |
4 |
3 |
56 |
67 |
将交叉类别合并到一个观测
trl4 <- trl trl4[4] <- rep(c(2018,2019),3) names(trl4)[4] <- "year" knitr::kable(trl4)
|
id |
bl |
vl |
year |
1 |
x |
23 |
2018 |
1 |
y |
32 |
2019 |
2 |
x |
4 |
2018 |
2 |
y |
5 |
2019 |
3 |
x |
67 |
2018 |
3 |
y |
89 |
2019 |
将trl4按照year分组
trl4 %>% pivot_wider( names_from = c(id,bl), values_from = vl ) %>% knitr::kable()
|
year |
1_x |
1_y |
2_x |
2_y |
3_x |
3_y |
2018 |
23 |
NA |
4 |
NA |
67 |
NA |
2019 |
NA |
32 |
NA |
5 |
NA |
89 |
多个变量的多种值
trl5 <- trl4 names(trl5)[4] <- "sd" trl5[4] <- c(0.5,0.4,0.8,0.7,0.9,0.2) trl5
|
trl5 %>% pivot_wider( names_from = "bl", values_from = c("vl","sd") ) %>% knitr::kable()
|
id |
vl_x |
vl_y |
sd_x |
sd_y |
1 |
23 |
32 |
0.5 |
0.4 |
2 |
4 |
5 |
0.8 |
0.7 |
3 |
67 |
89 |
0.9 |
0.2 |
长宽转换混合使用
要将以下数据:
id |
variable |
2018 |
2019 |
1 |
x |
0.5 |
11 |
1 |
y |
0.4 |
2 |
2 |
x |
0.8 |
3 |
2 |
y |
0.7 |
4 |
3 |
x |
0.9 |
56 |
3 |
y |
0.2 |
67 |
整理为:
id |
year |
x |
y |
1 |
2018 |
0.5 |
0.4 |
1 |
2019 |
11.0 |
2.0 |
2 |
2018 |
0.8 |
0.7 |
2 |
2019 |
3.0 |
4.0 |
3 |
2018 |
0.9 |
0.2 |
3 |
2019 |
56.0 |
67.0 |
首先先创建一个这样的数据集
trll <- tibble( id=c(1,1,2,2,3,3), variable= rep(c("x","y"),3), '2018' =c(0.5,0.4,0.8,0.7,0.9,0.2), '2019'= trl3[["x"]] )
|
数据转换:
trll %>% pivot_longer( '2018':'2019', names_to = "year", values_to = "value" ) %>% pivot_wider( names_from = "variable", values_from = "value" ) %>% knitr::kable()
|
id |
year |
x |
y |
1 |
2018 |
0.5 |
0.4 |
1 |
2019 |
11.0 |
2.0 |
2 |
2018 |
0.8 |
0.7 |
2 |
2019 |
3.0 |
4.0 |
3 |
2018 |
0.9 |
0.2 |
3 |
2019 |
56.0 |
67.0 |