简介

R语言中,自身已经带有了强大的数据处理、数据计算等方面的函数。
虽然,对于大规模的数据集合,处理过程可能会不如Python快,但是小规模的数据处理,R语言使用起来仍然会更方便。

值得注意的是,为了执行效率,我们要尽量避免在R语言中,使用循环函数,而是要运用向量化的处理函数,即R语言Base基础包中,

当然,记得apply家族的那么多函数以及不同的用法是一件麻烦的事情,于是类似plyr,dplyr,tidyr,reshape2等包相继出现,能够运用更加简化的函数来进行数据处理,同时又不会降低R语言的处理效率。

正文

一,数据处理中的基本函数

二,第三方包的介绍-plyr

plyr包,是用来实现Splitting, Applying and Combining Data的.
即,把原始数据经过条件拆分成数据子集—>子集经过函数逻辑的处理—>子集结果的合并的过程。

其动机在与提供超越for循环和内置的apply函数族的一个一揽子解决方案,高效的替代很多复杂的循环;

plyr包提供的*ply() 函数族如下:

R语言预处理geo代码 r语言数据预处理_第三方包

一般情况下,我们做模型分析,需要输出的格式是DataFrame,所以,此处以
adply函数来讲解。

adply(.data, .margins, .fun = NULL, …, .expand = TRUE,
.progress = “none”, .inform = FALSE, .parallel = FALSE,
.paropts = NULL, .id = NA)

df = data.frame(
                lot1 = rep_len(2,6),
                lot2 = seq(6)
                )
df1=within(df,{
    lot3 = lot1 + lot2
    lot4 = lot1 * lot2
     })

> adply(df1,1,'sum')      # 不是array的自动转成array再进行计算了
  lot1 lot2 lot4 lot3 sum
1    2    1    2    3   8
2    2    2    4    4  12
3    2    3    6    5  16
4    2    4    8    6  20
5    2    5   10    7  24
6    2    6   12    8  28

> adply(df1,2,'sum')
    X1 sum
1 lot1  12
2 lot2  21
3 lot4  42
4 lot3  33

> ldply(df1,'sum')
   .id sum
1 lot1  12
2 lot2  21
3 lot4  42
4 lot3  33

但是,虽然操作上看,plyr更容易,但是效率上还是R原生的函数快一些。

> system.time(ldply(df1,'sum'))
 用户  系统  流逝 
0.003 0.000 0.002 
> system.time(adply(df1,1,'sum'))
 用户  系统  流逝 
0.005 0.000 0.004 
> system.time(adply(df1,2,'sum'))
 用户  系统  流逝 
0.005 0.000 0.005 
> system.time(lapply(df1,sum))
用户 系统 流逝 
   0    0    0

三,第三方包的介绍-dplyr

在R中,dplyr起到的作用如同数据库中的SQL。主要用来实现增删改查,以及聚合汇总的数据操作。

1, 变量筛选(列操作)

R语言预处理geo代码 r语言数据预处理_R语言预处理geo代码_02

2, 条件过滤(行操作)

R语言预处理geo代码 r语言数据预处理_R语言预处理geo代码_03

3,添加变量或重命名

R语言预处理geo代码 r语言数据预处理_R语言预处理geo代码_04

4,变量排序

arrange(mtcars, cyl, disp)
arrange(mtcars, desc(disp))

5,数据聚合与汇总

R语言预处理geo代码 r语言数据预处理_数据_05

6,数据集合并

R语言预处理geo代码 r语言数据预处理_缺失值_06

四,第三方包的介绍-tidyr

宽数据与长数据之间进行变换时,tidyr与reshape2都有相同的功能。
只不过除此之外,reshape2的dcast()还有部分数据聚合汇总的相关功能。

1,gather()宽数据转为长数据

R语言预处理geo代码 r语言数据预处理_第三方包_07


最主要的是key的生成,在…中选择的列,变换为key,以实现宽数据转为长数据。

例子:

stocks <- data_frame(
  time = as.Date('2009-01-01') + 0:9,
  X = rnorm(10, 0, 1),
  Y = rnorm(10, 0, 2),
  Z = rnorm(10, 0, 4)
)

gather(stocks, stock, price, -time)
stocks %>% gather(stock, price, -time)  #表示 time列保持原样,根据stock,price 重新建立k-v对关系的变量表示

R语言预处理geo代码 r语言数据预处理_数据_08

2,spread()长数据转为宽数据

类似于reshape2包中的cast函数,与 gather正好相反

R语言预处理geo代码 r语言数据预处理_第三方包_09

