R中的data.table包提供了一个data.frame的高级版本,让你的程序做数据整型的运算速度大大的增加。data.table已经在金融,基因工程学等领域大放光彩。他尤其适合那些需要处理大型数据集(比如 1GB 到100GB)需要在内存中处理数据的人。不过这个包的一些符号并不是很容易掌握,因为这些操作方式在R中比较少见。这也是这篇文章的目的,为了给大家提供一个速查的手册。
data.table的通用格式: DT[i, j, by],对于数据集DT,选取子集行i,通过by分组计算j
install.packages("data.table")
library(data.table)
利用fread函数导入数据,在data.table包支持使用fread函数从本地或者web上导入数据,功能相当于base包的read.csv。
mydata = fread("https://github.com/arunsrinivasan/satrdays-workshop/raw/master/flights_2014.csv")
nrow(mydata)
[1] 253316
ncol(mydata)
[1] 17
names(mydata)
[1] "year" "month" "day" "dep_time" "dep_delay" "arr_time" "arr_delay"
[8] "cancelled" "carrier" "tailnum" "flight" "origin" "dest" "air_time"
[15] "distance" "hour" "min"
head(mydata[,c(2:6)])
month day dep_time dep_delay arr_time
1: 1 1 914 14 1238
2: 1 1 1157 -3 1523
3: 1 1 1902 2 2224
4: 1 1 722 -8 1014
5: 1 1 1347 2 1706
6: 1 1 1824 4 2145
.()为list()的一个别名。如果使用.(),返回的为一个data.table对象。如果不使用.(),结果为返回一个向量。
如果要选择carrier列,那么可以选择如下几种方式实现:
dat1 = mydata[ , carrier] # 返回一组向量
dat1 = mydata[ , .(carrier)] #返回一个data.table
dat1 = mydata[, c("carrier"), with=FALSE] #返回一组数据框
根据列的位置保留某几列,比如选择第二列:
dat2 =mydata[, 2, with=FALSE]
保留多列:
dat3 = mydata[, .(origin, year, month, hour)]
根据列的位置保留多列:
dat3 = mydata[, .(origin, year, month, hour)]
删去列:利用!符号删除某列
dat5 = mydata[, !c("origin"), with=FALSE]
删去多列
dat6 = mydata[, !c("origin", "year", "month"), with=FALSE]
利用%like% 命令进行模糊匹配:
dat7 = mydata[,names(mydata) %like% "dep", with=FALSE]
可以利用setnames()函数对变量进行重命名操作:
setnames(mydata, c("dest"), c("Destination"))
对多个变量进行重命名:
setnames(mydata, c("dest","origin"), c("Destination", "origin.of.flight"))
假设要找到origin为‘JFK’的所有子集:
dat8 = mydata[origin == "JFK"]
按多个条件选择
dat9 = mydata[origin %in% c("JFK", "LGA")]
dat11 = mydata[origin == "JFK" & carrier == "AA"]
使用setkey()函数设置键值
setkey()函数可以在数据集mydata上设置键值。当我们设置好key后,data.table会将数据按照key来排序。
利用setkey函数将origin设置为mydata的索引:
setkey(mydata, origin)
当设置好索引后,可直接利用索引的值进行过滤查找
data12 = mydata[c("JFK", "LGA")]
来看看用了索引与没用索引的搜索效率:
system.time(mydata[origin %in% c("JFK", "LGA")])
system.time(mydata[c("JFK", "LGA")])
对多个变量设置索引
setkey(mydata, origin, dest)
mydata[.("JFK", "MIA")]
这等同于:
mydata[origin == "JFK" & dest == "MIA"]
mult参数
mult参数是用来控制i匹配到的哪一行的返回结果默认情况下会返回该分组的所有元素
返回匹配到键值所在列(V2列)所有行中的第一行
DT["A", mult ="first"]
返回匹配到键值所在列(V2列)所有行中的最后一行
DT["A", mult = "last"]
nomatch参数
nomatch参数用于控制,当在i中没有到匹配数据的返回结果,默认为NA,也能设定为0。0意味着对于没有匹配到的行将不会返回。
返回匹配到键值所在列(V2列)所有包含变量值A或D的所有行:
DT[c("A","D")]
变量值A匹配到了,而变量值D没有,故返回NA。
返回匹配到键值所在列(V2列)所有包含值A或D的所有行:
DT[c("A","D"), nomatch = 0]
因为nomatch参数,值D没有匹配到故不返回。
by=.EACHI参数
by=.EACHI允许按每一个已知i的子集分组,在使用by=.EACHI时需要设置键值
返回键值(V2列)中包含A或C的所有行中,V4列的总和。
DT[c("A","C"),
sum(V4)]
返回键值所在列(V2列)中包含A的行在V4列总和与包含C的行在V4列的总和。
DT[c("A","C"),
sum(V4), by=.EACHI]
使用setkey()设置一个多列主键
任意列都能使用setkey()来设置主键,这种方式可以选择2个列作为一个主键。以下是一个等值连接V1列的每个组先根据V1排序,再根据V2排序。
setkey(DT,V1,V2)
无显式返回结果
选择键值1(V1列)为2且键值2(V2列)为C的行。
DT[.(2,"C")]
选择键值1(V1列)为2且键值2(V2列)为A或C的行
DT[.(2,c("A","C"))]
利用setorder()函数可对数据进行排序:升序
mydata01 = setorder(mydata, origin)
对数据进行降序
mydata02 = setorder(mydata, -origin)
还可 基于多个变量进行排序
mydata03 = setorder(mydata, origin, -carrier)
在一行中使用:=引用来添加或更新列.
注意: 额外的指定 (DT <- DT[…])是多余的
使用:=来更新V1列:
> DT[, V1 := round(exp(V1),2)]
#这段代码没有显式的返回结果,而V1列从[1] 1 2 1 2 … 变成了 [1] 2.72 7.39 2.72 7.39 …
> DT
V1 V2 V3 V4
1: 2.72 A -0.8981 1
2: 7.39 B -0.3348 2
3: 2.72 C -0.5014 3
4: 7.39 A -0.1745 4
5: 2.72 B -0.8981 5
6: 7.39 C -0.3348 6
7: 2.72 A -0.5014 7
8: 7.39 B -0.1745 8
9: 2.72 C -0.8981 9
10: 7.39 A -0.3348 10
11: 2.72 B -0.5014 11
12: 7.39 C -0.1745 12
使用:=引用来添加或更新多列
使用:=更新V1列和V2列:
>DT[, c("V1","V2") := list(round(exp(V1),2), LETTERS[4:6])]
> DT
V1 V2 V3 V4
1: 15.18 D -0.8981 1
2: 1619.71 E -0.3348 2
3: 15.18 F -0.5014 3
4: 1619.71 D -0.1745 4
5: 15.18 E -0.8981 5
6: 1619.71 F -0.3348 6
7: 15.18 D -0.5014 7
8: 1619.71 E -0.1745 8
9: 15.18 F -0.8981 9
10: 1619.71 D -0.3348 10
11: 15.18 E -0.5014 11
12: 1619.71 F -0.1745 12
#同样没有显式的返回结果,V1列的结果与上相同,V2列从[1] “A” “B” “C” “A” “B” “C” … 变成: [1] “D” “E” “F” “D” “E” “F” …
使用函数:=
上例的另一种写法,但会在书写时更易并齐。而且,当添加[]时,结果会返回在屏幕中
> DT[, ':=' (V1 =round(exp(V1),2),V2 = LETTERS[4:6])][]
V1 V2 V3 V4
1: Inf D -0.8981 1
2: Inf E -0.3348 2
3: Inf F -0.5014 3
4: Inf D -0.1745 4
5: Inf E -0.8981 5
6: Inf F -0.3348 6
7: Inf D -0.5014 7
8: Inf E -0.1745 8
9: Inf F -0.8981 9
10: Inf D -0.3348 10
11: Inf E -0.5014 11
12: Inf F -0.1745 12
与上例变化相同,但是由于在语句最后添加了[],这一结果会返回至屏幕
通过使用:=来移除一列
移除V1列
> DT[, V1 := NULL]
> DT
V2 V3 V4
1: D -0.8981 1
2: E -0.3348 2
3: F -0.5014 3
4: D -0.1745 4
5: E -0.8981 5
6: F -0.3348 6
7: D -0.5014 7
8: E -0.1745 8
9: F -0.8981 9
10: D -0.3348 10
11: E -0.5014 11
12: F -0.1745 12
#无显式的返回结果,但V1列变为NULL
通过使用:=来移除多列
移除V1列与V2列
DT[, c("V1","V2") := NULL]
#无显式的返回结果,但V1列与V2列变为NULL
将一个包含列名的变量用小括号包裹起来,变量所传递的内容将会被删除
注意:列名为Cols.chosen的列将会被删除,这里不是删除”V1”,”V2”列
Cols.chosen = c("V1","V2")
DT[, Cols.chosen := NULL]
#无显式的返回结果,列名为Cols.chosen的列将会被删除
#删除指定变量Cols.chosen包含的V1列和V2列
DT[, (Cols.chosen) := NULL]
#无显式的返回结果,列名为V1和V2的列变为NULL
对原数据增加一列
mydata[, dep_sch:=dep_time - dep_delay]
增加多列
mydata002 = mydata[, c("dep_sch","arr_sch"):=list(dep_time - dep_delay, arr_time - arr_delay)]
mydata[, .(mean = mean(arr_delay, na.rm = TRUE),
median = median(arr_delay, na.rm = TRUE),
min = min(arr_delay, na.rm = TRUE),
max = max(arr_delay, na.rm = TRUE))]
mean median min max
1: 8.146702 -4 -112 1494
mydata[,.(sum(distance),sd(hour))]
V1 V2
1: 278507079 4.897891
若列的长度不一,则会循环对齐
选择V1这一列,并计算V3这列的标准差,将会得到一个标准差的值并循环补齐
DT <- data.table(V1=c(1L,2L),
V2=LETTERS[1:3],
V3=round(rnorm(4),4),
V4=1:12)
DT[,.(V1, Sd.V3 = sd(V3))]
V1 Sd.V3
1: 1 0.2810601
2: 2 0.2810601
3: 1 0.2810601
4: 2 0.2810601
5: 1 0.2810601
6: 2 0.2810601
7: 1 0.2810601
8: 2 0.2810601
9: 1 0.2810601
10: 2 0.2810601
11: 1 0.2810601
12: 2 0.2810601
多个表达式可以包裹在花括号中
输出V2这一列并绘制V3这一列
DT[,{print(V2)
plot(V3)
NULL}]
[1] "A" "B" "C" "A" "B" "C" "A" "B" "C" "A" "B" "C"
NULL
对多个变量做聚合计算
mydata[, .(mean(arr_delay), mean(dep_delay))]
如果要对大量的变量做聚合计算,可以使用.SD函数,和.SDcols函数。
mydata[, lapply(.SD, mean), .SDcols = c("arr_delay", "dep_delay")]
默认的,.SD函数指对所有变量进行计算
mydata[, lapply(.SD, mean)]
对多个变量实现多个统计指标计算
mydata[, sapply(.SD, function(x) c(mean=mean(x), median=median(x)))]
#按照单个变量分组
mydata[, .(mean_arr_delay = mean(arr_delay, na.rm = TRUE)), by = origin]
#按照多个变量分组
mydata[, .(mean_arr_delay = mean(arr_delay, na.rm = TRUE)), by = .(origin,carrier)]
在by中调用函数
以sign(V1-1)为分组,计算各个分组中V4列的和:
DT[,.(V4.Sum = sum(V4)),by=sign(V1-1)]
使用函数.N来得到每个类别的总观测数
在V1列中计算每个分组的观测数
DT[,.N,by=V1]
V1 N
1: 1 6
2: 2 6
.N
.N可以用来表示行的数量或者最后一行
在i处使用:
DT[.N-1]
V1 V2 V3 V4
1: 1 B -0.5765 11
返回每一列的倒数第二行
在j处使用:
DT[,.N-1]
[1] 11
返回倒数第二行所在的行数。
.()
.()是list()的一个别名,他们在data.table中是等价的。当只有一个元素的位置j或者by中,是不需要.()的。
在j中使用:
DT[,.(V2,V3)] #or DT[,list(V2,V3)]
V2 V3
1: A -0.8313
2: B 0.7615
3: C -0.5765
在by中使用:
DT[, mean(V3),by=.(V1,V2)]
V1 V2 V1
1: 1 A -0.70390
2: 2 B 0.06755
3: 1 C -0.70390
4: 2 A 0.06755
5: 1 B -0.70390
6: 2 C 0.06755
#以V1,V2为分组,对V3求均值
.SD参数
.SD是一个data.table,他包含了各个分组,除了by中的变量的所有元素。.SD只能在位置j中使用:
DT[, print(.SD), by=V2]
V1 V3 V4
1: 1 -0.8313 1
2: 2 -0.6264 4
3: 1 -0.5765 7
4: 2 0.7615 10
V1 V3 V4
1: 2 0.7615 2
2: 1 -0.8313 5
3: 2 -0.6264 8
4: 1 -0.5765 11
V1 V3 V4
1: 1 -0.5765 3
2: 2 0.7615 6
3: 1 -0.8313 9
4: 2 -0.6264 12
Empty data.table (0 rows) of 1 col: V2
以V2为分组,选择每组的第一和最后一列:
DT[,.SD[c(1,.N)], by=V2]
V2 V1 V3 V4
1: A 1 -0.8313 1
2: A 2 0.7615 10
3: B 2 0.7615 2
4: B 1 -0.5765 11
5: C 1 -0.5765 3
6: C 2 -0.6264 12
以V2为分组,计算.SD中所有元素的和:
DT[, lapply(.SD, sum), by=V2]
V2 V1 V3 V4
1: A 6 -1.2727 22
2: B 6 -1.2727 26
3: C 6 -1.2727 30
.SDcols
.SDcols常于.SD用在一起,他可以指定.SD中所包含的列,也就是对.SD取子集:
DT[, lapply(.SD,sum), by=V2,
+ .SDcols = c("V3","V4")]
V2 V3 V4
1: A -1.2727 22
2: B -1.2727 26
3: C -1.2727 30
#.SDcols也可以是一个函数的返回值:
DT[, lapply(.SD,sum), by=V2,
+ .SDcols = paste0("V",3:4)]
V2 V3 V4
1: A -1.2727 22
2: B -1.2727 26
3: C -1.2727 30
#结果与上一个是相同的。
把多个操作串联起来,这等价于SQL中的having
#这个是不使用串联的方法,先以V1为分组,对V4求和,然后再把分组总和大于35的取出来。
DT<-DT[, .(V4.Sum = sum(V4)),by=V1]
DT[V4.Sum > 35] #no chaining
V1 V4.Sum
1: 1 36
2: 2 42
使用串联的方法:
DT[, .(V4.Sum = sum(V4)),by=V1][V4.Sum > 35 ]
V1 V4.Sum
1: 1 36
2: 2 42
分组求和之后对V1进行排序:
DT[, .(V4.Sum = sum(V4)),by=V1][order(-V1)]
V1 V4.Sum
1: 2 42
2: 1 36
set()
set()通常用来更新给定的行和列的值,要注意的是,他不能跟by结合使用。
rows = list(3:4,5:6)
cols = 1:2
for (i in seq_along(rows))
+ {
+ set(DT,
+ i=rows[[i]],
+ j = cols[i],
+ value = NA)
+}
DT
V1 V2 V3 V4
1: 1 A -0.0559 1
2: 2 B -0.4450 2
3: NA C 0.0697 3
4: NA A -0.1547 4
5: 1 NA -0.0559 5
6: 2 NA -0.4450 6
7: 1 A 0.0697 7
8: 2 B -0.1547 8
以上程序把给定的一组行和列都设置为了NA
setname()
与set()同理,setname()可以修改给定的列名和行名,以下程序是
#把名字为"old"的列,设置为"new"
setnames(DT,"old","new")
#把"V2","V3"列,设置为"V2.rating","V3.DataCamp"
setnames(DT,c("V2","V3"),c("V2.rating","V3.DataCamp"))
setcolorder()
setcolorder()可以用来修改列的顺序。
setcolorder(DT,c("V2","V1","V4","V3"))
#这段代码会使得列的顺序变成:
[1] "V2" "V1" "V4" "V3"
举个栗子:
首先介绍下data.table的语法,如下所示:
在data.table包中,我们可以使用:=引用来添加或更新列
内置的 order() 函数 * 我们可以对一个字符型的列,使用减号“-”,来实现降序排列。
当我们用list()的时候,返回的是data.table,不用list()时,返回的是向量。一般建议加上list(),除非你就是想要得到向量格式的数据。
select取子集方法之subset(x, subset, select)
注:subset特指对列的选择,select特指对行的选择, with = FALSE 来引用列名
select列
既然列可以作为变量被引用,我们可以直接引用我们想选取的列。
既然我们想选取所有的行,则可毋需指定参数 i。
返回了所有行的 arr_delay 列。
特殊的语法
.SD: data.table提供一个特殊的语法,形式是 .SD。它是 Subset of Data 的缩写。
它自身就是一个data.table,包含通过by 分组后的每一组。 回忆一下,一个data.table本质上是一个list,它们的列包含的元素个数都相同(其实就是行数)。
说明:
说明:
.SDcols
使用参数 .SDcols。它接受列名或者列索引。比如,.SDcols = c(“arr_delay”, “dep_delay”)能确保.SD之包含 arr_delay 和 dep_delay 这两列。
和 with = FALSE 一样,我们也可以使用 - 或者 ! 来移除列。比如,我们指定 !(colA:colB) 或者 -(colA:colB)表示移除从 colA 到 colB 的所有列。
data.table的语法形式是:
DT[i, j, by]
指定参数i:
我们可以使用order()排序。为了得到更快速的效果,order()函数内部使用了data.table的快速排序。
我们可以通过参数i做更多的事,得到更快速的选取和连结。我们可以在教程“Keys and fast binary search based subsets”和“Joins and rolling joins”中学到这些。
指定参数j:
以data.table的形式选取列:DT[, .(colA, colB)]。
和i共同使用:DT[colA > value, sum(colB)]。
指定参数by:* 通过by,我们可以指定列,或者列名,甚至表达式,进行分组。参数j可以很灵活地配置参数i和by实现强大的功能。
by可以指定多个列,也可以指定表达式。
data.table包使用简介
data.table–cran
R–data.table介绍学习
R–data.table速查手册