【tidyverse】part2:数据整理
1. tibble数据
1.1 tibble数据简介
“Tibbles are data frames, but they tweak some older behaviors to make life a little easier.”
——Hadley Wickham《R for data science》
Tibble是数据框的一种形式,但是比数据框更整洁,结构更加紧凑,可以理解为tidy table之意
- 如果加载了tidyverse,统一采用tibble格式的数据集
1.2 tibble数据创建
可以通过tibble()
函数来像data.frame()
一样来创建tibble数据
tibble(
x = 1:5,
y = 1,
z = x ^ 2 + y
)
或者可以通过转置函数as_tibble()
来将data.frame格式的数据转化为tibble格式的数据
as_tibble(mtcars)
- 需要注意的是,不同于传统的R语言
as.*
一类函数的用法,对于变量类型的转换,使用的是parse_*
函数族,而对于数据集则使用的是as_*
函数族 - 并且转化时,tibble数据不会改变原始数据的任何信息
如果是想以表格的形式输入数据,可以使用tribble()
函数来创建,只是与tibble()
格式有点小小的不同:
tribble(
~x, ~y, ~z,
#--/--/----
'a', 2, 3.6,
'b', 3, 2.5
)
- 这种格式变量通常以「~」开头
- 使用注释符号来分隔符会使得输入能整洁且便于对应表头
- 注意不要漏掉「,」半角逗号
1.3 tibble数据与传统data.frame
tibble数据与data.frame数据相比主要在以下两个方面有所不同:
- 打印输出
- 子集索引
打印输出
tibble数据输出到console上只会显示前10行,和所有列;这对于大数据量而言是十分有益的
- 如果想获取更详细的信息,使用
str()
函数来提取
如果想明确输出具体的打印信息,可以在print()
函数中设置n=#
和width=Inf
,分别对应行和列的数量;
当然还可以设定默认打印输出的参数,主要有以下三个选项可供修改:
# 最大最小打印值
options(tibble.print_max = m,
tibble.print_min = n)
# 输出所有行
options(dplyr.print_min = Inf)
# 输出所有列
options(tibble.width = Inf)
- 如果想获取更多可以使用
package?tibble
查看
子集索引
tibble索引与传统data.frame格式索引一致,主要差别在:
tibble数据使用的是严谨的全部匹配,如果试图索引一个不存在的列,其会生成警告
小结
- tibble是data.frame的一种,但结构更紧凑、简洁
- 创建tibble数据有
tibble()
和tribble()
两个函数,也可以通过as_tibble()
来转换 - tibble与data.frame的区别在于输出打印和子集索引上,并且R许多基础的函数功能可能对与tibble数据并不起作用,因此如果需要用到某些函数功能时,可能需要将tibble数据转换成data.frame格式
2. 数据整理之tidyr包应用
2.1 什么是tidy数据
tidy数据表示的是已经整理好的、整洁的数据,通常现实中的数据许多都是混乱,甚至是「脏」的,将这些数据进行处理后所得到的数据就可以称之为tidy数据
tidyverse包中的其他包都是为tidy数据而设计的;也可以说,tibble格式的数据就是tidy数据的表现形式;通识tidy数据有以下三个方面的特征:
- 每个变量都对应着单独的一列
- 每条记录都对应着单独的一行
- 每个观察值都对应着单独的单元格
2.2 数据预处理函数
2.2.1 分散与聚合
在现实的分析中,往往需要对数据进行的初步处理,使数据变得整洁:
- 第一步是需要确定变量和观察值是哪些
- 使确定好的变量和观察值能够分别很好地对应列和行
spread函数
分散函数是将变量中的值拆分成新的变量,这个过程概括为就是将为成「列」的值,进行拆分,并指定其他列作为「值」,组合成「键-值」对的形式
#spread函数语法
spread(data, key= , value=, )
#以table2数据集为例子
table2 #查看数据集
table2 %>%
spread(key = 'type', value = 'count')
将type
列的值拆分作为「键」,count
列的值则作为每个键下的「值」,进而将变量值进行拆分
gather函数
在一些数据中存在的问题是,列名并非是变量名,反而是变量的某个具体值,因此就需要将这些已经成为列的「值」聚合到一起,还原成其本来归属的变量里;聚合函数与分散函数是一对互逆的过程
#gather函数语法
gather(data, set_colums, key= , value=, ...)
# 以tidyverse包中的table4a数据为例
table4a #查看数据集
table4a %>%
gather(`1999`, `2000`,
key = 'year', value = 'cases')
简而言之,gather函数就是将成为列的「值」进行「键-值」形式的拆分:
- 每个
1999
和2000
都对应着一个不同的值; - 因此根据
gather
函数中的key
和value
参数不难发现,前者是为1999
和2000
指定新的「键」,即变量;而后者则为这些「值」指定新的变量
通过观察可以很容易看出:
-
gather()
函数是将数据宽格式数据转化为长且窄的格式数据,这对于ggplot2包的使用极其有用 - 而
spread()
函数则是将长格式数据转化为短且宽格式数据
2.2.2 拆分与黏合
分散与聚合是处理将变量与值进行转化时使用到的函数;而有时需要将一个列中的值拆分成两个变量的值,或者将两个变量的值组合成一个新的变量的值,这时候就需要使用到拆分与黏合函数
separate函数
#separate函数语法
separate(data, col, into= )
#以table3数据集为例子
table3 #查看数据集
table3 %>%
separate(rate, into=c('cases','population'))
- 从语法和例子上可以清楚的了解到
separate
参数的指定过程 - 但是该函数默认是将非字母和数字的字符进行分隔,如果希望指定分隔符,则需要额外使用
sep= ''
参数-
如果是要将数字进行分隔,那么使用
sep
参数时需要注意位数的正负:-
sep=1
时,则是从左往右进行位数拆分 -
sep=-1
时,则是从右往左进行位数拆分
-
-
- 并且要注意拆分后变量的类型,有可能会并非正确的数据类型,因此需要在转换时加上
convert =T
参数来确保变量转换成正确的类型
unite函数
同分散与聚合一样,拆分与黏合函数同样时一对互逆的函数与过程;黏合函数是将两列变量的值黏合到一起,组成新的变量
#unite函数语法
unite(data, new_col, old_cols)
#接着separate的例子
table3 %>%
separate(year, into=c('century','year')) %>%
unite(new_year, century, year)
-
unite()
函数默认使用「_」来联结黏合的值,可以通过sep
参数来调整 - 拆分与黏合的语法基本一致,不同的是在第二个参数的使用上
2.2.3 缺失值处理
缺失值的存在方式主要可能有两种:
- 直接用NA来表明
- 间接的使用其他方式来呈现:如应该出现的却没有出现、NULL、stata中的「.」等,这些都有可能是缺失值
complete
complete()
函数是将数据集中所有不重复的列组合到一起进行展示,对于原始数据缺失的值用NA来进行填充
#complete函数语法
complete(data, unique_cols,...)
fill
fill()
函数通过名称可以很容易得知,这是将缺失值进行替换的函数:
#设定数据集
treatment <- tribble(
~ person, ~ treatment, ~response,
#-----------------/------------/----------/
"Derrick Whitmore", 1, 7,
NA, 2, 10,
NA, 3, 9,
"Katherine Burke", 1, 4
)
treatment %>%
fill(person)
fill()
函数默认是用NA值的前面一个值来覆盖NA,如果想调整方向,可以使用.direction=c('up')
参数来进行调整
- 如果忘记参数可以使用
?fill
查看帮助文档
小结
- 本小节主要设计三组对于数据预处理的函数:分散与聚合、拆分与黏合、缺失值展示与填充
- 分散与聚合是将以「键-值」对的形式来展开转化
- 拆分与黏合是将单个值或多个变量拆分或黏合成一个或多个变量的过程
- 缺失值的存在方式有直接和间接两种
运用案例
使用「who」数据集,对数据集进行整理并最后得到tidy数据
library(tidyverse)
who
查看数据集,可以发现:
- iso2和iso3表示地名
- year为变量,不需要变动
- 其他一系列的函数同名较多,可能为值,需要进行聚合
who %>%
gather(new_sp_m014:newrel_f65,
key = "key", value = "cases", na.rm = TRUE)
对数据列进行聚合,并且删除缺失值
之后通过查看key
变量的频数可以发现每个取值都是有规律的,都是由「_」下划线来分隔,因此只需要以「_」来分隔来决定是否对分隔后的变量保留即可;
但是有一点需要注意的是,newrel
的一系列值并没有下划线,因此需要额外使用到stringr包中的str_replace来对这一系列值进行分隔符添加,然后再进行分隔
#str_replace函数语法
library(stringr)
str_replace(string, pattern, replacement)
#添加下划线
who %>%
mutate(key = stringr::str_replace(key, 'newrel', 'new_rel') %>%
separate(key, c('new', 'type', 'sexage'), sep = '_')
最后需要再对sexage
进行拆分,然后删除冗余的变量,保留需要的变量即可
who %>%
select(-iso2, -iso3, -new) %>%
separate(sexage, c('sex', 'age'), sep = 1))
- 注意
sep=1
是指定从左往右分隔1位数
完整代码:
who %>%
gather(new_sp_m014:newrel_f65,
key = 'key', value = 'cases', na.rm = T) %>%
mutate(key = stringr::str_replace(key, 'newrel', 'new_rel')) %>%
separate(key, c('new', 'type', 'sexage'), sep = '_') %>%
select(-iso2, -iso3, -new) %>%
separate(sexage, c('sex', 'age'), sep = 1)
- 使用到的stringr包的
str_replace()
函数以及select()
和mutate()
函数为后面章节的函数