stocks <- data.frame(
  time = as.Date('2009-01-01') + 0:9,
  X = rnorm(10, 0, 1),
  Y = rnorm(10, 0, 2),
  Z = rnorm(10, 0, 4)
)
stocksm <- stocks %>% gather(stock, price, -time)
stocksm %>% spread(stock, price)

R语言预处理geo代码 r语言数据预处理_数据_10

3,unite() 多列合并为一列

R语言预处理geo代码 r语言数据预处理_缺失值_11

#直接合并
unite_(mtcars, "vs_am", c("vs","am"))
#把vs的值、am的值合,并且命名为 vs_am

R语言预处理geo代码 r语言数据预处理_R语言预处理geo代码_12

4,separate() 将一列分离为多列

R语言预处理geo代码 r语言数据预处理_数据_13

> df
     x
1 <NA>
2  a.b
3  a.d
4  b.c
> separate(df,x,c('a','b'))
     a    b
1 <NA> <NA>
2    a    b
3    a    d
4    b    c

五,第三方包的介绍-reshape2

reshape2 与 tidyr的功能类似,都是重塑数据变量的。

不过解决问题的思路缺失不同,reshape2首先更加melt处理原始数据,再运用cast来产生任意组合的数据模型,如下图所示。

R语言预处理geo代码 r语言数据预处理_第三方包_14

1,melt(data, …, na.rm = FALSE, value.name = “value”)

2,cast()

R语言预处理geo代码 r语言数据预处理_缺失值_15

3,tidyr vs reshape2

# 测试数据
set.seed(2018)
stocks <- data.frame(time = as.Date('2009-01-01') + 1:100000,
                     X = rnorm(100000, 0, 1),
                     Y = rnorm(100000, 0, 2),
                     Z = rnorm(100000, 0, 4))

stocksm <- gather(stocks, stock, price, -time)
head(stocksm)
  time         stock  price
1 2009-01-02     X   -0.42298398
2 2009-01-03     X   -1.54987816
3 2009-01-04     X   -0.06442932
4 2009-01-05     X    0.27088135
5 2009-01-06     X    1.73528367
6 2009-01-07     X   -0.26471121

# reshape2耗时
system.time(
  dat <-melt(stocksm, id=c("time", "stock"), na.rm=FALSE) 
  ) 
用户  系统  流逝 
0.004 0.001 0.005

system.time(
acast(dat, time ~ stock)
)
 用户  系统  流逝 
0.460 0.065 0.535

# tidyr耗时
system.time(
spread(stocksm, stock, price)
)
用户  系统  流逝 
0.135 0.030 0.166

综述,从效率上看,tidyr更高。不过reshape2灵活性更高,而且可以添加聚合函数。不过就像reshape2是reshape的简化版一样,tidyr也是reshape2的简化版。tidyr用来专注的解决一件事情,没有聚合函数混在其中。如果想对数据进行聚合,可以通过通道函数%>%来使用dplyr的聚合函数。

六,利用R语言进行数据预处理

前一节讲过,预处理的四个步骤,接下来演示相应的操作:

数据清洗:SINCE原则处理数据
数据集成:R自带函数与第三方包plyr,dplyr等
数据变换:R自带函数与第三方包plyr,dplyr等
数据规约:会放到第五章进行讲解

1,数据清洗

  • Simple原则: 发现重复与冗余
# 创建冗余数据
dat <- data_frame(x=rep(1,6),
                  y=seq(1,6)
                  )
dat1 <- rbind(dat,dat)[c(-1,-5,-6,-8),]
dat1
# 查看重复数据条数
sum(duplicated(dat1))
2
# 查看重复的数据是哪些
dat1[duplicated(dat1),]
    x     y
    1     3
    1     4

对于冗余,与数据规约的步骤是基本一致的,会合并到数据规约的步骤来计算。

  • Integral原则:处理缺失处理

处理缺失值的具体理论方法,参见第六章,此处仅介绍一下,如何发现以及填充缺失值。

R语言预处理geo代码 r语言数据预处理_R语言预处理geo代码_16


查看缺失值

# 创建缺失数据
dat <- data.frame(x=rep(1,6),
                  y=seq(1,6)
                  )
dat[c(3,6),c(1,2)] <- NA

# 有多少个缺失值
sum(is.na(dat))
4

统一填充缺失值

# 将缺失值统一填充为0

# 用赋值的方法
dat[is.na(dat)] <- 0

# 用替换的方法
replace(dat,is.na(dat),0)

# 用dplyr中的方法
dplyr::replace_na(dat,list(x=0,y=0))
mutate_at(dat,vars(x,y),funs(ifelse(is.na(.), 0, .)))  # 等价于
mutate_all(dat,funs(ifelse(is.na(.), 0, .)))

