tidyverse 包是 Hadley Wickham 及团队的集大成之作,集数据读取,操作,探索和可视化于一体的一系列R包的集合。其理念是一开始先忽略编程的一些基础概念,如向量,矩阵,数据框,因子等,而是直接从数据的操作入手,可以有效的在短时间内学会数据的处理和可视化。但需要强调的一点是,如果想深入学习R语言以及其他编程语言,基础乃根本。
1. 基础
tibbles数据框和管道操作是学习tidyverse的基础。tibble是通过对传统的数据框功能的修改,开发的一种简单的数据框,更易于使用。在进行线性操作时,管道操作可以让你的代码看起来更加简洁,加代码的可读性。
tibbles数据框
-
通过 tibble()
函数或tribble()
来创建新tibble,as_tibble()
函数将数据框转换为tibble。 -
tibble()函数当向量长度为1时,tibble会自动重复。 -
tibble默认的数字类型为double。 -
相较于data.frame,tibble()的劣势在于其不能创建行名,不能改变输入的类型。
tibble(
x = 1:4,
y = 1,
z = letters[1:4]
)
tribble(
~x, ~y, ~z,
1, 1, "a",
2, 1, "b",
3, 1, "c",
4, 1, "d"
)
tibble中也可以使用R中无效的变量名称,但需要加反引号。
tibble(
`100` = 1:4,
`<:` = 1,
`:)` = letters[1:4]
)
在处理大数据集时,tibble在打印时,只显示前10行的结果;并且,借鉴于 str()
函数,打印出每列的数据类型。
nycflights13::flights
2. 管道操作
管道操作%>%来源于Stefan Milton Bache开发的magrittr包,其原理是进行“词法变换”。在进行线性操作时,基于管道操作,可以优化代码,免去建议中间变量的繁琐步骤,增加代码的简洁性和可读性。并且管道操作也可用于基础函数,但是assign(),trycatch()等函数不适合于管道操作。
建议在操作步骤大于10,操作步骤复杂或有多个输入和输出的情况下最好不要使用管道。
# 对mtcars数据集每列求均值,函数不理解没关系,后续会讲到
mtcars %>%
as_tibble() %>%
summarise_all(mean)
更多的管道操作符可以使用help(package = "magrittr")查看。
2. 数据读写
数据读入
主要通过readr包的函数来实现。相较于基础函数,readr包函数的读取速度要快10倍左右,并且由于其不会集成操作系统的功能,因而更易重复使用。
-
read_csv()
读取逗号分隔的文件; -
read_csv2()
读取分号分隔的文件; -
read_tsv()
读取制表符分隔的文件; -
read_delim()
读取任意字符分隔的文件; -
read_fwf()
读取固定宽度的文件; -
read_table()
读取空白字符分隔的文件; -
......
此外,常见的excel文件可以使用readxl包的 read_excel
, read_xls()
, read_xlsx()
函数读取;SPSS、Stata 和 SAS ⽂件可以通过haven包读取;JSON串通过jsonlite包读取;XML通过xml2包读取。
以read_csv()函数为例,其主要参数如下:
这里我提前将iris数据集保存到本地,再使用read_csv来读取。
data <- read_csv("iris.csv")
数据写出
readr包提供的write_系列函数用于数据的写出,其中, write_csv
和 write_tsv()
最常见,因为他们使用UTF-8对字符串编码,使用ISO8601格式保存时间数据,所以他们生成的文件能顺利读取的概率更高。此外,wrtie_excel_csv()
可以生成excel文件。
3. 基于dplyr的数据转换
基于nycflights13::flights和mtcars数据集,介绍dplyr包中的基础操作。具体可使用?flights和?mtcars查看。
数据筛选
filter()
函数中的多个参数之间是通过“与”组合起来的。
筛选出12月31日的航班信息
nycflights13::flights %>%
filter(month == 12, day == 31)
此外,filter_系列函数还包括filter_all()
,filter_at()
, filter_if()
函数。
-
filter_all 该函数对数据框中的每一行都进行筛选操作。
# 筛选任一列中大于150的行
# 辅助函数还有all_vars
mtcars %>%
filter_all(any_vars(. > 150))
-
filter_at
# 筛选所有d开头的列变量都能够被2整除的行
mtcars %>%
filter_at(vars(starts_with("d")), any_vars((. %% 2) == 0))
-
filter_if
# 首先将所有列的值转换不大于某个值的最大整数值,然后使用all函数判断转换前后值是否相同而产生逻辑值,当值为TRUE时,筛选出所有列均不等于0的观测值
mtcars %>%
filter_if(~ all(floor(.) == .), all_vars(. != 0))
数据选择
select()
函数基于变量名操作,可以快速生成一个有用的变量子集。
nycflights13::flights %>%
select(year, month, dep_time)
nycflights13::flights %>%
select(year:dep_time)
select也可以使用“-”选择不在某个范围内,或者非某个变量名的列。
nycflights13::flights %>%
select(-(year:dep_time))
在 select() 函数中还可以使⽤⼀些辅助函数:
-
starts_with("abc") :匹配以 abc 开头的名称。 -
ends_with("xyz") :匹配以 xyz 结尾的名称。 -
contains("ijk") :匹配包含 ijk 的名称。 -
matches("(.)\1") :选择匹配正则表达式的那些变量。这个正则表达式会匹配名称中有重复字符的变量。 -
num_range("x", 1:3) :匹配 x1、x2 和 x3。
以ends_with为例:
nycflights13::flights %>%
select(ends_with("time"))
此外,与filter函数类似,select系列还有select_at()
, select_if()
,select_all()
等,具体可以通过help()查看,用法与filter类似。
数据排列
arrange()
函数基于选择的列,对数据框的行进行排序。如果参数不止一个,则在前一个参数排序的基础上再进行排序。默认是升序,使用desc()函数可以进行降序排列,缺失值总排在最后。
nycflights13::flights %>%
arrange(desc(month), dep_time)
那么,一次性想选择多列进行排序,该如何实现呢?across()
函数为我们提供了解决办法,其能够同时对多列应用函数。
nycflights13::flights %>%
arrange(across(year:month))
同样,arrange也存在三种变体,同样是at, all, if系列,具体使用参见函数帮助文档。
添加新变量
mutate()
函数,其也有at,all,if变体系列。此外,还有mutate_each()
和mutate_each_()
函数,但是,在dplyr 0.7.0.中,mutate_each_()
被弃用,作者建议使用across()函数代替。
nycflights13::flights %>%
.[, 4:8] %>%
mutate(time = dep_time - arr_time)
mtcars %>%
.[1:4, 1:4] %>%
mutate_each(mean)
分组、统计
group_by() 可以将分析单位从整个数据集更改为单个分组。接下来,在分组后的数据框上使⽤ dplyr 函数时,它们会⾃动地应⽤到每个分组,取消分组使用 ungroup()函数。常用的是分组后使用summmarise()函数。
flights %>%
group_by(dest) %>%
summarise(
count = n(),
dist = mean(distance, na.rm = TRUE),
delay = mean(arr_delay, na.rm = TRUE)
) %>%
filter(count > 20, dest != "HNL")
值得注意的是,当使⽤多个变量进⾏分组时,每次的摘要统计会⽤掉⼀个分组变量。这样就可以轻松地对数据集进⾏循序渐进的分析。 group_by()和summarise()函数也有一系列变体,建议通过阅读帮助文档学习,感觉大同小异(个人观点)。
重命名列
rename()
通过赋值的形式来改变列名; rename_with()
函数通过传递函数实现改变列名.当然,它也也诸多变体函数,在这里不一一介绍。
mtcars %>%
.[1:4, 1:4] %>%
rename(Mpg = mpg)
mtcars %>%
.[1:4, 1:4] %>%
rename_with(str_to_title)
长宽数据转换
-
宽数据格式变长数据格式
⼀种常⻅的问题是数据集中某些列名不是变量名,而是变量值。为了整理这样的数据集,我们需要将有问题的列旋转到⼀对新的变量中。
tidyr::table4a %>%
pivot_longer(
c(`1999`, `2000`),
names_to = "year",
values_to = "cases")
-
长数据格式变宽数据格式 为了方便,我们将上一部分得到的数据重新转成宽数据格式。
tidyr::table4a %>%
pivot_longer(
c(`1999`, `2000`),
names_to = "year",
values_to = "cases") %>%
pivot_wider(
names_from = year,
values_from = cases)
列的拆分、合并
separate()函数能够依据分隔符分隔列,unite()是其反向操作函数。
tidyr::table3 %>%
separate(
rate,
into = c("cases", "population"),
sep = '/')
library(lubridate)
nycflights13::flights %>%
.[, 1:3] %>%
unite("ymd", year, month, day, remove = F) %>%
mutate(ymd = as_date(ymd))
欢迎关注我的微信公众号。
参考
- 《R数据科学》