1. separate和unite
spread和gather函数可以帮助解决数据中的变量放错了位置的问题,而separate和unite则是为了解决以下问题:多个变量挤在了同一列中,或是一个变量分散到了不同的列中
举个例子,以下数据集记录了某种药物治疗的响应数据。它包含三个变量(时间、治疗状态和响应值),但是时间和治疗状态的数据被记在一起了,变成了一个变量:
trt <- dplyr::data_frame(var = paste0(rep(c("beg", "end"), each = 3), "_", rep(c("a", "b", "c"))), val = c(1, 4, 2, 10, 5, 11))
trt
separate()函数可以轻而易举地将这一混杂的列拆分成多个变量,它包含以下四个主要参数
- data 需要调整地数据框
- col 需要进行拆分的列的列名
- into 拆分后新生成变量的列名,格式为字符型变量
- sep 对如何拆分原变量的描述,其可以是正则表达式,如_表示通过下划线拆分,或[^a-z]表示通过任意非字母字符拆分,或一个指定位置的整数
在这个例子中,用_字符进行拆分
separate(trt, var, c("time", "treatment"), "_")
(如果变量被一种更复杂的形式混合在一起,可以尝试以下extract()函数,另外如果需要单独建立一列经由某种运算生成的变量,mutate()函数是一种方法)
unite()函数是separate()的逆运算——它可以将多列合并为一列。尽管不常用,但知道其实separate()的逆函数还是很有必要的
2. 案例学习
对于大部分真实数据,需要运用的整理命令不止一个。虽然有很多路径可以走,但是只要每一步都让数据集更整洁一些,最终总会得到一个令人满意的整洁数据集。这意味着,通常会以相同的顺序使用这些函数:gather()、separate()和spread()(虽然并不一定每个都会用到)
1. 血压
数据整理的第一步:确定数据集里的变量。看下面这个模拟医疗数据的数据集,其中有7个变量:姓名、年龄、开始日期、星期、收缩压和舒张压。它是以什么形式存储的?
‘# 从Barry Rowlingson的例子修改而来
# http://barryrowlingson.github.io/hadleyverse/
bpd <- readr::read_table(
"name age start week1 week2 week3
Anne 35 2014-03-27 100/80 100/75 120/90
Ben 41 2014-03-09 110/65 100/65 135/70
Carl 33 2014-04-02 125/80 <NA> <NA>
", na = "<NA>"
)
首先,化宽为长:
bpd_1 <- gather(bpd, week, bp, week1:week3)
bpd_1
数据看起来更整洁了,但是有两个变量(收缩压与舒张压)被一起记录在bp变量下。尽管经常这么记录血压,但是把它们分开菜更易于分析。separate就可以做到
bpd_2 <- separate(bpd_1, bp, c("sys", "dia"), "/")
bpd_2
这个数据集现在已经很整洁了,但可以再进一步让它变得更好用。下面的代码中运用了extract()函数,将week列中的数字单独取出来作为该列的值。还利用了arrange()函数对行进行了排列以使每个人的记录都聚在一起
bpd_3 <- extract(bpd_2, week, "week", "(\\d)", convert = TRUE)
bpd_4 <- dplyr::arrange(bpd_3, name, week)
bpd_4
可能会注意到这个数据集存在一定的重复:如果知道了姓名,自然就知道了年龄与开始时间。这反映了整洁的第三个条件:每个数据框应该有且仅有一个数据集。而这里事实上包含了两个数据集:不随时间改变的个人信息,和他们每周的血压测量值。
2. 考试成绩
# 修改自 https://stackoverflow.com/questions/29775461
set.seed(127)
scores <- dplyr::data_frame(
person = rep(c("Greg", "Sally", "Sue"), each = 2),
time = rep(c("pre", "post"), 3),
test1 = round(rnorm(6, mean = 80, sd = 4), 0),
test2 = round(jitter(test1, 15), 0)
)
scores
变量包括人、考试、干预前成绩、干预后成绩。像之前那样,先将宽数据(test1和test2)转换为长数据形式(test和score)
scores_1 <- gather(scores, test, score, test1:test2)
scores_1
接下来,需要做一件相反的事:pre(前)和post(后)应当是两个变量而非值,所以我们要展开time和score变量
scores_2 <- spread(scorres_1, time, score)
scores_2
我们将数据集整理好的一个重要证据是,它现在很容易计算出想要的统计量,即干预前后的成绩差值
scores_3 <- mutate(scores_2, diff = post - pre)
score_3
接下来作图就很容易了
ggplot(scores_3, aes(person, diff, color = test)) +
geom_hline(size = 2, color = "white", yintercept = 0) +
geom_point() +
geom_path(aes(group = person), color = "grey50",
arrow = arrow(length = unit(0.25, "cm")))
了解更多
数据整理是一个很大的话题,本章只触及其表面,关于其更深层次的讨论,推荐以下参考文献
- tidyr包的文档
- Tidy data
- data wrangling cheatsheet