加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

数据处理之——data.table

发布时间:2020-12-14 01:34:21 所属栏目:大数据 来源:网络整理
导读:data.table 简介 关于 R 中的数据处理,我们接着介绍 data.table 包。上期我们介绍的 dplyr 有类似于sql的一套数据处理的语法,而 data.table 则有着它自己的一套语法,它能处理几乎所有 dplyr 可以处理的数据,而且代码量更少,效率更高,特别是在处理大至G

data.table简介

关于R中的数据处理,我们接着介绍data.table包。上期我们介绍的dplyr有类似于sql的一套数据处理的语法,而data.table则有着它自己的一套语法,它能处理几乎所有dplyr可以处理的数据,而且代码量更少,效率更高,特别是在处理大至GB级别的数据时,它比dplyr以及python的pandas处理数据都要快。参见https://github.com/Rdatatable/data.table/wiki/Benchmarks-%3A-Grouping。


示例数据

我们继续沿用dplyr中使用到的示例数据集pgdat,这样可以方便做对比,如果你想要获取该数据集的源代码,可以通过点击微信公众号学习课堂中的案例数据得到。

pgdat <- as.data.table(pgdat)
pgdat
# ? ? ?rk player wl ? ? ? ? ? ? match gs min ? fgp fgm fga ? ?p3p p3m p3a
# ? 1: ?1 ? 库里 胜 ? 灰熊104-125勇士 ?1 ?30 62.5% ?15 ?24 ?52.6% ?10 ?19
# ? 2: ?2 ? 库里 胜 ? ? 马刺86-92勇士 ?1 ?35 59.1% ?13 ?22 ?44.4% ? 4 ? 9
# ? 3: ?3 ? 库里 胜 ? ?灰熊99-100勇士 ?1 ?34 31.8% ? 7 ?22 ?21.4% ? 3 ?14
# ? 4: ?4 ? 库里 胜 ? 马刺101-112勇士 ?1 ?36 57.9% ?11 ?19 ?42.9% ? 3 ? 7
# ? 5: ?5 ? 库里 负 森林狼124-117勇士 ?1 ?43 28.0% ? 7 ?25 ?28.6% ? 4 ?14
# ?--- ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
# 415: 49 ? 欧文 胜 ? ?魔术79-104骑士 ?1 ?21 33.3% ? 4 ?12 ? 0.0% ? 0 ? 4
# 416: 50 ? 欧文 胜 ? ?太阳97-101骑士 ?1 ?24 43.8% ? 7 ?16 ?28.6% ? 2 ? 7
# 417: 51 ? 欧文 负 ? ? 勇士89-83骑士 ?1 ?26 26.7% ? 4 ?15 ? 0.0% ? 0 ? 6
# 418: 52 ? 欧文 胜 ? 尼克斯84-91骑士 ?1 ?19 14.3% ? 1 ? 7 ? 0.0% ? 0 ? 1
# 419: 53 ? 欧文 胜 ? ?76人86-108骑士 ?1 ?17 41.7% ? 5 ?12 100.0% ? 2 ? 2
# ? ? ? ? ftp ftm fta reb oreb dreb ast stl blk tov pf pts
# ? 1: 100.0% ? 6 ? 6 ? 4 ? ?1 ? ?3 ? 6 ? 2 ? 0 ? 2 ?2 ?46
# ? 2: 100.0% ? 7 ? 7 ? 5 ? ?0 ? ?5 ? 5 ? 2 ? 0 ? 4 ?4 ?37
# ? 3: ? ? ? ? ?0 ? 0 ? 9 ? ?0 ? ?9 ? 8 ? 1 ? 0 ? 2 ?1 ?17
# ? 4: 100.0% ? 2 ? 2 ? 5 ? ?0 ? ?5 ? 9 ? 2 ? 0 ? 3 ?2 ?27
# ? 5: 100.0% ? 3 ? 3 ? 6 ? ?2 ? ?4 ?15 ? 3 ? 0 ? 3 ?4 ?21
# ?--- ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
# 415: ?71.4% ? 5 ? 7 ? 2 ? ?1 ? ?1 ? 5 ? 0 ? 0 ? 1 ?1 ?13
# 416: 100.0% ? 6 ? 6 ? 1 ? ?0 ? ?1 ? 2 ? 1 ? 0 ? 1 ?0 ?22
# 417: 100.0% ? 5 ? 5 ? 3 ? ?3 ? ?0 ? 2 ? 1 ? 0 ? 2 ?1 ?13
# 418: 100.0% ? 3 ? 3 ? 1 ? ?1 ? ?0 ? 4 ? 0 ? 0 ? 2 ?2 ? 5
# 419: ? ? ? ? ?0 ? 0 ? 1 ? ?0 ? ?1 ? 4 ? 2 ? 0 ? 1 ?1 ?12

