数据处理的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种函数针对单参数传递,多参数传递的函数mdplymaply、和mlply

(4)maplymdply、和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包结合使用。

tidyr包主要涉及: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语言的基础包中提供了两种类型的时间数据

  1. Date类型,仅包括日期数据,它不包括时间和时区信息
  2. 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/