对于不同的列,动态指定不同列的填充值

# 使用循环与自定义函数
sapply(dat,function(x) ifelse(is.na(x),x[is.na(x)] <- mean(x,na.rm = TRUE),x))

剔除缺失值

# 方法一
na.omit(dat)

# 方法二
dat[complete.cases(dat),]
  • Normal原则:统一格式并标准化
    统一格式,基本就是修改并赋值的过程,此处不再讨论。

对于数值型变量,我们有两种方式,标准化:

归一化公式
R语言预处理geo代码 r语言数据预处理_数据_17

# 运用归一化化公式,自己算值
(dat-min(dat)) / (max(dat)-min(dat))

标准化公式
R语言预处理geo代码 r语言数据预处理_数据_18

# center—表示是否进行中心化,所有值减去均值
# scale—表示是否进行标准化,所有值除标准差

dat<-c(1, 2, 3, 4, 5, 6, 7)
# 使用scale标准化
scale(dat,center = TRUE, scale = TRUE)
         [,1]
[1,] -1.3887301
[2,] -0.9258201
[3,] -0.4629100
[4,]  0.0000000
[5,]  0.4629100
[6,]  0.9258201
[7,]  1.3887301
attr(,"scaled:center")
[1] 4
attr(,"scaled:scale")
[1] 2.160247

# 运用标准化公式,自己算值
(dat-mean(dat))/sd(dat)
-1.3887301 -0.9258201 -0.4629100  0.0000000  0.4629100  0.9258201  1.3887301

#结果一致
  • Consistent一致性原则:就要以业务与经验为主来判断
  • Effective有效原则:错误与异常
    对于异常值,我们最简单的方法是查看样本数据的分布来得到结果。根据箱线图的三个重要的分位数,在Q3+1.5IQR和Q1-1.5IQR的范围以外的,认为是异常值,或者叫做离群点。
#用 quantile() 函数来计算
q <- quantile(dat,c(1/4,1/2,3/4))
IQR = IQR(x) = quantile(x, 3/4) - quantile(x, 1/4) = q[[3]]-q[[1]]
O1 = q[[1]] - 1.5*IQR
O2 = q[[3]] + 1.5*IQR
dat[which(dat>O1 & dat<O2)]   # 去除异常点

此外,还有专门研究异常检测的方法论,可以参照第十八章异常检测,来进行学习。

2,数据集成:

系统自带函数

cbind()  #列合并
rbind()  #行合并
merge() #根据条件合并

# 创建数据
dat1 <- data.frame(x=c('a','b','c','d'),y=seq(1,4))
dat2 <- data.frame(x=c('a','c','d','e'),y=seq(5,8))

# 把dat2添加到dat2下方
rbind(dat1,dat2)
  x y
1 a 1
2 b 2
3 c 3
4 d 4
5 a 5
6 c 6
7 d 7
8 e 8

# 把dat2添加到da1右侧
 cbind(dat1,dat2)
  x y x y
1 a 1 a 5
2 b 2 c 6
3 c 3 d 7
4 d 4 e 8

# 类似于 inner join
merge(dat1,dat2,by=c('x','x'),suffixes = c('_dat1','_dat2'))
  x y_dat1 y_dat2
1 a      1      5
2 c      3      6
3 d      4      7

# left join
merge(dat1,dat2,by=c('x','x'),suffixes = c('_dat1','_dat2'),all.x = TRUE)

# right join
merge(dat1,dat2,by=c('x','x'),suffixes = c('_dat1','_dat2'),all.y = TRUE)

# full join
merge(dat1,dat2,by=c('x','x'),suffixes = c('_dat1','_dat2'),all = TRUE)

dplyr函数

bind_rows()   # 等价于 rbind() 
bind_cols()   # 等价于 cbind() 

left_join()
例子
left_join(dat1,dat2,by=c('x'='x'),suffix = c('_dat1','_dat2'))

right_join()  # 略
inner_join()  # 略
full_join()   # 略

3,数据变换:

将数值属性拆分成分类属性

dat = data.frame(x=c(12,3,5,3,5,6,7,8,2,5,8,1,4,8))

#将数据分为三组,并且命名组1、2、3; 
a = cut(dat$x,3,labels = c('组1','组2','组3'))

#将分组变量dummy化 
# 利用包
library(dummies)
dum=dummy(a)

#利用model.matrix()
b = factor(a)
dum = model.matrix(~b-1)  # 此处记得减1,处理截距项

#合并这些结果 
cbind(dat,a,dum)