1. 基本概念

a) data.table是什么?

我们知道R中的数据框data.frame,而data.table可以看做是一个增强版的data.frame。创建一个data.table对象可以使用fread函数以读入文件的形式生成,也可以直接采用data.table函数生成。

DT = data.tableID c("b","a",68);">"c"),
? ? ? ? ? ? ? ?a = 1:6,b 712,0);">c=1318DT
# ? ?ID a ?b ?c
# 1: ?b 1 ?7 13
# 2: ?b 2 ?8 14
# 3: ?b 3 ?9 15
# 4: ?a 4 10 16
# 5: ?a 5 11 17
# 6: ?c 6 12 18
classDT$ID)
# [1] "character"

as.data.table可以将data.frame转化为data.table

data.tabledata.frame的区别:
  • data.frames不同,在生成data.table的时候character类型的数据绝对不会默认地转换成factors类型,这样的好处就是你不用再在语句中每次都添加stringsAsFactors = FALSE了;

  • 打印数据时在行号后面加了一个冒号:用以区分行号与第一列数据;

  • 当数据行数超过options()参数设置datatable.print.nrows的值时,仅仅只会打印该数据集的前5行以及最后5行的数据;

# 查看默认参数设置
getOption"datatable.print.nrows"# [1] 100
  • data.table没有行名称。

b)?一般形式

为了让大家快速理解data.table语法的一般形式,先看看如下说明:

DT[i,0);">j,0);">by]
## ? R: ? i ? ? ?j ? ? ? ? ?by
## SQL: where select|update group by

看看上述描述,如果你了解sql语言,可能你就能很快明白data.table的语法。对于一个数据集DT,用i对数据的行数进行筛选,用j对数据的列进行筛选以及计算,用by来指定数据如何进行分组。接下来我们就来看看如何利用ij对数据进行处理。

c) 用i筛选行数

