数据处理的R包
- 2020 年 2 月 17 日
- 笔记
好久没有更新了,觉得不好意思
3.2 数据处理的R包
@Author:By Runsen (版权所有)
内容来源自己的葵花宝典
3.2.1 plyr
整理数据的本质可以归纳为:对数据进行分割(Split),然后应用(Apply)某些处理函数,最后将结果重新组合(Combine)成所需的格式返回,简单描述为:Split – Apply – Combine。plyr
包是Hadley Wickham为解决split – apply – combine问题而写的一个包。使用plyr
包可以针对不同的数据类型,在一个函数内同时完成split – apply – combine三个步骤。plyr
包的主函数是**ply形式的,函数名的第一个字符代表输入数据的类型,第二个字符代表输出数据的类型,其中第一个字符可以是(d、l、a),第二个字母可以是(d、l、a、_ ),不同的字母表示不同的数据格式,d表示数据框格式,l表示列表,a表示数组,_则表示没有输出。
plyr
具体函数如下表所示:
函数名 |
输入值类型 |
输出值类型 |
---|---|---|
aaply |
数组/向量/矩阵 |
数组/向量/矩阵 |
adply |
数组/向量/矩阵 |
数据框 |
aply |
数组/向量/矩阵 |
列表 |
a_ply |
数组/向量/矩阵 |
无 |
ddply |
数据框 |
数据框 |
dlply |
数据框 |
列表 |
daply |
数据框 |
数组/向量/矩阵 |
d_ply |
数据框 |
无 |
laply |
列表 |
数组/向量/矩阵 |
ldply |
列表 |
数据框 |
lldpiy |
列表 |
列表 |
l_ply |
列表 |
无 |
_则表示没有输出,a_ply,d_ply和l_ply在plyr中运用不多。
(1)aaply,adply和alply
a*ply函数语法,需要指定MARGIN和FUN参数:
a*ply(.data, .MARGIN, .FUN)
MARGIN参数选择:
- MARGIN=1:操作基于行
- MARGIN=2:操作基于列
- MARGIN=c(1,2):对行和列都进行操作
FUN内置的函数有mean(平均值)、medium(中位数)、sum(求和)、min(最小值)、max(最大值),当然还包括自定义函数
> library(plyr) > mymat <- matrix(c(1:6), nrow=2, ncol=3) > mymat [,1] [,2] [,3] [1,] 1 3 5 [2,] 2 4 6 > apply(mymat,1,mean) [1] 3 4 > apply(mymat,2,mean) [1] 1.5 3.5 5.5 > class(apply(mymat,1,mean)) [1] "numeric" > adply(mymat,1,mean) X1 V1 1 1 3 2 2 4 > class(adply(mymat,1,mean)) [1] "data.frame" > alply(mymat,1,mean) $`1` [1] 3 $`2` [1] 4 attr(,"split_type") [1] "array" attr(,"split_labels") X1 1 1 2 2 > class(alply(mymat,1,mean)) [1] "list"
(2)daply,ddply和dlply
d*ply函数语法
d*ply(.data,.variables,.fun = NULL, ...)
参数注释:
- data:函数处理的数据框;
- variables:要进行拆分的变量名称,传递变量的格式是:
.(col_name)
,就是把进行分组的变量名包含在.()中; - fun:应用到每行的函数
> df <- data.frame(group = c(rep('A', 2), rep('B', 2), rep('C', 2)), sex = sample(c("M", "F"), size = 2, replace = TRUE),age = round(runif(n = 2, min = 18, max = 54))) > df group sex age 1 A F 26 2 A F 38 3 B F 26 4 B F 38 5 C F 26 6 C F 38 > df_da <- daply(df,.(group,sex), summarize,mean = round(mean(age), 2),sqrt_mean = round(sqrt(mean), 2)) > df_da group mean sqrt_mean A 32 5.66 B 32 5.66 C 32 5.66 > class(df_da) [1] "matrix" > df_dd <- ddply(df, .(group, sex), summarize,mean = round(mean(age), 2),sqrt_mean = round(sqrt(mean), 2)) > df_dd group sex mean sqrt_mean 1 A F 32 5.66 2 B F 32 5.66 3 C F 32 5.66 > class(df_dd) [1] "data.frame" > df_dl <- dlply(df, .(group, sex), summarize,mean = round(mean(age), 2),sqrt_mean = round(sqrt(mean), 2)) > df_dl $A.M mean sqrt_mean 1 34.5 5.87 $B.M mean sqrt_mean 1 34.5 5.87 $C.M mean sqrt_mean 1 34.5 5.87 attr(,"split_type") [1] "data.frame" attr(,"split_labels") group sex 1 A M 2 B M 3 C M > class(df_dl) [1] "list"
(3)laply,llply和ldply
l*ply函数语法
l*ply(.data,.fun = NULL, ...)
参数注释:
- data:函数处理的列表;
- fun:应用到每行的函数
> x = list(a=c(1:3,letters[1:3])) > x $a [1] "1" "2" "3" "a" "b" "c" > myfun = function(x) paste(x,'A',sep='-') > laply(x,myfun) 1 2 3 4 5 6 "1-A" "2-A" "3-A" "a-A" "b-A" "c-A" > class(laply(x,myfun)) [1] "character" > llply(x,myfun) $a [1] "1-A" "2-A" "3-A" "a-A" "b-A" "c-A" > class(llply(x,myfun)) [1] "list" > ldply(x,myfun) .id V1 V2 V3 V4 V5 V6 1 a 1-A 2-A 3-A a-A b-A c-A > class(ldply(x,myfun)) [1] "data.frame"
上面9种函数针对单参数传递,多参数传递的函数mdply
、maply
、和mlply
(4)maply
、mdply
、和mlply
m*ply函数语法
m*ply(data,fun = NULL,expand = TRUE,progress = "none",parallel = FALSE...)
参数注释:
- data:函数处理的数据,矩阵或者数据框
- fun:应用到每行的函数
- progress:是否显示进度条,可以设置为 text
- parallel:是否使用并行
> # 双参数 > f <- function(a, b){return(c(2*a, 3*b))} > # maply返回array数组 > maply(data.frame(a=c(1:2),b=c(2:3)),f) , , = 1 b a 2 3 1 2 NA 2 NA 4 , , = 2 b a 2 3 1 6 NA 2 NA 9 < # mdply自动创建V1 V2作为dataframe的columns > mdply(data.frame(a=c(1:2),b=c(2:3)),f) a b V1 V2 1 1 2 2 6 2 2 3 4 9 > # mlply返回list列表 > mlply(data.frame(a=c(1:2),b=c(2:3)),f) $`1` [1] 2 6 $`2` [1] 4 9 attr(,"split_type") [1] "array" attr(,"split_labels") a b 1 1 2 2 2 3
如果需要阅读更多的plyr教程,可以参考官方文档:http://plyr.had.co.nz/
3.2.2 dplyr
dplyr是一个强大的R包,用于处理,清理和汇总非结构化数据,使得R中的数据探索和数据操作变得简单快捷,也是出于Hadley Wickham之手。
(1)filter
filter函数筛选,查找特定条件的行或者样本,但不能筛选变量
> library(dplyr) > # 筛选Sepal.Length>7.8,Species=="virginica" > filter(.data=iris,Sepal.Length>7.8,Species=="virginica") Sepal.Length Sepal.Width Petal.Length Petal.Width Species 1 7.9 3.8 6.4 2 virginica
管道函数:%>%。其意思是将 %>% 左边的对象传递给右边的函数。
> a <- 1 > f1 <- function(x){ return(x+1) } > b <- a %>% f1 > b [1] 2
filter延展的相关函数filter_all()和filter_at()
> 去掉Species列(非数值型列) > iris_data <- iris %>% select(-Species) > # filter_all 筛选所有属性小于4.5的行 > iris_data%>% filter_all(all_vars(.<4.5)) Sepal.Length Sepal.Width Petal.Length Petal.Width 1 4.4 2.9 1.4 0.2 2 4.3 3.0 1.1 0.1 3 4.4 3.0 1.3 0.2 4 4.4 3.2 1.3 0.2 > #filter_all 筛选任意一个属性大于7.6的行 > iris_data%>% filter_all(any_vars(.>7.6)) Sepal.Length Sepal.Width Petal.Length Petal.Width 1 7.7 3.8 6.7 2.2 2 7.7 2.6 6.9 2.3 3 7.7 2.8 6.7 2.0 4 7.9 3.8 6.4 2.0 5 7.7 3.0 6.1 2.3 > # filter_at 筛选以"Pet开头的属性任一大于>6.5的行 > iris_data%>% filter_at(vars(starts_with("Pet")), any_vars(. >6.5)) Sepal.Length Sepal.Width Petal.Length Petal.Width 1 7.6 3.0 6.6 2.1 2 7.7 3.8 6.7 2.2 3 7.7 2.6 6.9 2.3 4 7.7 2.8 6.7 2.0
(2)select
select函数可以通过指定列名选择指定的变量进行分析,选择变量的同时也可以重新命名变量,类似于SQL语句中的where语句中的筛选条件。
> df name sex age height 1 Runsen M 20 160 2 Zhangsan M 21 165 3 Lisi F 22 150 4 Wangwu F 23 155 > select(df,name) name 1 Runsen 2 Zhangsan 3 Lisi 4 Wangwu > # 选择列的时候,同时对列名进行重命名 > select(df,Name = name) Name 1 Runsen 2 Zhangsan 3 Lisi 4 Wangwu > # 选取以“s”开头的列 > select(df ,starts_with("s")) sex 1 M 2 M 3 F 4 F > # mutate()函数在数据集新增列,但不对原数据作更改 > mutate(df,weigth = 0.67*height -38) name sex age height weigth 1 Runsen M 20 160 69.20 2 Zhangsan M 21 165 72.55 3 Lisi F 22 150 62.50 4 Wangwu F 23 155 65.85
(3)join
join函数合并多个数据表
> df1 <- data.frame(x = c('a','b','c'), y = c('A','B','C')) > df2 <- data.frame(x = c('a','b','d'), z = c('A','B','D')) > # 内连接 inner_join > inner_join(df1,df2,by='x') x y z 1 a 1 A 2 b 2 B 3 c 3 C > # 左连接 right_join > left_join(df1,df2,by ='x') x y z 1 a A A 2 b B B 3 c C <NA> > # 右连接 right_join > right_join(df1,df2,by='x') x y z 1 a A A 2 b B B 3 d <NA> D > # 全连接 full_join > full_join(df1,df2,by="x") x y z 1 a A A 2 b B B 3 c C <NA> 4 d <NA> D
如果需要阅读更多的dplyr
教程,可以参考dplyr
官方文档:https://www.rdocumentation.org/packages/dplyr
3.2.3 tidyr
在数据整合过程中,tidyr
包主要用于处理dataframe
格式数据的整合,tidyr
是同样也是出自 Hadley Wickham,常与dplyr
包结合使用。
tidy
r包主要涉及:gather(宽数据转为长数据),spread(长数据转为宽数据),separate(多列合并为一列)和unite(将一列分离为多列)
(1)gather
使用gather()函数实现宽表转长表,语法如下:
gather(data, key, value, na.rm = FALSE,···)
- data:需要被转换的宽形表
- key:将原数据框中的所有列赋给一个新变量key
- value:将原数据框中的所有值赋给一个新变量value
- na.rm:是否删除缺失值
> library(tidyr) > df <- data.frame(grade=c("A","B","C","D","E"),female=c(5, 4, 1, 2, 3), male=c(1, 2, 3, 4, 5)) > df grade female male 1 A 5 1 2 B 4 2 3 C 1 3 4 D 2 4 5 E 3 5 > # female male两列给于新变量gender_class,所有值赋给与新变量count, -grade除去grade列 > df_gather <- gather(df,gender_class, count, -grade) > df_gather grade gender_class count 1 A female 5 2 B female 4 3 C female 1 4 D female 2 5 E female 3 6 A male 1 7 B male 2 8 C male 3 9 D male 4 10 E male 5
上面是最简单的用法,只指定key与value,返回了一个长数据框。gender_class是列名转化行后指定的列名, count为各列的值。
(2) spread
spread函数作用和gather相反。
spread语法如下:
spread(data, key, value, fill = NA, convert = FALSE, drop = TRUE)
- data:为需要转换的长形表
- key:需要将变量值拓展为字段的变量
- value:需要分散的值
- fill:对于缺失值,可将fill的值赋值给被转型后的缺失值
> df_spread <- spread(df_gather,gender_class,count) > df_spread grade female male 1 A 5 1 2 B 4 2 3 C 1 3 4 D 2 4 5 E 3 5
gender_class是拓展为字段的变量, count为需要分散的值。
(3)unite
tidyr包中提供了unite函数,可以将列,变量以某种形式合并为一列,一个变量
unite语法如下:
unite(data, col, sep = "_", remove = TRUE,...)
- data:数据框
- col:需要合并的列
- sep:被拆分列的分隔符(默认下划线:_)
- remove:是否删除被合并的列
> df_unite <- unite(df, col = 'sex', female, male, sep = '|') grade sex 1 A 5|1 2 B 4|2 3 C 1|3 4 D 2|4 5 E 3|5
(4)separate
separate函数可将一列拆分为多列,一般可用于日志数据或日期时间型数据的拆分,语法如下:
separate(data, col, into, sep = “[^[:alnum:]]+”, remove = TRUE,···)
- data:数据框
- col:需要被拆分的列
- into:新建的列名,为字符串向量
- sep:被拆分列的分隔符:
[^[:alnum:]]+
正则表达式,基本包含了大部分的分隔符 - remove:是否删除被分割的列
> separate(df_unite,sex,c("female","male")) grade female male 1 A 5 1 2 B 4 2 3 C 1 3 4 D 2 4 5 E 3 5
如果需要阅读更多的tidyr教程,可以参考官方文档:https://tidyr.tidyverse.org/
3.2.4 lubridate
R语言的基础包中提供了两种类型的时间数据
- Date类型,仅包括日期数据,它不包括时间和时区信息
- POSIXct / POSIXlt 类型,其中包括了日期、时间和时区信息。
Lubridate包可以减少在R中操作时间变量,内置函数提供了很好的解析日期与时间的便利方法。lubridate 包是 Hadley Wickham开发的用于高效处理时间数据的 R 包。
(1)日期函数
> library(lubridate) > time <- now() #返回系统的日期时间 > time [1] "2020-01-23 12:29:24 CST" > year(time) #返回年 [1] 2020 > month(time) #返回月 [1] 1 > day(time) #返回日 [1] 23 > week(time) #返回周数 [1] 4 > wday(time,label=TRUE) #返回周几 [1] 周四 Levels: 周日 < 周一 < 周二 < 周三 < 周四 < 周五 < 周六 > quarter(time) #返回季度 [1] 1 > minute(time) #返回分钟 [1] 29 > second(time) #返回秒 [1] 24.52954 > Sys.Date() 返回系统的日期时间(base包函数) [1] "2020-01-23"
(2)日期格式转化
日期值通常以文本的形式输入到R中,然后转化为以数值形式存储的日期变量。日期需要转换为文本,才方便读取。
> # ymd将字符串转换为日期类型 : 年(y)月(m)日(d) > ymd('2020-01-23') [1] "2020-01-23" > class(ymd('2020-01-23')) [1] "Date" > # ymd_hms将字符串转换为日期时间类型:时(h)分钟(m)秒(s) > ymd_hms("2020-01-23 12:29:24") [1] "2020-01-23 12:29:24 UTC" > class(ymd_hms("2020-01-23 12:29:24")) [1] "POSIXct" "POSIXt
(3)时段数据
lubridate包内的函数可处理三种类型的时段数据,他们分别是 Interval型、Duration 型和 Period 型。
> arrive <- ymd_hms("2020-01-01 12:00:00", tz = "Pacific/Auckland") > leave <- ymd_hms("2020-01-10 14:00:00", tz = "Pacific/Auckland") # > # Interval: 由两个时间点构成 > interval(arrive, leave) [1] 2020-01-01 12:00:00 NZDT--2020-01-10 14:00:00 NZDT
与 period有关的函数通常以时间单位的复数形式命名,如: minutes、hours,years;与duration 有关的函数通常在对应的 period 函数前加 d,如:dminutes、dhours,dyears。注意:duration 没有dmonths函数
> minutes(4) [1] "4M 0S" > dminutes(4) [1] "240s (~4 minutes)" > hours(24) [1] "24H 0M 0S" > dhours(24) [1] "86400s (~1 days)"
(4)时间运算
比如获得日期的下一个月,只需要加上months(1)
> ymd('2020-01-01') + months(1) [1] "2020-02-01"
获取时间段长度
> time <- interval(arrive, leave) > time / days(1) [1] 9.083333 > time / ddays(1) [1] 9.083333 > # 也可以使用 time_length获取时间段长度 > time_length(time,unit ="day") [1] 9.083333 > time / minutes(1) [1] 13080 > time / dminutes(1) [1] 13080 > time_length(time)
两种类型返回的结果并不是相同的,因为两种类型的时间数据不同的原因。比如闰年366天:period为366,但是duration为365。
> ymd(20200101) + dyears(1) [1] "2020-12-31" > ymd(20200101) + years(1) [1] "2021-01-01"
3.2.5 ggplot2
ggplot2是由Hadley Wickham创建的一个十分强大的可视化R包。按照ggplot2的绘图理念,Plot(图)= data(数据集)+ Aesthetics(美学映射)+ Geometry(几何对象):
- data: 数据集,主要是data frame;
- Aesthetics: 美学映射,比如将变量映射给x,y坐标轴,或者映射给颜色、大小、形状等图形属性;
- Geometry: 几何对象,比如柱形图、直方图、散点图、线图、密度图等。
在ggplot2中有两个主要绘图函数:qplot()以及ggplot()。
- qplot: 顾名思义,快速绘图;
- ggplot:远比qplot()强大,可以一步步绘制十分复杂的图形。
由ggplot2绘制出来的ggplot图可以作为一个变量,然后由print()显示出来。
本文将使用R语言gcookbook包内数据集pg_mean。
> library(gcookbook) > library(ggplot2)
(1)条形图
使用ggplot函数和geom_bar(stat="identity")绘制条形图
> pg_mean group weight 1 ctrl 5.032 2 trt1 4.661 3 trt2 5.526 > ggplot(pg_mean,aes(x=group,y=weight)) + geom_bar(stat = "identity")
绘制条形图,如下图3-18所示。

