简介
R语言中,自身已经带有了强大的数据处理、数据计算等方面的函数。
虽然,对于大规模的数据集合,处理过程可能会不如Python快,但是小规模的数据处理,R语言使用起来仍然会更方便。
值得注意的是,为了执行效率,我们要尽量避免在R语言中,使用循环函数,而是要运用向量化的处理函数,即R语言Base基础包中,
当然,记得apply家族的那么多函数以及不同的用法是一件麻烦的事情,于是类似plyr,dplyr,tidyr,reshape2等包相继出现,能够运用更加简化的函数来进行数据处理,同时又不会降低R语言的处理效率。
正文
一,数据处理中的基本函数
二,第三方包的介绍-plyr
plyr包,是用来实现Splitting, Applying and Combining Data的.
即,把原始数据经过条件拆分成数据子集—>子集经过函数逻辑的处理—>子集结果的合并的过程。
其动机在与提供超越for循环和内置的apply函数族的一个一揽子解决方案,高效的替代很多复杂的循环;
plyr包提供的*ply() 函数族如下:
一般情况下,我们做模型分析,需要输出的格式是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, 变量筛选(列操作)
2, 条件过滤(行操作)
3,添加变量或重命名
4,变量排序
arrange(mtcars, cyl, disp)
arrange(mtcars, desc(disp))
5,数据聚合与汇总
6,数据集合并
四,第三方包的介绍-tidyr
宽数据与长数据之间进行变换时,tidyr与reshape2都有相同的功能。
只不过除此之外,reshape2的dcast()还有部分数据聚合汇总的相关功能。
1,gather()宽数据转为长数据
最主要的是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对关系的变量表示
2,spread()长数据转为宽数据
类似于reshape2包中的cast函数,与 gather正好相反
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)
3,unite() 多列合并为一列
#直接合并
unite_(mtcars, "vs_am", c("vs","am"))
#把vs的值、am的值合,并且命名为 vs_am
4,separate() 将一列分离为多列
> 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来产生任意组合的数据模型,如下图所示。
1,melt(data, …, na.rm = FALSE, value.name = “value”)
2,cast()
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原则:处理缺失处理
处理缺失值的具体理论方法,参见第六章,此处仅介绍一下,如何发现以及填充缺失值。
查看缺失值
# 创建缺失数据
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原则:统一格式并标准化
统一格式,基本就是修改并赋值的过程,此处不再讨论。
对于数值型变量,我们有两种方式,标准化:
归一化公式
# 运用归一化化公式,自己算值
(dat-min(dat)) / (max(dat)-min(dat))
标准化公式
# 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)