# 筛选出得分大于40并且助攻数大于7次的数据
ans pts > 40 & ast >= 8]headans# ? ?rk player wl ? ? ? ? ? match gs min ? fgp fgm fga ? ?p3p p3m p3a ? ?ftp# 1: 25 ? 库里 胜 魔术114-130勇士 ?1 ?34 74.1% ?20 ?27 ?66.7% ?10 ?15 100.0%# 2: 63 ? 库里 胜 太阳116-135勇士 ?1 ?31 55.0% ?11 ?20 ?56.2% ? 9 ?16 ?90.9%# 3: 77 ? 库里 胜 鹈鹕120-134勇士 ?1 ?36 63.0% ?17 ?27 ?57.1% ? 8 ?14 100.0%# 4: 31 ? 沃尔 负 勇士134-121奇才 ?1 ?35 68.0% ?17 ?25 100.0% ? 3 ? 3 ?66.7%# 5: 23 ? 威少 负 鹈鹕123-119雷霆 ?1 ?38 60.0% ?15 ?25 ?60.0% ? 3 ? 5 ?78.6%# 6: 69 ? 威少 胜 鹈鹕103-110雷霆 ?1 ?36 56.0% ?14 ?25 ? 0.0% ? 0 ? 3 ?75.0%# ? ?ftm fta reb oreb dreb ast stl blk tov pf pts
# 1: ? 1 ? 1 ? 7 ? ?0 ? ?7 ? 8 ? 0 ? 1 ? 5 ?1 ?51
# 2: ?10 ?11 ? 6 ? ?1 ? ?5 ? 8 ? 2 ? 0 ? 6 ?2 ?41
# 3: ?11 ?11 ? 4 ? ?0 ? ?4 ? 9 ? 4 ? 0 ? 2 ?3 ?53
# 4: ? 4 ? 6 ? 3 ? ?1 ? ?2 ?10 ? 2 ? 0 ? 3 ?4 ?41
# 5: ?11 ?14 ? 4 ? ?2 ? ?2 ? 9 ? 0 ? 0 ? 2 ?3 ?44
# 6: ?15 ?20 ? 9 ? ?3 ? ?6 ? 8 ? 1 ? 1 ? 4 ?1 ?43
– 提取pgdat的前两行数据
[2ans
# ? ?rk player wl ? ? ? ? ? match gs min ? fgp fgm fga ? p3p p3m p3a ? ?ftp
# 1: ?1 ? 库里 胜 灰熊104-125勇士 ?1 ?30 62.5% ?15 ?24 52.6% ?10 ?19 100.0%
# 2: ?2 ? 库里 胜 ? 马刺86-92勇士 ?1 ?35 59.1% ?13 ?22 44.4% ? 4 ? 9 100.0%
# 1: ? 6 ? 6 ? 4 ? ?1 ? ?3 ? 6 ? 2 ? 0 ? 2 ?2 ?46
# 2: ? 7 ? 7 ? 5 ? ?0 ? ?5 ? 5 ? 2 ? 0 ? 4 ?4 ?37

– 排序

我们可以直接用R里自带的函数order来完成排序。

# 把数据首先按得分降序排列,然后再按助攻数升序排列
order(-pts,0);">ast)# ? ?rk player wl ? ? ? ? ? ? match gs min ? fgp fgm fga ? p3p p3m p3a# 1: 77 ? 库里 胜 ? 鹈鹕120-134勇士 ?1 ?36 63.0% ?17 ?27 57.1% ? 8 ?14
# 2: 33 ? 库里 胜 ? 奇才121-134勇士 ?1 ?36 67.9% ?19 ?28 73.3% ?11 ?15
# 3: 25 ? 库里 胜 ? 魔术114-130勇士 ?1 ?34 74.1% ?20 ?27 66.7% ?10 ?15
# 4: 79 ? 威少 胜 ? 魔术136-139雷霆 ?1 ?48 47.2% ?17 ?36 20.0% ? 1 ? 5
# 5: 70 ? 库里 胜 森林狼116-129勇士 ?1 ?38 60.0% ?15 ?25 61.5% ? 8 ?13
# 6: ?1 ? 库里 胜 ? 灰熊104-125勇士 ?1 ?30 62.5% ?15 ?24 52.6% ?10 ?19
# ? ? ? ftp ftm fta reb oreb dreb ast stl blk tov pf pts
# 1: 100.0% ?11 ?11 ? 4 ? ?0 ? ?4 ? 9 ? 4 ? 0 ? 2 ?3 ?53
# 2: ?66.7% ? 2 ? 3 ? 7 ? ?1 ? ?6 ? 2 ? 3 ? 0 ? 7 ?3 ?51
# 3: 100.0% ? 1 ? 1 ? 7 ? ?0 ? ?7 ? 8 ? 0 ? 1 ? 5 ?1 ?51
# 4: ?81.3% ?13 ?16 ?11 ? ?5 ? ?6 ? 8 ? 1 ? 1 ? 6 ?3 ?48
# 5: 100.0% ? 8 ? 8 ? 5 ? ?0 ? ?5 ? 4 ? 2 ? 0 ? 3 ?3 ?46
# 6: 100.0% ? 6 ? 6 ? 4 ? ?1 ? ?3 ? 6 ? 2 ? 0 ? 2 ?2 ?46

在使用order进行排序时,默认是升序排列,如果需要降序排列,可以在列名前加上-
值得一提的是,在data.table里面用到的order实际上经过内部优化过了,它比R自带的order实际上要快不少,看如下例子:

# Win7 32 # R 3.2.1
odt col=sample(1e7## uses order from base R
t1 system.timeans1 odtbase::col]) ?
# ? ?user ?system elapsed
# ? 10.07 ? ?0.04 ? 10.15
## uses data.table's forder
t2 ans2 ) ? ? ? ?
# ? ?0.63 ? ?0.06 ? ?0.69
identicalans1,0);">ans2# [1] TRUE

