r语言中v1=c(v1i),R语言学习笔记之——数据处理神器data.table

原标题:R语言学习笔记之——数据处理神器data.table

报 名

数据处理在数据分析流程中的地位相信大家都有目共睹,也是每一个数据从业者面临的最为繁重的工作任务。

在实际应用场景下,虽然SQL(SQL类专业的etl语言)是数据处理的首选明星语言,性能佳、效率高、容易培养数据思维,但是SQL没法处理构建全流程的数据任务,之后仍然需要借助其他数据分析工具来对接更为深入的分析任务。

R语言作为专业的统计计算语言,数据处理是其一大特色功能,事实上每一个处理任务在R语言中都有着不止一套解决方案(这通常也是初学者在入门R语言时,感觉内容太多无从下手的原因),当然这些不同方案确实存在着性能和效率的绝大差异。

合理选择一套自己的数据处理工具组合算是挺艰难的选择,因为这个涉及到使用习惯和迁移成本的问题,比如你先熟知了R语言的基础绘图系统,在没有强大的驱动力的情况下,你可能不太愿意画大把时间去研究ggplot2,你用会写for/while循环,就不太愿意去掌握apply组函数,甚至那些性能逆天的并行算运算包;刚开始会用基础字符串处理,看到stringr包就面临着技能工具更新的问题……

太多的选择,让人眼花缭乱,我自己也遇到过这种困惑,为了避免注意力分散,我的做法是先做可能性罗列——罗列一个可以实现同类功能的所有工具清单并做一套功能卡(也算是初步了解)。然后根据自己掌握的现状选择最熟练的一套,随着时间的推移慢慢发现现有工具组合的不足,开始尝试往更加高效、简介的工具迁移,这样以需求为推动力的技能升级和迁移更为彻底和明确。

最典型的几个技能组合迁移如下:

基础字符串处理函数——stringr绘图系统:plot——ggplot2代码风格:函数嵌套——管道函数(`%>%`)列表处理:list(自建循环)——rlistjson处理:Rjson+RJSONIO——jsonlite数据抓取:RCurl+XML——httr+xml2循环任务:for/while——apply——plyr::a_ply——并行运算(foreach、parallel)切片索引:subset——dplyr::select+filter聚合运算:aggregate——plyr::ddply+mutate——dplyr::group_by+summarize数据联结:merge——plyr::join——dplyr::left/right/inner/outer_join数据塑型:plyr::melt/dcast——tidyr::gather/spread

……

其实还有很多类型的同类功能组合技能升级的路径,不一给出,虽然工具迁移确实面临着很高昂的代价,特别是时间成本、学习成本,但是迁移之后获得的高效、代码简洁的体验还是很爽的,以上特别是管道函数的迁移感触最深,再也不存在自己写完的东西间歇性懵逼的场景了。

说了这么多,绕了这么大的弯子想干啥呢,没错今天又要给自己升级新技能啦,这次的主角儿是

data.table

一个R语言高性能数据处理包,一个包可以涵盖以上所说的数据处理的大部分内容,而且操作高度抽象化话(抽象化就意味着代码量少的可怕)。

其实很早就接触过data.table,之所以一直没有深入应用,因为它的理念与其他数据处理包偏离太远,可以说迁移成本很高,几乎就是技能重构而非迁移。

不过随着视野的开阔,发现确实有必要深入了解这个高性能包,尽管有点儿颠覆R的传统风格,但是性能和效率的提升可以弥补这一点。

data.table

1、I/O性能:

data.table的被推崇的重要原因就是他的IO吞吐性能在R语言诸多包中首屈一指,这里以一个1.6G多的2015年纽约自行车出行数据集为例来检验其性能到底如何,希望我的小米本能扛得住折腾~_~

#清空内存

rm(list=ls())gc()#使用传统的I/O函数read_csv2进行导入:

setwd("D:/Python/citibike-tripdata/")system.time( mydata1

object.size(mydata1)

1914019808bytes数据量还是很大的,将近1.6G,900多万记录,16个字段。

70dcd6a8eab2b89af046dbbf50dc4fdc.png

可怜的机器呀,内存和磁盘要撑爆了~

使用data.table内的I/O函数进行导入:

rm(list=ls())gc()

library("data.table")system.time( mydata1

五倍效率,45秒钟900万1.606G的数据,还是很有说服力的(虽然没有传说中的十倍性能)。

rm(list=ls())gc()2、索引切片聚合

data.table中提供了将行索引、列切片、分组功能于一体的数据处理模型。

DT[i,j,by]

如果这个过程是SQL中是由select …… from …… where …… groupby …… having 来完成的,在R的其他基础包中起码也是分批次完成的。

dplyr::fliter() %>% select() %>% group_by() %>% summarize()

虽然可以借助管道函数进行代码优化,但是仍然无法与data.table的简洁想抗衡。

mydata

这里使用一个在线数据集,包含2014年纽约机场发出的所有航班信息。

class(mydata)[1] "data.table""data.frame"

31cf11e4306974f705a7dcdffbd857df.png

使用fread函数导入之后便会自动转化为data.table对象,这是data.table所特有的高性能数据对象,同时继承了data.frame传统数据框类,也意味着他能囊括很多数据框的方法和函数调用。

str(mydata)一共253316条记录,17个字段。

“year” 航班日期——年

“month” 航班日期——月

“day” 航班日期——天

“dep_time” 航班起飞时间

“dep_delay” 航班延误时长

“arr_time” 航班到达时间

“arr_delay” 航班到达延误时间

“cancelled” 航班是否取消

“carrier”

“tailnum”

“flight”

“origin” 起飞地

“dest” 目的地

“air_time”

“distance” 距离

“hour”

“min”

d91c6664266d72ff4df0d0c8f65e5d8f.png

data.table行索引carrier

tailnum

origin

dest

data.table列索引

列索引与数据框相比操作体验差异比较大,data.table的列索引摒弃了data.frame时代的向量化参数,而使用list参数进行列索引。

mydata[,list(carrier,tailnum)]

ca189179495f8d6e5d7535a30dece43d.png

为了操作体验更佳,这里的list可以简化为一个英文句点符号。即:

mydata[,.(carrier,tailnum)]

#但心里要清楚列索引接受的条件是含有列表的列表,而且这里的列表作为变量给出,而非data.frame时代的字符串向量。

行列同时索引毫无压力。

mydata[carrier %in% c("AA","AS"),.(carrier,tailnum)]

fac7d4774916e5b9f1ca1818b11cf992.png

列索引的位置不仅支持列名索引,可以直接支持内建函数操作。

mydata[,.(flight/1000,carrier,tailnum)]

支持直接在列索引位置新建列,赋值符号为:=。

mydata[,delay_all := dep_delay+arr_delay]

#销毁某一列:

mydata[,delay_all := NULL]

7340dfc78c24902d6871fa2ce34cb91a.png

19d57c26c4a58caa988ad1d4f6a0c52c.png

批量新建列:

mydata[,c("delay_all","delay_dif") := .((dep_delay+arr_delay),(dep_delay-arr_delay))]等价于写法2:mydata[,`:=`(delay_all = dep_delay+arr_delay,delay_dif =dep_delay-arr_delay )]

#销毁新建列:

mydata[,c("delay_all","delay_dif") := NULL]

ff29e2498b7b471ee98aec5a3440002e.png

b3caba69a33f92038f7c83c38a3226f9.png

注意以上新建列时,如果只有一列,列名比较自由,写成字符串或者变量都可以,但是新建多列,必须严格按照左侧列名为字符串向量,右侧为列表的模式,当然你也可以使用第二种写法。

DT[,`:=`(varname1 = statement1 ,varname1 = statement2)]

可以直接使用data.table内建的函数。

mydata[carrier %in% c("AA","AS"),.N][1] 26876

.N是一个计数函数,相当于plyr中的count,或者基础函数中的length。

基本的统计函数都可以直接支持。

mydata[carrier %in% c("AA","AS"),.(sum(dep_delay),mean(arr_delay))] V1 V2

1: 2289135.263841

mydata[carrier %in% c("AA","AS"),.(dep_delay,mean(arr_delay))]mydata[carrier %in% c("AA","AS") & dep_delay %between% c(500,1000),.(dep_delay,arr_delay)]

7ca84e9d9cb8d997068e12b39bd9cc5b.png

当整列和聚合的单值同时输出时,可以支持自动补齐操作。

当聚合函数与data.table中的分组参数一起使用时,data.table的真正威力才逐渐显露。

mydata[,.(sum(dep_delay),mean(arr_delay)),by = carrier]

78d1c12fe05c67415504bea98ed90644.png

多分组聚合。

mydata[,.(sum(dep_delay),mean(arr_delay)),by = .(carrier,origin)]

f9119689f894020b94900fd73ce7c5a8.png

多分组计数。

mydata[,.N,by = .(carrier,origin)]

87f4efabb14c49d8493e01d911a2acb9.png

自定义名称:

mydata[,.(dep_delay_sum = sum(dep_delay),arr_delay_mean = mean(arr_delay)),by = carrier]mydata[,.(dep_delay_sum = sum(dep_delay),arr_delay_mean = mean(arr_delay)),by = .(carrier,origin)]mydata[,.(carrier_n = .N),by = .(carrier,origin)]

dd105f3a3201494b76180b2f85cdfd52.png

数据排序:

排序行:

setorder(mydata,carrier,-arr_delay)

setorder函数作用于mydata本身,运行无输出。如果想要运行的同时进行输出则可以在结尾加上[]

setorder(mydata,carrier,-arr_delay)[]

9e62ca2bc58c59f4a109529795d84355.png

这个功能有点儿类似于基础函数中,在语句外部加上圆括号。(a

排序列:

sample(names(mydata),length(names(mydata))) [1] "arr_time""air_time""distance""dep_time""dest""arr_delay""month""min""tailnum""origin"

[11] "year""hour""cancelled""flight""day""carrier""dep_delay"

setcolorder(mydata,sample(names(mydata),length(names(mydata))))mydata[carrier == "AA", lapply(.SD, mean), by=.(carrier,origin,dest), .SDcols=c("arr_delay","dep_delay") ]

97f88e25338d5cc26e98c0ee4ab6e836.png

e2de8945f707e34e75cdc13d0f593c94.png

以上语法加入了新的参数.SDcols和.SD,咋一看摸不着头脑,其实是在按照carrier,origin,dest三个维度分组的基础上,对每个子块特定列进行均值运算。

这里的执行逻辑是这样的:

by=.(carrier,origin,dest) 先按照三个维度进行全部的分组;.SDcols=c("arr_delay","dep_delay")则分别在筛选每一个子数据块儿上的特定列;lapply(.SD, mean)则将各个子块的对应列应用于均值运算,并返回最终的列表。数据合并:

data.table的数据合并方式非常简洁;

DT

设置各自的主键:

setkey(DT,x)setkey(DX,z)DT[DX]

efb648a405ce9c81c87fbb55df1d5c32.png

就是如此简单,连接的执行逻辑是,内侧是左表,外侧是右表,所以是DX left join DT

如果没有设置主键,需要显式声明内部的on参数,指定连接主键,单主键必须在左右表中名称一致。

当然你要是特别不习惯这种用法,还是习惯使用merge的话,data.table仍然是支持的,因为他本来就继承了数据框,支持所有针对数据框的函数调用。

本文转自:数据小魔方

-END-

87b65cfe14ce59bf8f959d4c002a142b.png

图文来自网络、如涉及版权问题,请联系我们以便处理。文章内容纯属作者个人观点,不代表本网观点。返回搜狐,查看更多

责任编辑:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值