Data Summarisation

数据整理

用select()选择列子集

select(d.class, name, age) %>%
head(5) %>%
knitr::kable()
name age
Alice 13
Becka 13
Gail 14
Karen 12
Kathy 12
# 使用列名取age到weight的所有列
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
# similar to:
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()函数,

# paste(pull(head(d.class,3),name), collapse = ":")
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 %>%
# 先用字母顺序把sex排序,然后在此基础上对age进行排序
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默认从低到高排列

# 使用desc()降序排列
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,
# 将F等于女
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()
# ~的作用,算t2和t1的关系。t2=2t1+4
t1 <- c(0,1,2)
t2 <- c(4,6,8)
lm(t2 ~ t1)
## 以下为输出结果
# Call:
# lm(formula = t2 ~ t1)
#
# Coefficients:
# (Intercept) t1
# 4 2

为了明确表示不使用管道输入作为第一自变量, 可以将管道操作的那一层加上大括号,如:

d.class %>% {
lm(weight ~ height, data=.) } %>%
coef()

宽表转换为长表

pivot_longer函数

tidyr的pivot_longer()函数可以将横向的多次观测堆叠在一列中。 例如, 下面的数据:

knitr::kable(dwide1)

显示没有对象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,
# 表示x或y接着数字的命名
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指定保存实际变量值的列:

# 根据id,x,y分列
trl %>%
pivot_wider(
names_from = "bl",
values_from = "vl"
) %>%
knitr::kable()
id x y
1 23 32
2 4 5
3 67 89
# 以id为列名,以x,y为行
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列名操作:

# 复制一个trl 的tibble数据
trl2 <- trl
names(trl2)
# 更改v1为y1
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(
# 第一行的列名为id_bl的值,这里可以省略双引号
names_from = c(id,bl),
# 填充的值来自vl(就是除了需要的列名和行名之外的值),剩下的就是行名
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

多个变量的多种值

# 将trl4改造
trl5 <- trl4
names(trl5)[4] <- "sd"
trl5[4] <- c(0.5,0.4,0.8,0.7,0.9,0.2)
trl5
# 填充两个value,列名也会自动适应更改
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',
# 将2018和2019列转为行,并将这两行对应的列名命名为year
names_to = "year",
# 将2018,2019的列的值另取一列,命名为value
values_to = "value"
) %>%
# 用pivot_wider函数将长表变成宽表。
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