上述测试在win7的32位系统下进行,内存大小为4GB,R采用的3.2.1版本,我们可以看到加速了~15倍

d) 用j选择列

– 选择pts列,返回向量
[,0);">pts]
# [1] 46 37 17 27 21 39
– 选择data.table
list# ? ?pts
# 1: ?46
# 2: ?37
# 3: ?17
# 4: ?27
# 5: ?21
# 6: ?39
  • 当我们用list()的时候,返回的是data.table,不用list()时,返回的是向量。一般建议加上list(),除非你就是想要得到向量格式的数据。

  • 选择列还可以使用.(),它list()的用法是完全一样的。

– 选择ptsast两列数据
# ? ?pts ast
# 1: ?46 ? 6
# 2: ?37 ? 5
# 3: ?17 ? 8
# 4: ?27 ? 9
# 5: ?21 ?15
# 6: ?39 ? 7
– 选择ast两列数据并且将它们重命名为pointsassists
points assists # ? ?points assists
# 1: ? ? 46 ? ? ? 6
# 2: ? ? 37 ? ? ? 5
# 3: ? ? 17 ? ? ? 8
# 4: ? ? 27 ? ? ? 9
# 5: ? ? 21 ? ? ?15
# 6: ? ? 39 ? ? ? 7

e) 用j来计算

– 计算直接得分和间接得分之和
# 间接得分简单记为助攻数*2
player,0);">tpts + 2*# ? ? ?player tpts
# ? 1: ? 库里 ? 58
# ? 2: ? 库里 ? 47
# ? 3: ? 库里 ? 33
# ? 4: ? 库里 ? 45
# ? 5: ? 库里 ? 51
# ?--- ? ? ? ? ? ?
# 415: ? 欧文 ? 23
# 416: ? 欧文 ? 26
# 417: ? 欧文 ? 17
# 418: ? 欧文 ? 13
# 419: ? 欧文 ? 20

f) 用i筛选数据并且用j做一些计算

– 计算库里的场均得分和助攻数
player == "库里",? ? ? ? ? ? ? .m_ptsmeanm_ast# ? ? ? m_pts ? ?m_ast
# 1: 30.06329 6.670886
– 计算得分大于等于20分并且助攻数大于等于10个的两双的总场次
20 10,0);">length# [1] 87

length用来计算数据的长度,这里的列名实际上选取任意一列都可以,data.table提供了一个相同功能的函数.N来实现,用来表示数据的行数。于是上述代码可以有以下等价表述方式:

.N# [1] 87

g) 用j通过字符串的变量名来选择数据列

使用参数with = FALSE

– 跟data.frame相同的方式选择"pts",68);">"ast"with=FALSE# 6: ?39 ? 7

2. 数据整合

介绍完了ij的用法,接下来介绍如何用by来实现分组数据计算。

a) 用by分组