绘图 geom_bar函数里的stat参数表示对样本点做统计的方式,默认为identity,表示一个x对应一个y,同时还可以是bin,表示一个x对应落到该x的样本数。
绘制簇状条形图
> cabbage_exp Cultivar Date Weight sd n se 1 c39 d16 3.18 0.9566144 10 0.30250803 2 c39 d20 2.80 0.2788867 10 0.08819171 3 c39 d21 2.74 0.9834181 10 0.31098410 4 c52 d16 2.26 0.4452215 10 0.14079141 5 c52 d20 3.11 0.7908505 10 0.25008887 6 c52 d21 1.47 0.2110819 10 0.06674995 > # 将分类变量映射到fill参数,并运行命令geom_bar(position="dodge) > ggplot(cabbage_exp,aes(x=Date,y=Weight,fill=Cultivar))+ geom_bar(position = "dodge",stat = "identity")
绘制簇状条形图,如下图3-19所

示。
fill参数用来指定条形的填充色,position="dodge"使得两组条形在水平方向上错开排列。如果不指定position="dodge",则默认绘制堆积条形图
> ggplot(cabbage_exp,aes(x=Date,y=Weight,fill=Cultivar))+ geom_bar(stat = "identity")
绘制堆积条形图,如下图3-20所示。

(2)散点图
使用ggplot函数和geom_point绘制散点图
> x <- rnorm(100,14,5) > y <- x + rnorm(100,0,1) > ggplot(data= NULL, aes(x = x, y = y)) + geom_point(color = "darkred") + annotate("text",x =13 , y = 20,parse = T,label = "y == x") + geom_smooth(method="lm")
绘制散点图,如下图3-21所示。

