R包data.table详情介绍
该教程介绍了
data.table
语法,其一般形式,如何对行进行子集,如何在列上进行选择和计算以及如何按组进行聚合。熟悉基础函数data.frame
生成数据结构对阅读这篇教程很有用,但不是必需的。
1 安装
install.packages("data.table")
2 基础用法
# DT是指用data.table()所产生的data,下同
DT[i, j, by]
对DT
,用子/重新排序行i
,然后计算j
,按分组by
。
2.1 与data.frame的不同
2.1.1 数据生成与展示
DT = data.table(
ID = c("b","b","b","a","a","c"),
a = 1:6,
b = 7:12,
c = 13:18)
DX = data.frame(
ID = c("b","b","b","a","a","c"),
a = 1:6,
b = 7:12,
c = 13:18)
> class(DT$ID)
[1] "character"
> class(DX$ID)
[1] "factor"
> DT$ID
[1] "b" "b" "b" "a" "a" "c"
> DX$ID
[1] b b b a a c
Levels: a b c
-
与
data.frame
不同,character
类型的列默认情况下从不转换为factors
。 -
行号上印有
:
,以便在视觉上将行号与第一列分开。 -
当要打印的行数超过全局选项
datatable.print.nrows
(默认= 100)时,它将自动仅打印前5行和后5行(在数据部分中可以看到)。如果您对data.frame
s 有很多经验,那么您可能会发现自己在等待较大的表打印页面时在等待,有时似乎是无休止的。您可以这样查询默认号码:getOption("datatable.print.nrows")
-
data.table
永远不会设置或使用行名。我们将在“基于键和快速二进制搜索的子集”小插图中看到原因。
2.1.2 获取列/行的区别
# 因为date.table所创建的data里的每一列相当于一个变量,因此调用某一行时不需要加DT$
> DT[ID == "a"]
ID a b c
1: a 4 10 16
2: a 5 11 17
# 而data.frame所创建的data众所周知是下面这样的
# 不指定data,即DX$,错!
> DX[ID == "a"]
Error in `[.data.frame`(DX, ID == "a") : object 'ID' not found
# 不设置代表取全列的符号“,”,错!
> DX[DX$ID == "a"]
Error in `[.data.frame`(DX, DX$ID == "a") : undefined columns selected
# 这才是data.frame的data的标准用法
> DX[DX$ID == "a",]
ID a b c
4 a 4 10 16
5 a 5 11 17
另外,还会发现date.table
所创建的data并不会创建和使用固定的行名,而data.frame
所创建的data是要指定的或自动分配行名。
2.1.3 获取前两行
> DT[1:2]
ID a b c
1: b 1 7 13
2: b 2 8 14
> DX[1:2]
ID a
1 b 1
2 b 2
3 b 3
4 a 4
5 a 5
6 c 6
蛤?数据框还能这样
DX[1:2]
取数据?而且是前两列?(弱小又无助.jpg)
在这种情况下,没有条件。行索引已在中提供i
。因此,对于那些行索引,我们data.table
从flights
at行返回所有列。
我们可以看到取行也是很大差距的
2.3 使用i
取行
我们将使用通过flights
包获得的NYC-flights14数据(仅在GitHub上可用),它包含运输统计局 2014年从纽约市机场起飞的所有航班的按时航班数据(灵感来自nycflights13)。该数据仅适用于2014年1月10日。
我们可以使用data.table
的快速友好的文件阅读器直接fread
进行加载flights
:
# get the file "flights14.csv"
URL = "https://raw.githubusercontent.com/Rdatatable/data.table/master/vignettes/flights14.csv"
flights <- fread(input)
> flights
year month day dep_delay arr_delay carrier origin dest air_time distance hour
1: 2014 1 1 14 13 AA JFK LAX 359 2475 9
2: 2014 1 1 -3 13 AA JFK LAX 363 2475 11
3: 2014 1 1 2 9 AA JFK LAX 351 2475 19
4: 2014 1 1 -8 -26 AA LGA PBI 157 1035 7
5: 2014 1 1 2 1 AA JFK LAX 350 2475 13
---
253312: 2014 10 31 1 -30 UA LGA IAH 201 1416 14
253313: 2014 10 31 -5 -14 UA EWR IAH 189 1400 8
253314: 2014 10 31 -8 16 MQ LGA RDU 83 431 11
253315: 2014 10 31 -4 15 MQ LGA DTW 75 502 11
253316: 2014 10 31 -5 1 MQ LGA SDF 110 659 8
> dim(flights)
[1] 253316 11
2.3.1 获取6月所有以“肯尼迪国际机场”为始发机场的航班。
ans <- flights[origin == "JFK" & month == 6L]
> ans
year month day dep_delay arr_delay carrier origin dest air_time distance hour
1: 2014 6 1 -9 -5 AA JFK LAX 324 2475 8
2: 2014 6 1 -10 -13 AA JFK LAX 329 2475 12
3: 2014 6 1 18 -1 AA JFK LAX 326 2475 7
4: 2014 6 1 -6 -16 AA JFK LAX 320 2475 10
5: 2014 6 1 -4 -45 AA JFK LAX 326 2475 18
---
8418: 2014 6 30 -3 -6 MQ JFK PIT 62 340 14
8419: 2014 6 30 -5 -32 MQ JFK RDU 65 427 14
8420: 2014 6 30 -3 -16 MQ JFK DCA 39 213 17
8421: 2014 6 30 -2 7 MQ JFK DCA 52 213 7
8422: 2014 6 30 -7 -18 MQ JFK RDU 67 427 8
2.3.2 排序:先以origin
列在升序,然后以dest
降序
我们可以使用R函数order()
来完成此操作。
ans <- flights[order(origin, -dest)]
> ans
year month day dep_delay arr_delay carrier origin dest air_time distance hour
1: 2014 1 5 6 49 EV EWR XNA 195 1131 8
2: 2014 1 6 7 13 EV EWR XNA 190 1131 8
3: 2014 1 7 -6 -13 EV EWR XNA 179 1131 8
4: 2014 1 8 -7 -12 EV EWR XNA 184 1131 8
5: 2014 1 9 16 7 EV EWR XNA 181 1131 8
---
253312: 2014 10 31 -1 -22 WN LGA ATL 112 762 9
253313: 2014 10 31 -5 -23 WN LGA ATL 112 762 20
253314: 2014 4 6 -6 -1 EV LGA AGS 110 678 10
253315: 2014 4 7 2 1 EV LGA AGS 111 678 11
253316: 2014 4 11 0 -19 EV LGA AGS 102 678 10
2.4 使用 j
取列
2.4.1 提取arr_delay
列, 但是返回一个vector.
ans <- flights[, arr_delay]
head(ans)
[1] 13 13 9 -26 1 0
由于使用的是参数j
,所以就要先加个,
了。
2.4.2 提取arr_delay
列, 但是返回一个此列组成的data.table.
ans <- flights[, list(arr_delay)]
> head(ans)
arr_delay
1: 13
2: 13
3: 9
4: -26
5: 1
6: 0
要确保列名要在list()
里面,才能保证返回一个data.table
。由上例可知不在list里面会返回一个vector。
此外,list()
还被允许写为.()
,非常地方便,下文就用简写的形式了。
2.4.3 提取多列
ans <- flights[, .(arr_delay, dep_delay)]
> head(ans)
arr_delay dep_delay
1: 13 14
2: 13 -3
3: 9 2
4: -26 -8
5: 1 2
6: 0 4
2.4.4 提取多列,并对列名重命名
ans <- flights[, .(hahahah1=arr_delay, hahaha2=dep_delay)]
> head(ans)
hahahah1 hahaha2
1: 13 14
2: 13 -3
3: 9 2
4: -26 -8
5: 1 2
6: 0 4
3 使用j
进行计算
3.1 有多少航班总晚点时长<0 ?
ans <- flights[, sum( (arr_delay + dep_delay) < 0 )]
> ans
[1] 141814
上文说过,每一列都是一个变量,因此我们可以对每一列的设置使用数学函数进行计算。
3.2 i
取行&做 j
运算
计算6月份所有以“肯尼迪国际机场”为始发机场的航班的平均到达和起飞延迟:
ans <- flights[origin == "JFK" & month == 6L,
.(m_arr = mean(arr_delay), m_dep = mean(dep_delay))]
> ans
m_arr m_dep
1: 5.839349 9.807884
2014年总共有多少航班从JFK
机场出发 ?
ans <- flights[origin == "JFK" & month == 6L, length(dest)]
> ans
[1] 8422
3.3 特殊符号.N
:
.N
是一个特殊的内置变量,它保存当前组中的观测值数量。当与by
下一节中将要结合使用时,它特别有用。在没有分组操作的情况下,它仅返回子集中的行数。
因此,我们现在可以通过使用.N
以下命令来完成相同的任务:
ans <- flights[origin == "JFK" & month == 6L, .N]
> ans
[1] 8422
3.4 提取指定多列的几种方法
# 方法一,同list()
> flights[, .(day, hour)]
day hour
# 方法二
> flights[, c("day", "hour")]
# 方法三,'..'前缀
> a=c("day", "hour")
> flights[, ..a]
# 方法四,使用参数'with = F'
select_cols = c(day, hour)
> flights[ , select_cols, with = FALSE]
# 以上结果均为:
day hour
1: 1 9
2: 1 11
3: 1 19
4: 1 7
5: 1 13
---
253312: 31 14
253313: 31 8
253314: 31 11
253315: 31 11
253316: 31 8
with
参数默认是TURE
,因为这样我能够对select出的数据做更多的事情,如果输出的是data.table
就不方便继续对里面的数据进行编辑了。当其与by
参数连用时就会看得出来其好处,下文会讲到。
3.5 反选:!
或者-
ns <- flights[, !c("arr_delay", "dep_delay")]
# or
ans <- flights[, -c("arr_delay", "dep_delay")]
> ns
year month day carrier origin dest air_time distance hour
1: 2014 1 1 AA JFK LAX 359 2475 9
2: 2014 1 1 AA JFK LAX 363 2475 11
3: 2014 1 1 AA JFK LAX 351 2475 19
4: 2014 1 1 AA LGA PBI 157 1035 7
5: 2014 1 1 AA JFK LAX 350 2475 13
---
253312: 2014 10 31 UA LGA IAH 201 1416 14
253313: 2014 10 31 UA EWR IAH 189 1400 8
253314: 2014 10 31 MQ LGA RDU 83 431 11
253315: 2014 10 31 MQ LGA DTW 75 502 11
253316: 2014 10 31 MQ LGA SDF 110 659 8
3.6 连选
> flights[, year:day]
year month day
1: 2014 1 1
2: 2014 1 1
3: 2014 1 1
4: 2014 1 1
5: 2014 1 1
---
253312: 2014 10 31
253313: 2014 10 31
253314: 2014 10 31
253315: 2014 10 31
253316: 2014 10 31
# 反转day, month和year
> flights[, day:year]
day month year
1: 1 1 2014
2: 1 1 2014
3: 1 1 2014
4: 1 1 2014
5: 1 1 2014
---
253312: 31 10 2014
253313: 31 10 2014
253314: 31 10 2014
253315: 31 10 2014
253316: 31 10 2014
# 反选之连选
ans <- flights[, -(year:day)]
# or
ans <- flights[, !(year:day)]
4 高级用法——by
汇总
4.1 使用by
进行分组
获取每个机场的航班数:
> flights[, .(.N), by = .(origin)]
origin N
1: JFK 81483
2: LGA 84433
3: EWR 87400
-
分组依据是
origin
,.N
对当前组分别进行行数的统计 -
分组变量的原始顺序保留在结果中。注意这一点很重要!
-
by还可以接受列名称的字符向量
flights[, .(.N), by = "origin"]
- 如果只有一列或表达式来引用
j
和by
,我们可以删除.()
符号。这纯粹是为了方便。我们可以改为:
ans <- flights[, .N, by = origin]
我们将在以后适用的地方使用这种方便的形式。
计算承运商代码为AA
的每个机场的航班数
> flights[carrier == "AA", .N, by = origin]
origin N
1: JFK 11923
2: LGA 11730
3: EWR 2649
-
首先获得要处理的对象
i
,即AA
;分组依据是origin
;处理方式是j
,即.N
,统计行数。 -
因此我们就能得出以机场名为分组的对应的承运商代码为
AA
的航班数的个数。
计算每对origin
和dest
的对应的承运商代码为AA
的航班数的个数
> ans = flights[carrier == "AA", .N, by = .(origin, dest)]
> head(ans)
origin dest N
1: JFK LAX 3387
2: LGA PBI 245
3: EWR LAX 62
4: JFK MIA 1876
5: JFK SEA 298
6: EWR MIA 848
计算每个月份每对orig
和dest
的平均起飞晚点时间和平均到达晚点时间
ans = (flights[carrier == "AA", .(mean(dep_delay), mean(arr_delay)), by = .(origin, dest, month)])
> head(ans)
origin dest month V1 V2
1: JFK LAX 1 14.2289157 6.590361
2: LGA PBI 1 0.3103448 -7.758621
3: EWR LAX 1 7.5000000 1.366667
4: JFK MIA 1 18.7430168 15.720670
5: JFK SEA 1 30.7500000 14.357143
6: EWR MIA 1 12.1235955 11.011236
4.2 排序:keyby
data.table
保留组的原始顺序是有意并有意设计的。在某些情况下,保留原始顺序至关重要。但是有时我们希望根据分组中的变量自动排序。
方法很简单:将by改为keyby即可,默认升序。
ans <- flights[carrier == "AA",
.(mean(arr_delay), mean(dep_delay)),
keyby = .(origin, dest, month)]
> ans
origin dest month V1 V2
1: EWR DFW 1 10.0125786 6.427673
2: EWR DFW 2 11.3455882 10.536765
3: EWR DFW 3 8.0797546 12.865031
4: EWR DFW 4 12.9207317 17.792683
5: EWR DFW 5 18.6829268 18.487805
---
196: LGA PBI 1 0.3103448 -7.758621
197: LGA PBI 2 2.4038462 -7.865385
198: LGA PBI 3 3.0327869 -5.754098
199: LGA PBI 4 -4.7333333 -13.966667
200: LGA PBI 5 -6.8571429 -10.357143
4.3 链接
计算每对origin
和dest
的对应的承运商代码为AA
的航班数的个数,并以origin
升序,以dest
降序
ans <- flights[carrier == "AA", .N, by = .(origin, dest)][order(origin, -dest)]
> head(ans, 10)
origin dest N
1: EWR PHX 121
2: EWR MIA 848
3: EWR LAX 62
4: EWR DFW 1618
5: JFK STT 229
6: JFK SJU 690
7: JFK SFO 1312
8: JFK SEA 298
9: JFK SAN 299
10: JFK ORD 432
4.4 在by中使用表达式
计算晚发早归(含正点到)的航班次数
ans <- flights[, .N, .(dep_delay>0, arr_delay>0)]
> ans
dep_delay arr_delay N
1: TRUE TRUE 72836
2: FALSE TRUE 34583
3: FALSE FALSE 119304
4: TRUE FALSE 26593
4.5 j
中的特殊符号.SD
特殊符号.SD
表示subset of data,是根据by来进行分组。以dataDT
来说明:
DT[, print(.SD), by = ID]
a b c
1: 1 7 13
2: 2 8 14
3: 3 9 15
a b c
1: 4 10 16
2: 5 11 17
a b c
1: 6 12 18
Empty data.table (0 rows and 1 cols): ID
使用lapply
函数计算分组后的每一列的均值:
DT[, lapply(.SD, mean), by = ID]
ID a b c
1: b 2.0 8.0 14.0
2: a 4.5 10.5 16.5
3: c 6.0 12.0 18.0
- 首先使用.SD求出以ID为分组的三组(如上例所示)
- 然后lapply的功能就是对.SD中每一组的每一列使用mean求均值。
4.6 指定你想要计算的行
使用参数.SDcols
:
> flights[carrier == "AA", lapply(.SD, mean),
by = .(origin, dest, month),
.SDcols = c("arr_delay", "dep_delay")]
origin dest month arr_delay dep_delay
1: JFK LAX 1 6.590361 14.2289157
2: LGA PBI 1 -7.758621 0.3103448
3: EWR LAX 1 1.366667 7.5000000
4: JFK MIA 1 15.720670 18.7430168
5: JFK SEA 1 14.357143 30.7500000
---
196: LGA MIA 10 -6.251799 -1.4208633
197: JFK MIA 10 -1.880184 6.6774194
198: EWR PHX 10 -3.032258 -4.2903226
199: JFK MCO 10 -10.048387 -1.6129032
200: JFK DCA 10 16.483871 15.5161290
得到每个月的前两行
ans <- flights[, head(.SD, 2), by = month]
> head(ans)
month year day dep_delay arr_delay carrier origin dest air_time distance hour
1: 1 2014 1 14 13 AA JFK LAX 359 2475 9
2: 1 2014 1 -3 13 AA JFK LAX 363 2475 11
3: 2 2014 1 -1 1 AA JFK LAX 358 2475 8
4: 2 2014 1 -5 3 AA JFK LAX 358 2475 11
5: 3 2014 1 -11 36 AA JFK LAX 375 2475 8
6: 3 2014 1 -3 14 AA JFK LAX 368 2475 11
4.7 为什么参数j
如此灵活多样?
这样是为了使得R基础函数也能应用到其中,使得其功能更强大且具有高的兼容性。
如何对以ID为分组的每一subset的a
列和b
列进行连接?
> DT[, .(val = c(a,b)), by = ID]
ID val
1: b 1
2: b 2
3: b 3
4: b 7
5: b 8
6: b 9
7: a 4
8: a 5
9: a 10
10: a 11
11: c 6
12: c 12
如何对以ID为分组的每一subset的a
列和b
列进行连接,但是以list的形式输出连接结果?
> DT[, .(val = list(c(a,b))), by = ID]
ID val
1: b 1,2,3,7,8,9
2: a 4, 5,10,11
3: c 6,12
4.8 结合print()函数的妙用
结合print()
函数你就能更好地理解j
参数每步在做什么了。
> # (1) look at the difference between
> DT[, print(c(a,b)), by = ID]
[1] 1 2 3 7 8 9
[1] 4 5 10 11
[1] 6 12
Empty data.table (0 rows and 1 cols): ID
> # (2) and
> DT[, print(list(c(a,b))), by = ID]
[[1]]
[1] 1 2 3 7 8 9
[[1]]
[1] 4 5 10 11
[[1]]
[1] 6 12
Empty data.table (0 rows and 1 cols): ID
它能够返回每一组的处理结果。可以发现(1)中每一组返回的是个vector,而(2)中每组返回的是个list。
5 总结
data.table
的通用语法:
DT[i, j, by]
i
的用法:
- 你可以用i根据列值以处理
data.frame
产生的data所类似的方式来取行,但是你不必使用在每列前面加DT$,因为DT在data.table
产生的的data的列名,本身就相当于一个变量,可以直接调用。 - 使用
order
函数对列进行排序,其使用了data.table
的快速排序功能。
j
的用法:
- 取列:
DT[, .(colA, colB)]
. - 取列:
DT[, c("colA", "colB")]
. - 计算列:
DT[, .(sum(colA), mean(colB))]
. - 提供列名:
DT[, .(sA =sum(colA), mB = mean(colB))]
. - 与
i
连用:DT[colA > value, sum(colB)]
.
by
:
- 通过给出一组列名的list或者character vector,对这些列进行分组;也可以使用表达式进行分组。
j
具有非常高的灵活性,其结合基础函数和i
及by
可以显示出强大的功能。 - 处理多列。
- 设置
keyby
以自定义排序方式。 - 在
j
中使用.SD
和.SDcols
用以得到以by
为分类依据的subset,和规定所要处理的列;并且可以使用基础函数对subset进行分别处理:- 结合
lapply
函数,使用特定函数fun
对特定的列的特定分类方式所得的subset进行处理:DT[, lapply(.SD, fun), by = ..., .SDcols = ...]
。 - 返回前两行:
DT[, head(.SD, 2), by = ...]
。 - 联合使用
i
、j
和by
:DT[col > val, head(.SD, 1), by = ...]
。
- 结合
最后记住一点:
一旦j
返回的是一个list,那么这个list的每个元素都将成为一列。