– 计算不同球员的参赛场次
by=.player# ? ?player ?N
# 1: ? 库里 79
# 2: ? 康利 56
# 3: ? 保罗 74
# 4: ? 沃尔 77
# 5: ? 威少 80
# 6: ? 欧文 53
## 采用字符串形式
# ans <- pgdat[,.(.N),by="player"]
– 计算不同球员助攻大于等于8次的参赛场次
8,0);">.N,136); font-style: italic;"># 1: ? 库里 30
# 2: ? 康利 17
# 3: ? 保罗 54
# 4: ? 沃尔 62
# 5: ? 威少 61
# 6: ? 欧文 ?8
– 计算不同球员在不同胜负关系下助攻大于等于8次的参赛场次
wl# ? ? player wl ?N
# ?1: ? 库里 胜 28
# ?2: ? 库里 负 ?2
# ?3: ? 康利 胜 14
# ?4: ? 康利 负 ?3
# ?5: ? 保罗 胜 34
# ?6: ? 保罗 负 20
# ?7: ? 沃尔 负 29
# ?8: ? 沃尔 胜 33
# ?9: ? 威少 胜 44
# 10: ? 威少 负 17
# 11: ? 欧文 负 ?2
# 12: ? 欧文 胜 ?6
– 计算不同球员在不同胜负关系下场均得分与助攻数
## j设置参数名
# ? ? player wl ? ? ? V1 ? ? ? ?V2
# ?1: ? 库里 胜 30.39437 ?6.647887
# ?2: ? 库里 负 27.12500 ?6.875000
# ?3: ? 康利 负 13.95652 ?5.000000
# ?4: ? 康利 胜 16.24242 ?6.878788
# ?5: ? 保罗 胜 18.98000 10.240000
# ?6: ? 保罗 负 20.70833 ?9.416667
# ?7: ? 沃尔 负 18.80000 ?9.400000
# ?8: ? 沃尔 胜 21.05405 11.162162
# ?9: ? 威少 胜 22.30909 10.563636
# 10: ? 威少 负 26.04000 10.120000
# 11: ? 欧文 胜 20.08108 ?4.783784
# 12: ? 欧文 负 18.62500 ?4.562500
## j不设置参数名
mpts mast # ? ? player wl ? ? mpts ? ? ?mast
# 12: ? 欧文 负 18.62500 ?4.562500
  • j不提供参数名的时候,会自动生成类似(V1,?V2)的参数名。

b) keyby

如果在分组数据计算后,想要对分组数据进行排序,这时便可用到keyby参数。

– 计算不同球员在不同胜负关系下场均得分与助攻数,并按球员和胜负关系进行排序
keyby# ?1: ? 保罗 胜 18.98000 10.240000
# ?2: ? 保罗 负 20.70833 ?9.416667
# ?3: ? 康利 胜 16.24242 ?6.878788
# ?4: ? 康利 负 13.95652 ?5.000000
# ?5: ? 库里 胜 30.39437 ?6.647887
# ?6: ? 库里 负 27.12500 ?6.875000
# ?7: ? 欧文 胜 20.08108 ?4.783784
# ?8: ? 欧文 负 18.62500 ?4.562500
# 11: ? 沃尔 胜 21.05405 11.162162
# 12: ? 沃尔 负 18.80000 ?9.400000

c) 管道操作

data.table中也有类似dplyr%>%功能的函数,下面我们来看看这个功能的好处:

# 按球员分组求出场均得分和助攻,再按得分降序排列
mptsmastby = .mpts# ? ?player ? ? mpts ? ? ?mast
# 1: ? 库里 30.06329 ?6.670886
# 2: ? 威少 23.47500 10.425000
# 3: ? 沃尔 19.88312 10.246753
# 4: ? 欧文 19.64151 ?4.716981
# 5: ? 保罗 19.54054 ?9.972973
# 6: ? 康利 15.30357 ?6.107143
# 使用管道操作
# 6: ? 康利 15.30357 ?6.107143

可以看出,data.table中的管道操作是按照DT[ ... ][ ... ][ ... ]的形式进行操作的。

e) 用.SD同时对多列进行相同计算

– 计算每一个球员的场均数据
# 提取球员,得分,助攻,抢断,失误数据列
ast,0);">stl,0);">tov# 按球员分组计算其他所有数据列的均值
lapply.SD,136); font-style: italic;"># ? ?player ? ? ?pts ? ? ? ast ? ? ?stl ? ? ?tov
# 1: ? 库里 30.06329 ?6.670886 2.139241 3.316456
# 2: ? 康利 15.30357 ?6.107143 1.196429 1.500000
# 3: ? 保罗 19.54054 ?9.972973 2.054054 2.621622
# 4: ? 沃尔 19.88312 10.246753 1.883117 4.129870
# 5: ? 威少 23.47500 10.425000 2.037500 4.275000
# 6: ? 欧文 19.64151 ?4.716981 1.056604 2.339623