annotate("text",x =13 , y = 20, parse = T,label = "y == x") 添加注释
geom_smooth(method="lm")添加平滑线性回归线。
(3)折线图
使用ggplot函数和geom_line函数,并分别指定一个变量映射给x和y
> ggplot(BOD,aes(x=Time,y=demand)) + geom_line() + geom_point()
绘制折线图,如下图3-22所示。

(4)地图
使用maps包绘制的地图与其他ggplot2图形的结合变得十分方便。
下面是一些maps地图数据名和国家
国家 |
maps地图数据名 |
---|---|
法国 |
france |
意大利 |
italy |
新西兰 |
nz |
美国(郡) |
country |
美国(州) |
state |
美国(边界 |
usa |
全世界 |
world |
> library(maps) > data(us.cities) > # 人口大于50万的城市 > # pop人口,lat经度,long纬度 > qplot(long,lat,data) + borders("state",size=0.5)
绘制美国城市分布图,如下图3-23所示。

> China <- subset(world.cities,country.etc == "China") > # 中国城市分布图 > ggplot(China, aes(long, lat)) + geom_point(colour = alpha("black", 1)) + borders("world",xlim = c(75.97,132.19),ylim = c(18.23,52.34),size=0.5)
绘制中国城市分布图,如下图3-24所示。

除了maps包,ggmap,maptools,baidumap和REmap也是不错绘制地图的R包
ggmap包整合了四种地图资源,分别是Google、OpenStreetMaps、Stamen和Cloudmade。可以方便的与ggplot进行涂层叠加,实现在R中的地图绘制需求。
ggmap包中的函数
- get_map:ggmap包中最基本函数,用来下载地图。
- geocode:用来返回某地的经纬度。
- ggmap:绘制地图函数,可与ggplot2中函数进行叠加。
注意:使用ggmap注册谷歌地图开发者,需要获取谷歌地图开发者API,参看链接网址:https://developers.google.com/maps/documentation/javascript/get-api-key
> register_google("API")
baidumap和REmap需要通过github安装
> library("devtools") > install_github('badbye/baidumap',force=TRUE) > install_github('lchiffon/REmap',force=TRUE)
百度地图开发平台官方网址:http://lbsyun.baidu.com/。下面使用baidumap绘制北京大学地图
> library(baidumap) > options(baidumap.key="" > #获取北京大学的地图信息 > q <- getBaiduMap('北京大学', width=600, height=600, zoom=18, scale = 2, messaging=FALSE) > ggmap(q) #绘制地图
绘制北京大学地图,如下图3-25所示。

如果需要阅读更多的ggplot2教程,可以参考官方文档:https://ggplot2.tidyverse.org/reference/