1 Star 7 Fork 1

zhongyf/Rbook

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
01-datatable.Rmd 28.09 KB
一键复制 编辑 原始数据 按行查看 历史
zhongyf 提交于 2021-05-26 18:47 . commit
# data.table{#Data-Manipulation-datatable} data.table包是我数据处理最常用的R包,是我目前觉得最好用的数据处理包,大部分我需要用到的功能集成在包里,不需要很多的依赖包。我简单接触过python,julia两种语言,并没有深入比较,所以我这个好用的印象仅仅是个人感受。 data.table包是我用了较长一段时间tidyverse系列后发现的“数据处理包”。已经忘记最初是什么吸引了我,我猜测可能是“大数据处理利器”之类的标签吸引了我,因为我喜欢“快”。但是和大部分人可能不同的是,初次接触时,语法的“怪异”并没有给我带来多少麻烦,因为我本来就没有编程基础以及很深的R语言基础。 所以我死记硬背data.table里一些常用用法,尤其喜欢拿Excle的一些用法参照,去实现Excle上面的部分操作,从读取、增、改、删除、筛选、计算列等常规操作入手。慢慢熟悉data.table语法之后,将会享受data.table带来的便利,其简洁的语法以及高效的计算速度(相比tidyverse系列)。 另外,Python中也有该包,目前正在积极开发中,期待ing,毕竟python也是很好用,在不同需求下选择不同的语言实现功能。 官方关于data.table的基础介绍请参阅: https://cran.r-project.org/web/packages/data.table/vignettes/datatable-intro.html data.table 优势: - 速度快 - 内存效率高 - API生命周期管理好 - 语法简洁 >本文会照搬很多官方关于data.table的demo ## 基础介绍{#datatable:description} 本部分从data.table安装,内置的案例查看,到data.table的句式语法,实现基础行列筛选和聚合计算。 1.安装 安装详细信息请参考[the Installation wiki](https://github.com/Rdatatable/data.table/wiki/Installation),有关于不同系统安装首次以及相关说明。 ```{r eval=FALSE} install.packages("data.table") # latest development version: data.table::update.dev.pkg() ``` 2.使用说明 通过以下代码查看内置的使用案例。 ```{r eval=FALSE} library(data.table) example(data.table) ``` ### 读取数据 在我实际工作中接触的数据大部分以数据库,csv,Excel等形式存在,并且CSV格式数据较少。但是data.table包读取数据的`fread`函数仅接受CSV格式。如果是Excel格式文件,需要通过如`readxl`,`openxlsx`等包读入后转换为`data.table`格式数据。 fread 函数可以直接读取CSV格式文件,无论是本地文件或者在线文件,如下所示: >案例中使用的数据集是R包`nycflights13`带的flights数据集。 ```{r} library(data.table) input 、 =、<=分别代表大于、小于、等于、不等于、大于等于、小于等于。常用的逻辑运算符 &、|、!等。 ```{r eval=FALSE} #单条件筛选 filghts[year == 2014] #筛选year==2014 #多条件筛选 用 & 链接 flights[ year == 2014 & month == 6] # | 相当于中文条件或 flights[ month == 5 | month == 6] # %in% 类似sql中in用法 flights[month %in% c(1,3,5,7,9)] # %between% 类似sql中between and 用法 flights[month %between% c(1,7)] ``` #### j列操作 数据集较大、字段较多时,由于无效信息较多可以做适当精选,这时需要我们筛选列。与sql中的select用法一致,即保留想要的字段。 .()或list()是data.table中的比较特殊的实现列筛选的用法。常规数字索引,字符向量索引同样有效。 ```{r} #注意前面的. .() flights[,.(year,month,day,dep_delay,carrier,origin)] # flights[,list(year,month,day,dep_delay,carrier,origin)] same above # not run # flights[,1:3] # not run # flights[,c('year','month','day')] ``` setcolorder函数可以调整列的顺序,将常用的字段信息排在前面可以用过该函数实现。 ```{r} # not run # setcolorder(x = flights,neworder = c( "month","day","dep_delay" ,"arr_delay","carrier" )) # 按照指定列顺序排序 其余字段保持不变,不是建立副本,是直接修改了flights 数据的列顺序 ``` - 常规计算 根据最开始的Excel透视表截图,我们想要获得如截图一样的结果该怎么实现呢?代码如下: ```{r,eval=FALSE} flights[year==2014 & month==6,.(求和项distance=sum(distance),平均距离=mean(distance)),by=.(origin,dest)] ``` 在i的位置做筛选,j的位置做计算,by指定分组字段。在j的位置可以做各种各样的计算,R中自带的函数,或者是自己定义的函数。 ```{r} myfun 6)] #by里面可以做计算 head(dt) ``` ### 行列筛选总结 行筛选在 i 的位置上进行, 列筛选在 j 的位置上进行;data.table中j的位置比较灵活多变,但是i的位置大部分时候都是进行条件筛选。我们通过上述的行列筛选已经大概知道data.table中i,j的用法。也就是我们常规数据清洗过程中的数据筛选过程,筛选符合要求的数据记录。 ```{r} dt =15,.(year,month,day,dep_delay,carrier,origin)] head(dt) ``` ## 常规操作{#datatable:usage} ### 行筛选 上文已经大致讲过行筛选,但是行筛选使用有一定的技巧,涉及到运算的快慢。主要是逻辑条件的设置,交集并集之间的差异。除了上文中的关系运算筛选,逻辑运算筛选除外,data.table中还有几个常用的筛选函数。 - 数字向量筛选 %in%用法与 sql 中 in 用法类似。 ```{r eval=FALSE} # 筛选 %in% flights[ hour %in% seq(1,24,2) ] ``` - 字符向量筛选 %chin%用法与 %in% 类似,但仅仅针对字符。 ```{r eval=FALSE} # 字符筛选 flights[ origin %chin% c('JFK','LGA')] # not run 同上 %chin% 对字符速度筛选速度更快 #flights[ origin %in% c('JFK','LGA')] ``` - between 筛选 该函数的新特性矢量化挺实用。 ```{r} #between 函数参数 #between(x, lower, upper, incbounds=TRUE, NAbounds=TRUE, check=FALSE) X Note: DT[a > 4, b := c] is different from DT[a > 4][, b := c] ### 排序 当我们清洗数据时,我们需要将数据框排序,我们可以使用`setorder`或`setorderv`函数实现排序。函数是`data.table`包的函数,比base R 中的`order`函数要节省内存。 注意:按照函数文档说法:Note that queries like x[order(.)] are optimised internally to use data.table's fast order。即x[order(.)]这样的用法会被优化为data.table的排序方法。 ```{r} set.seed(45L) DT = data.table(A=sample(3, 10, TRUE), B=sample(letters[1:3], 10, TRUE), C=sample(10)) setorder(DT, A, -B) #将DT按照A、B排序 A 升序,-B降序 # 和上面同样的效果 但是函数变成 setorderv setorderv(DT, c("A", "B"), c(1, -1)) ``` ## 常用函数{#datatable:function} 常用函数指我们常用功能的函数,如排名、排序、非重复计数、判断、表连接、长宽转换等功能。 ### 特殊符号 .SD,.BY,.N,.I,.NGRP和.GRP,.SDcols等,只能用在 j 的位置,.N 可以用在 i 的位置。 如果想要记住用法需要自己多尝试练习,对于我来说.N使用较多。 ```{r} DT = data.table(x=rep(c("b","a","c"),each=3), v=c(1,1,1,2,2,1,1,2,2), y=c(1,3,6), a=1:9, b=9:1) DT X = data.table(x=c("c","b"), v=8:7, foo=c(4,2)) X # 用在i的位置 DT[.N] #取DT最后一行,.N 计数函数 DT[,.N] #DT 共有多少行记录 返回一个整数 DT[, .N, by=x] #分组计数 DT[, .SD, .SDcols=x:y] # 选择x 到y 列 #DT[, .SD, .SDcols=c("x","y")] 与上面不一样 DT[, .SD[1]] #取第一行 DT[, .SD[1], by=x] #按x列分组后 DT[, c(.N, lapply(.SD, sum)), by=x] #按照x分组后 行数计数和每列求和 ``` ### 排序函数 `frank`和`frankv`函数参数如下: ```{r eval=FALSE} frank(x, ..., na.last=TRUE, ties.method=c("average", "first", "last", "random", "max", "min", "dense")) frankv(x, cols=seq_along(x), order=1L, na.last=TRUE, ties.method=c("average", "first", "random", "max", "min", "dense")) ``` 官方案例,如下所示: ```{r} # on vectors x = c(4, 1, 4, NA, 1, NA, 4) # NAs are considered identical (unlike base R) # default is average frankv(x) # na.last=TRUE frankv(x, na.last=FALSE) # on data.table DT = data.table(x, y=c(1, 1, 1, 0, NA, 0, 2)) frankv(DT, cols="x") # same as frankv(x) from before frankv(DT, cols="x", na.last="keep") frankv(DT, cols="x", ties.method="dense", na.last=NA) frank(DT, x, ties.method="dense", na.last=NA) # equivalent of above using frank ``` * frankv在排序时,NA被认为是一样的,基础base R 中认为不一样. ```{r} x 2L, x, x - 1L) fifelse(x > 2L,fifelse(x >= 4L,x + 1L,x),x-1L) ``` - fcase 与sql中的case when,与dplyr中的`case_when()`函数用法相似。相比fifelse相比,嵌套更加方便。 ```{r} x = 1:10 fcase( x < 5L, 1L, x > 5L, 3L ) # not run 两种函数实现方式 fifelse(x > 5,fifelse(x >8,2,1),0) fcase( x > 8,2, x > 5,1, default = 0 ) ``` ### 交集 差集 合并 相当于base R 中 union(),intersect(),setdiff() 和setequal() 功能.all参数控制如何处理重复的行,和SQL中不同的是,data.table将保留行顺序. ```{r eval=FALSE} fintersect(x, y, all = FALSE) fsetdiff(x, y, all = FALSE) funion(x, y, all = FALSE) fsetequal(x, y, all = TRUE) x % setorderv(cols=c('area','grouping'),na.last = TRUE)) ``` 通过上述计算,发现计算结果与Excel透视表一样。 - cube 观察`cube()`计算结果与`rollup()`差异,发现`cube()`聚合层次更多。 ```{r} cube(DT,j = sum(value),by = c("area","store_type"),id = TRUE) ``` - groupingsets 根据需要指定指定聚合的层次。 ```{r} # 与本例中rollup 结果一致 groupingsets(DT,j = sum(value),by = c("area","store_type"),sets = list('area',c("area","store_type"), character()),id = TRUE) # 与本例中cube 结果一致 groupingsets(DT,j = sum(value),by = c("area","store_type"),sets = list('area',c("area","store_type"),"store_type", character()),id = TRUE) ``` > groupingsets: sets参数,用list()包裹想要聚合的字段组合,最后character(),加上该部分相当于不区分层级全部聚合,用法类似sql中"()". > SELECT brand, size, sum(sales) FROM items_sold GROUP BY GROUPING SETS ((brand), (size), ()); [comment]: <> (This is a comment, it will not be included) ### rleid 该函数根据分组生成长度列。 即将0011001110111101类似这种分组成1 1 2 2 3 3 4 4 4 5 6 6 6 6 7 8。在特定时候是很便捷的一个函数。如在计算股票连续上涨或下跌天数时。 ```{r} rleid(c(0,0,1,1,0,0,1,1,1,0,1,1,1,1,0,1)) ``` 用法: ```{r eval=FALSE} rleid(..., prefix=NULL) rleidv(x, cols=seq_along(x), prefix=NULL) ``` ```{r} DT = data.table(grp=rep(c("A", "B", "C", "A", "B"), c(2,2,3,1,2)), value=1:10) rleid(DT$grp) # get run-length ids rleidv(DT, "grp") # same as above rleid(DT$grp, prefix="grp") # prefix with 'grp' ``` ### shift 向前或向后功能,通俗来说就是向前或向后移动位置。 示例如下: ```{r} x = 1:5 # lag with n=1 and pad with NA (returns vector) shift(x, n=1, fill=NA, type="lag") ``` 其中参数n控制偏移量,n正负数和type的参数相对应。, n=-1 and type='lead' 与 n=1 and type='lag'效果相同。 在data.table上使用: ```{r} DT = data.table(year=2010:2014, v1=runif(5), v2=1:5, v3=letters[1:5]) cols = c("v1","v2","v3") anscols = paste("lead", cols, sep="_") DT[, (anscols) := shift(.SD, 1, 0, "lead"), .SDcols=cols] ``` 例如求某人连续消费时间间隔天数时: ```{r} DT = data.table(dates =lubridate::ymd(c(20210105,20210115,20210124,20210218,20210424))) DT[,newdate:=shift(dates)] DT ``` 通过构造新列newdate,然后将两列相减`dates-newdate`即可得到每次购物间隔天数。 ### J J 是`.()`,`list()`等的别名。`SJ`是排序连接,`CJ`是交叉连接。 用法: ```{r eval=FALSE} # DT[J(...)] # J() only for use inside DT[...] # DT[.(...)] # .() only for use inside DT[...] # DT[list(...)] # same; .(), list() and J() are identical SJ(...) # DT[SJ(...)] CJ(..., sorted=TRUE, unique=FALSE) # DT[CJ(...)] ``` - CJ 我喜欢用`CJ()`函数创建笛卡尔积表。例如在商品运营中,时常需要将门店和商品形成笛卡尔积表,相比起`dplyr::full_join()` ,`data.table::merge.data.table(allow.cartesian = TRUE )`,`CJ`更加方便快捷。 ```{r} # CJ usage examples CJ(c(5, NA, 1), c(1, 3, 2)) # sorted and keyed data.table # do.call(CJ, list(c(5, NA, 1), c(1, 3, 2))) # same as above # CJ(c(5, NA, 1), c(1, 3, 2), sorted=FALSE) # same order as input, unkeyed ``` - SJ SJ : Sorted Join. The same value as J() but additionally setkey() is called on all columns in the order they were passed to SJ. For efficiency, to invoke a binary merge rather than a repeated binary full search for each row of i. ## 小技巧{#datatable:tips} ### 用{}抑制中间过程输出 默认只返回未命名花括号中定义的最后一个对象。 ```{r} dt
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
R
1
https://gitee.com/zhongyufei/Rbook.git
git@gitee.com:zhongyufei/Rbook.git
zhongyufei
Rbook
Rbook
master

搜索帮助

23e8dbc6 1850385 7e0993f3 1850385