采用.SD会对除分组数据外所有的数据列做处理,而实际处理数据的时候,往往是需要指定其中多列数据进行处理,并不是全部数据列,这时候就需要配合.SDcols参数进行操作。例子如下:

# 按球员分组仅计算得分和助攻数据的均值
.SDcols]
## 用.SDcols对球员的得分和助攻数据进行处理
# ? ?player ? ? ?pts ? ? ? ast
# 2: ? 康利 15.30357 ?6.107143
# 3: ? 保罗 19.54054 ?9.972973
# 4: ? 沃尔 19.88312 10.246753
# 5: ? 威少 23.47500 10.425000
# 6: ? 欧文 19.64151 ?4.716981
– 取每个球员前两场的数据
# ? ? player pts ast
# ?1: ? 库里 ?46 ? 6
# ?2: ? 库里 ?37 ? 5
# ?3: ? 康利 ?22 ? 4
# ?4: ? 康利 ?12 ? 4
# ?5: ? 保罗 ?12 ?13
# ?6: ? 保罗 ? 5 ?11
# ?7: ? 沃尔 ?15 ?13
# ?8: ? 沃尔 ?22 ?10
# ?9: ? 威少 ?13 ?14
# 10: ? 威少 ?24 ?10
# 11: ? 欧文 ?35 ? 5
# 12: ? 欧文 ?11 ? 8
– 把每个球员的得分和助攻数连起来作为一个变量
# 新变量为一列
val # ? ? ?player val
# ? 1: ? 库里 ?46
# ? 2: ? 库里 ?37
# ? 3: ? 库里 ?17
# ? 4: ? 库里 ?27
# ? 5: ? 库里 ?21
# ?--- ? ? ? ? ?
# 834: ? 欧文 ? 5
# 835: ? 欧文 ? 2
# 836: ? 欧文 ? 2
# 837: ? 欧文 ? 4
# 838: ? 欧文 ? 4

## 新变量为列表
# ? ?player ? ? ? ? ? ? ? ?val
# 1: ? 库里 46,37,27,21,39,
# 2: ? 康利 22,12,24,7,19,136); font-style: italic;"># 3: ? 保罗 12,5,13,25,20,136); font-style: italic;"># 4: ? 沃尔 15,22,14,8,136); font-style: italic;"># 5: ? 威少 13,23,26,136); font-style: italic;"># 6: ? 欧文 35,11,15,10,

小结

data.table的一般语法形式如下:

]
i选择行:
  1. 按条件筛选行数据:DT[colA > value1 & colB < value2]

  2. 排序:DT[order(colA,-colB)]

j选择列和计算:
  1. 选择数据列:?DT[,.(colA,colB)]?或者?.

  2. 计算:?.

  3. 配合i一起使用:?DT[colA > value,sum(colB)].

by数据分组:
  1. 分组计算汇总:

  2. 配合ij一起使用:DT[col > val,by=.]

至此,data.table的基本函数功能就介绍完了。 最后在这里说说自己使用data.table以及dplyr进行数据处理的区别感受,在使用dplyr进行数据处理时的处理逻辑层次比较清晰,毕竟类似sql的操作习惯比较容易接受,其代码整体可读性更好;而用data.table的时候,代码量更少,处理速度更快。总体来说,在处理小数据的时候更倾向于使用dplyr,在处理数据量比较大的情形时,更倾向于使用data.table,当然这最终看你个人的使用习惯。

data.table还有一些其它的功能,比如表关联操作,快速读写文件的函数fread以及fwrite等等,更多功能就留给大家自己去探索吧。


?本文来自№→★飞刀的微信公共帐号“数据挖掘与R语言”,用微信添加数据挖掘与R语言公众号,即可订阅。转载必须保留作者、公共帐号信息。

长按识别图中二维码?


(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读