加载data.table包后,运行example(“data.table”)可以获得data.table自带的例子代码。本文对代码的意思进行了中文注释,以深入理解data.table的特性。
data.table入门介绍,见这里。相关中文翻译,见本博客日志:
其实本部分内容在早期日志data.table学习笔记5中翻译了一部分,没有翻译完。本次作为一个新的日志,全新翻译了一次。
library(data.table)
1.生成数据集
生成DF和DT数据集,类型分别为data frame和data.table。二者本质上均为list类型。
DF = data.frame(x = rep(c("b", "a", "c"), each = 3),
y = c(1, 3, 6),
v = 1:9)
DT = data.table(x = rep(c("b", "a", "c")
, each = 3),
y = c(1, 3, 6),
v = 1:9)
DF
## x y v
## 1 b 1 1
## 2 b 3 2
## 3 b 6 3
## 4 a 1 4
## 5 a 3 5
## 6 a 6 6
## 7 c 1 7
## 8 c 3 8
## 9 c 6 9
DT
## x y v
## 1: b 1 1
## 2: b 3 2
## 3: b 6 3
## 4: a 1 4
## 5: a 3 5
## 6: a 6 6
## 7: c 1 7
## 8: c 3 8
## 9: c 6 9
identical(DF$a, DT$a)
## [1] TRUE
is.list(DF)
## [1] TRUE
is.list(DT)
## [1] TRUE
2.行操作
DT[2] # 提取第二行数据
## x y v
## 1: b 3 2
DT[3:2] # 提取第三行和第二行数据
## x y v
## 1: b 6 3
## 2: b 3 2
DT[order(x)] # 根据x升序对DT进行排序
## x y v
## 1: a 1 4
## 2: a 3 5
## 3: a 6 6
## 4: b 1 1
## 5: b 3 2
## 6: b 6 3
## 7: c 1 7
## 8: c 3 8
## 9: c 6 9
DT[order(x), ] # 这样写也可以,不如上一行简洁
## x y v
## 1: a 1 4
## 2: a 3 5
## 3: a 6 6
## 4: b 1 1
## 5: b 3 2
## 6: b 6 3
## 7: c 1 7
## 8: c 3 8
## 9: c 6 9
DT[y>2] # 提取所有y>2 的行
## x y v
## 1: b 3 2
## 2: b 6 3
## 3: a 3 5
## 4: a 6 6
## 5: c 3 8
## 6: c 6 9
DT[y>2 & v>5] # 提取满足条件:y> 2 并且 v> 5的行
## x y v
## 1: a 6 6
## 2: c 3 8
## 3: c 6 9
DT[!2:4] #提取除2, 3,4行外的其他行
## x y v
## 1: b 1 1
## 2: a 3 5
## 3: a 6 6
## 4: c 1 7
## 5: c 3 8
## 6: c 6 9
DT[-(2:4)] #同上
## x y v
## 1: b 1 1
## 2: a 3 5
## 3: a 6 6
## 4: c 1 7
## 5: c 3 8
## 6: c 6 9
3.列操作
DT[, v] #提取列名为v的该列所有值,返回一个向量
## [1] 1 2 3 4 5 6 7 8 9
DT[, list(v)] #提取列名为v的该列所有值,返回一个data.table
## v
## 1: 1
## 2: 2
## 3: 3
## 4: 4
## 5: 5
## 6: 6
## 7: 7
## 8: 8
## 9: 9
DT[, .(v)] #同上,.()是list()的缩写
## v
## 1: 1
## 2: 2
## 3: 3
## 4: 4
## 5: 5
## 6: 6
## 7: 7
## 8: 8
## 9: 9
DT[, sum(v)] #列v所有值的和,返回一个向量
## [1] 45
DT[, .(sum(v))] #同上,返回一个data.table
## V1
## 1: 45
DT[, .(sv=sum(v))] #同上,但列被命名为sv
## sv
## 1: 45
DT[, .(v, v*2)] #返回一个data.table,包括两列:v和V2
## v V2
## 1: 1 2
## 2: 2 4
## 3: 3 6
## 4: 4 8
## 5: 5 10
## 6: 6 12
## 7: 7 14
## 8: 8 16
## 9: 9 18
4.对部分行进行列操作
譬如,仅对2,3行v列进行求和操作。
DT[2:3, sum(v)]
## [1] 5
DT[2:3, .(sum(v))]
## V1
## 1: 5
DT[2:3, .(sv=sum(v))]
## sv
## 1: 5
DT[2:5, cat(v, "\n")]
## 2 3 4 5
## NULL
5.通过data frame方式选择列
DT[, 2] #通过数字方式选择列,返回一个data.table
## y
## 1: 1
## 2: 3
## 3: 6
## 4: 1
## 5: 3
## 6: 6
## 7: 1
## 8: 3
## 9: 6
col_num = 2
DT[, ..col_num] #..后跟随变量表示该变量名不是DT中的某一列,它存储的值为DT中的列名。
## y
## 1: 1
## 2: 3
## 3: 6
## 4: 1
## 5: 3
## 6: 6
## 7: 1
## 8: 3
## 9: 6
DT[["y"]] #同DT[, y],返回向量,但读取速度更快
## [1] 1 3 6 1 3 6 1 3 6
6.分组操作
DT[, sum(v), by=x] #根据x进行分组,每组求v列的和
## x V1
## 1: b 6
## 2: a 15
## 3: c 24
DT[, sum(v), keyby=x] #同上,但输出结果根据x排序
## x V1
## 1: a 15
## 2: b 6
## 3: c 24
DT[, sum(v), by=x][order(x)] #同上,通过链式表达式操作
## x V1
## 1: a 15
## 2: b 6
## 3: c 24
7.快速读取行
通过二级索引形式,通过二分法快速查询。
DT["a", on = "x"] # 获取x列中值为a的所有行,跟DT[x == "a"]结果一致,但是对列x建立主键,二分法搜索,更快
## x y v
## 1: a 1 4
## 2: a 3 5
## 3: a 6 6
DT["a", on = .(x)] #更加方便的形式,不用写双引号
## x y v
## 1: a 1 4
## 2: a 3 5
## 3: a 6 6
DT[.("a"), on = .(x)] #同上
## x y v
## 1: a 1 4
## 2: a 3 5
## 3: a 6 6
DT[x == "a"] #同上,然而针对单个==内部进行了优化,速度同上
## x y v
## 1: a 1 4
## 2: a 3 5
## 3: a 6 6
DT[x != "b" | y != 3] #没有进行优化,仍然采用向量扫描提取子集
## x y v
## 1: b 1 1
## 2: b 6 3
## 3: a 1 4
## 4: a 3 5
## 5: a 6 6
## 6: c 1 7
## 7: c 3 8
## 8: c 6 9
DT[.("b", 3), on = c("x", "y")] # 对两列x和y设置二级索引,利用二分法查找,速度快
## x y v
## 1: b 3 2
DT[.("b", 3), on = .(x, y)] #同上, .()代替
## x y v
## 1: b 3 2
DT[.("b", 1:2), on = c("x", "y")] #没有匹配返回NA,y中没有2,因此返回的v值为NA
## x y v
## 1: b 1 1
## 2: b 2 NA
DT[.("b", 1:2), on = c("x", "y"), nomatch = NULL] #没有匹配的行,不返回值
## x y v
## 1: b 1 1
DT[.("b", 1:2), on = c("x", "y"), roll = Inf] # 没有匹配,返回上一行值
## x y v
## 1: b 1 1
## 2: b 2 1
DT[.("b", 1:2), on = .(x, y), roll = -Inf] #没有匹配,返回下一行值
## x y v
## 1: b 1 1
## 2: b 2 2
DT["b", sum(v * y), on = "x"] # 对于所有x=="b"的行, 列v与y相乘,求和,注意返回的是向量
## [1] 25
8.综合小练习
DT[x != "a", sum(v), by = x] # 提取x不等于"a"的所有行,根据x进行分组,计算每一组v列数值之和
## x V1
## 1: b 6
## 2: c 24
DT[!"a", sum(v), by = .EACHI, on = "x"] # 同上,.EACHI 代表x列中每一类元素
## x V1
## 1: b 6
## 2: c 24
DT[c("b", "c"), sum(v), by = .EACHI, on = "x"] #同上
## x V1
## 1: b 6
## 2: c 24
DT[c("b", "c"), sum(v), by = .EACHI, on = .(x)] #同上
## x V1
## 1: b 6
## 2: c 24
9.数据集连接、合并等操作
X = data.table(x = c("c", "b"),
v = 8:7,
foo = c(4, 2)) #创建一个新的数据集
X
## x v foo
## 1: c 8 4
## 2: b 7 2
DT[X, on = "x"] # 把DT并入x,根据X中的x列,优先并入DT中其他列,X中的剩余列在最后,如果有跟DT重复的列名,加i.作为前缀;加i指明了这一列来自X
## x y v i.v foo
## 1: c 1 7 8 4
## 2: c 3 8 8 4
## 3: c 6 9 8 4
## 4: b 1 1 7 2
## 5: b 3 2 7 2
## 6: b 6 3 7 2
X[DT, on = "x"] # 把X并入DT,根据DT中的x列,优先并入X中其他列,DT中的剩余列在最后,如果有跟X重复的列名,加i.作为前缀;加i指明了这一列来自DT,不匹配的用NA填充
## x v foo y i.v
## 1: b 7 2 1 1
## 2: b 7 2 3 2
## 3: b 7 2 6 3
## 4: a NA NA 1 4
## 5: a NA NA 3 5
## 6: a NA NA 6 6
## 7: c 8 4 1 7
## 8: c 8 4 3 8
## 9: c 8 4 6 9
X[DT, on = "x", nomatch = NULL] #屏蔽不匹配的行
## x v foo y i.v
## 1: b 7 2 1 1
## 2: b 7 2 3 2
## 3: b 7 2 6 3
## 4: c 8 4 1 7
## 5: c 8 4 3 8
## 6: c 8 4 6 9
DT[!X, on = "x"] #不合并数据集
## x y v
## 1: a 1 4
## 2: a 3 5
## 3: a 6 6
DT[X, on = c(y = "v")] #通过DT中的y列与X中的v列进行连接
## x y v i.x foo
## 1: <NA> 8 NA c 4
## 2: <NA> 7 NA b 2
DT[X, on = "y==v"] #同上
## x y v i.x foo
## 1: <NA> 8 NA c 4
## 2: <NA> 7 NA b 2
DT[X, on = .(y <= foo)] # 只要DT中y列的值小于等于X列中foo值,两行就连接在一起;注意再返回的结果,y列实际是X中foo列的值
## x y v i.x i.v
## 1: b 4 1 c 8
## 2: b 4 2 c 8
## 3: a 4 4 c 8
## 4: a 4 5 c 8
## 5: c 4 7 c 8
## 6: c 4 8 c 8
## 7: b 2 1 b 7
## 8: a 2 4 b 7
## 9: c 2 7 b 7
DT[X, on = "y<=foo"] #同上
## x y v i.x i.v
## 1: b 4 1 c 8
## 2: b 4 2 c 8
## 3: a 4 4 c 8
## 4: a 4 5 c 8
## 5: c 4 7 c 8
## 6: c 4 8 c 8
## 7: b 2 1 b 7
## 8: a 2 4 b 7
## 9: c 2 7 b 7
DT[X, on = c("y<=foo")] #同上
## x y v i.x i.v
## 1: b 4 1 c 8
## 2: b 4 2 c 8
## 3: a 4 4 c 8
## 4: a 4 5 c 8
## 5: c 4 7 c 8
## 6: c 4 8 c 8
## 7: b 2 1 b 7
## 8: a 2 4 b 7
## 9: c 2 7 b 7
DT[X, on = c("y>=foo")] #只要DT中y列的值大于等于X列中foo值,两行就连接在一起;注意再返回的结果,y列实际是X中foo列的值
## x y v i.x i.v
## 1: b 4 3 c 8
## 2: a 4 6 c 8
## 3: c 4 9 c 8
## 4: b 2 2 b 7
## 5: b 2 3 b 7
## 6: a 2 5 b 7
## 7: a 2 6 b 7
## 8: c 2 8 b 7
## 9: c 2 9 b 7
DT[X, on = .(x, y <= foo)] #依据共有x列(即x==x)和y<=foo两个条件,把DT并入X中;
## x y v i.v
## 1: c 4 7 8
## 2: c 4 8 8
## 3: b 2 1 7
DT[X, .(x, y, x.y, v), on = .(x, y >= foo)] # 当X是一个data.table时,x.y 指的是DT中的y;i.x值得时X中的x
## x y x.y v
## 1: c 4 6 9
## 2: b 2 3 2
## 3: b 2 6 3
DT[X, on = "x", mult = "first"] #返回每个组的第一行
## x y v i.v foo
## 1: c 1 7 8 4
## 2: b 1 1 7 2
DT[X, on = "x", mult = "last"] #返回每个组的最后一行
## x y v i.v foo
## 1: c 6 9 8 4
## 2: b 6 3 7 2
DT[X, sum(v), by = .EACHI, on = "x"] #基于连接后数据集,依据x分组评估每个组的v列值之和
## x V1
## 1: c 24
## 2: b 6
DT[X, sum(v) * foo, by = .EACHI, on = "x"] #原理同上,依据x分组评估每个组的v列值之和与foo之积
## x V1
## 1: c 96
## 2: b 12
DT[X, sum(y) * foo, on = .(x, v >= v), by = .EACHI] #连接条件:DT与X中x列相等,DT中v列大于X中v列
## x v V1
## 1: c 8 36
## 2: b 7 NA
DT[X, on = .(x)]
## x y v i.v foo
## 1: c 1 7 8 4
## 2: c 3 8 8 4
## 3: c 6 9 8 4
## 4: b 1 1 7 2
## 5: b 3 2 7 2
## 6: b 6 3 7 2
DT[X, on = .(x, v >= v)]
## x y v foo
## 1: c 3 8 4
## 2: c 6 8 4
## 3: b NA 7 2
10.设置和使用主键
kDT = copy(DT) #深层拷贝
setkey(kDT, x) # 设置主键为x列
setkeyv(kDT, "x") # 同上v表示向量
v = "x"
setkeyv(kDT, v) #同上
#key(kDT) <- v 已经废弃
haskey(kDT) #判断是否存在主键
## [1] TRUE
key(kDT) #列出主键
## [1] "x"
kDT["a"] # 根据x==a进行过滤
## x y v
## 1: a 1 4
## 2: a 3 5
## 3: a 6 6
kDT["a", on = "x"] #更明晰的写法
## x y v
## 1: a 1 4
## 2: a 3 5
## 3: a 6 6
kDT[!"a", sum(v), by = .EACHI] #依据x列中元素分组(不包括"a",每一组求v列值之和
## x V1
## 1: b 6
## 2: c 24
setkey(kDT, x, y) #设置多列作为主键
setkeyv(kDT, c("x", "y")) #同上
kDT[.("a", 3:6)] # 根据x和y列进行过滤
## x y v
## 1: a 3 5
## 2: a 4 NA
## 3: a 5 NA
## 4: a 6 6
11.特殊符号
主要包括:.N (行数), .SD (所有列), .SDcols (指定列), .I (行序列), .GRP (分组序列)等。
DT[.N] #.N总行数。返回最后一行
## x y v
## 1: c 6 9
DT[, .N] #返回总行数
## [1] 9
DT[, .N, by = .(x)] #返回每一组行数
## x N
## 1: b 3
## 2: a 3
## 3: c 3
DT[, .SD, .SDcols = x:y] #.SD表示选择的特定列,DT数据集中从x到y列之间的所有列,包括x和y
## x y
## 1: b 1
## 2: b 3
## 3: b 6
## 4: a 1
## 5: a 3
## 6: a 6
## 7: c 1
## 8: c 3
## 9: c 6
DT[, .SD, .SDcols = !x:y] #选择特定的列,剔除DT数据集中从x到y列之间的所有列,包括x和y
## v
## 1: 1
## 2: 2
## 3: 3
## 4: 4
## 5: 5
## 6: 6
## 7: 7
## 8: 8
## 9: 9
DT[, .SD, .SDcols = patterns('^[xv]')] # 通过模式匹配选择特定的列。返回列名中包括x或者v的数据集。可以模式匹配是个好东西!
## x v
## 1: b 1
## 2: b 2
## 3: b 3
## 4: a 4
## 5: a 5
## 6: a 6
## 7: c 7
## 8: c 8
## 9: c 9
DT[, .SD[1]] #返回第一行
## x y v
## 1: b 1 1
DT[, .SD[1], by = x] #返回每一组中的第一行
## x y v
## 1: b 1 1
## 2: a 1 4
## 3: c 1 7
DT[, c(.N, lapply(.SD, sum)), by = x] #根据x分组。获得每一个分组的行数,以及y和v列之和
## x N y v
## 1: b 3 10 6
## 2: a 3 10 15
## 3: c 3 10 24
DT[, .I[1], by = x] #定义每一行的顺序编号。每一组中第一行的顺序编号
## x V1
## 1: b 1
## 2: a 4
## 3: c 7
DT[, grp := .GRP, by = x] #定义每一组顺序编号
DT
## x y v grp
## 1: b 1 1 1
## 2: b 3 2 1
## 3: b 6 3 1
## 4: a 1 4 2
## 5: a 3 5 2
## 6: a 6 6 2
## 7: c 1 7 3
## 8: c 3 8 3
## 9: c 6 9 3
DT[, grp := .GRP, keyby = x] #x排好序,再定义组号
DT
## x y v grp
## 1: a 1 4 1
## 2: a 3 5 1
## 3: a 6 6 1
## 4: b 1 1 2
## 5: b 3 2 2
## 6: b 6 3 2
## 7: c 1 7 3
## 8: c 3 8 3
## 9: c 6 9 3
X[, DT[.BY, y, on = "v"], by = v] # .BY 接受v分组后两个值(8,7),对DT进行过滤,获取y值。返回一个data.table,其中v来自X,V1来自DT中的y。
## v V1
## 1: 8 3
## 2: 7 1
12.通过reference方式增加/更新/删除列
DT[, z := 42L] #通过reference 方式增加z列,值为42
DT
## x y v grp z
## 1: a 1 4 1 42
## 2: a 3 5 1 42
## 3: a 6 6 1 42
## 4: b 1 1 2 42
## 5: b 3 2 2 42
## 6: b 6 3 2 42
## 7: c 1 7 3 42
## 8: c 3 8 3 42
## 9: c 6 9 3 42
DT[, z := NULL] #删除z列
DT["a", v := 42L, on = "x"] #对列x中值为a的行,修改列v的值为42
DT["b", v2 := 84L, on = "x"] #对列x中值为b的行,修改列v的值为84
DT[, m := mean(v), by = x][] #根据x分组,求每组v列均值,作为新列m
## x y v grp v2 m
## 1: a 1 42 1 NA 42
## 2: a 3 42 1 NA 42
## 3: a 6 42 1 NA 42
## 4: b 1 1 2 84 2
## 5: b 3 2 2 84 2
## 6: b 6 3 2 84 2
## 7: c 1 7 3 NA 8
## 8: c 3 8 3 NA 8
## 9: c 6 9 3 NA 8
13.高级用法
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[, sum(v), by = .(y %% 2)] # by中也可以指定表达式。 %%-余数,分组0和1
## y V1
## 1: 1 9
## 2: 0 4
DT[, sum(v), by = .(bool = y %% 2)] #同上,改变分组列名为bool
## bool V1
## 1: 1 9
## 2: 0 4
DT[, .SD[2], by = x] #获得每个分组的第二行
## x v y a b
## 1: b 1 3 2 8
## 2: a 2 3 5 5
## 3: c 2 3 8 2
DT[, tail(.SD, 2), by = x] #获得每个分组的最后两行
## x v y a b
## 1: b 1 3 2 8
## 2: b 1 6 3 7
## 3: a 2 3 5 5
## 4: a 1 6 6 4
## 5: c 2 3 8 2
## 6: c 2 6 9 1
DT[, lapply(.SD, sum), by = x] #对每个分组,每一列求和
## x v y a b
## 1: b 3 10 6 24
## 2: a 5 10 15 15
## 3: c 5 10 24 6
DT[, .SD[which.min(v)], by = x] #每个分组中v最小值多在行集合
## x v y a b
## 1: b 1 1 1 9
## 2: a 1 6 6 4
## 3: c 1 1 7 3
DT[, list(MySum = sum(v),
MyMin = min(v),
MyMax = max(v)),
by = .(x, y %% 2)] # 统计每个组合(x和y%%2)的v列和,最大和最小值
## x y MySum MyMin MyMax
## 1: b 1 2 1 1
## 2: b 0 1 1 1
## 3: a 1 4 2 2
## 4: a 0 1 1 1
## 5: c 1 3 1 2
## 6: c 0 2 2 2
DT[, .(a = .(a), b = .(b)), by = x] #这个有意思,a和b列的属性是列表
## x a b
## 1: b 1,2,3 9,8,7
## 2: a 4,5,6 6,5,4
## 3: c 7,8,9 3,2,1
DT[, .(seq = min(a):max(b)), by = x] # 返回的seq列值:b-1:9, a-4:6, c-7:3
## x seq
## 1: b 1
## 2: b 2
## 3: b 3
## 4: b 4
## 5: b 5
## 6: b 6
## 7: b 7
## 8: b 8
## 9: b 9
## 10: a 4
## 11: a 5
## 12: a 6
## 13: c 7
## 14: c 6
## 15: c 5
## 16: c 4
## 17: c 3
DT[, sum(v), by = x][V1 < 20] #根据x分组,对v求和,然后对新数据,筛选和小于20的行
## x V1
## 1: b 3
## 2: a 5
## 3: c 5
DT[, sum(v), by = x][order(-V1)] #对新数据集降序排列
## x V1
## 1: a 5
## 2: c 5
## 3: b 3
DT[, c(.N, lapply(.SD, sum)), by = x] #汇总每个分组的行数,每一列的和
## x N v y a b
## 1: b 3 3 10 6 24
## 2: a 3 5 10 15 15
## 3: c 3 5 10 24 6
DT[, {
tmp <- mean(y)
.(a = a - tmp, b = b - tmp)
}, by = x] # 这个高级,可以自定义小函数
## x a b
## 1: b -2.3333333 5.6666667
## 2: b -1.3333333 4.6666667
## 3: b -0.3333333 3.6666667
## 4: a 0.6666667 2.6666667
## 5: a 1.6666667 1.6666667
## 6: a 2.6666667 0.6666667
## 7: c 3.6666667 -0.3333333
## 8: c 4.6666667 -1.3333333
## 9: c 5.6666667 -2.3333333
DT[, plot(a, b), by = x] # 也可以直接画图,牛
## Empty data.table (0 rows and 1 cols): x
DT[, c(.(y = max(y)), lapply(.SD, min)), by = rleid(v), .SDcols = v:b] #这个也很复杂 rleid函数功能类似.GRP
## rleid y v y a b
## 1: 1 6 1 1 1 7
## 2: 2 3 2 1 4 5
## 3: 3 6 1 1 6 3
## 4: 4 6 2 3 8 1