data.table学习笔记1
data.table的特点:减小计算复杂度,降低计算时间。
1.数据
教程中,使用NYC-flights14数据集,纽约机场2014年出发的所有航班信息,时间是2014.01-10月份。下载到本地目录,与Rmd代码在同一目录
data.table中提供了fread()函数,用于快速读取大数据。
执行代码前,首先运行setwd()
或者通过rstudio软件Session
-> Set Working Directory
-> To Source File Location
,将工作目录切换到代码文件和数据文件所在路径。
require(data.table)
## Loading required package: data.table
flights <- fread("flights14.csv")
flights
## year month day dep_time dep_delay arr_time arr_delay cancelled
## 1: 2014 1 1 914 14 1238 13 0
## 2: 2014 1 1 1157 -3 1523 13 0
## 3: 2014 1 1 1902 2 2224 9 0
## 4: 2014 1 1 722 -8 1014 -26 0
## 5: 2014 1 1 1347 2 1706 1 0
## ---
## 253312: 2014 10 31 1459 1 1747 -30 0
## 253313: 2014 10 31 854 -5 1147 -14 0
## 253314: 2014 10 31 1102 -8 1311 16 0
## 253315: 2014 10 31 1106 -4 1325 15 0
## 253316: 2014 10 31 824 -5 1045 1 0
## carrier tailnum flight origin dest air_time distance hour min
## 1: AA N338AA 1 JFK LAX 359 2475 9 14
## 2: AA N335AA 3 JFK LAX 363 2475 11 57
## 3: AA N327AA 21 JFK LAX 351 2475 19 2
## 4: AA N3EHAA 29 LGA PBI 157 1035 7 22
## 5: AA N319AA 117 JFK LAX 350 2475 13 47
## ---
## 253312: UA N23708 1744 LGA IAH 201 1416 14 59
## 253313: UA N33132 1758 EWR IAH 189 1400 8 54
## 253314: MQ N827MQ 3591 LGA RDU 83 431 11 2
## 253315: MQ N511MQ 3592 LGA DTW 75 502 11 6
## 253316: MQ N813MQ 3599 LGA SDF 110 659 8 24
class(flights)
## [1] "data.table" "data.frame"
2.基础
flights的类型显示为data.table和data.frame。也可以通过as.data.table()
将对象转化为data.table类型。
读取形式 语法如下所示:
DT[i, j, by]
其中i表示行过滤(实际上按照行选择数据集),j表示对列进行选择,by表示根据什么条件进行分组。
2.1行过滤
首先来看一下,如何对行进行过滤。譬如要获得6月份从JFK机场起飞,途径纽约或者终点是纽约的航班信息。
flights.JFK <- flights[origin=="JFK" & month == 6L]
head(flights.JFK)
## year month day dep_time dep_delay arr_time arr_delay cancelled carrier
## 1: 2014 6 1 851 -9 1205 -5 0 AA
## 2: 2014 6 1 1220 -10 1522 -13 0 AA
## 3: 2014 6 1 718 18 1014 -1 0 AA
## 4: 2014 6 1 1024 -6 1314 -16 0 AA
## 5: 2014 6 1 1841 -4 2125 -45 0 AA
## 6: 2014 6 1 1454 -6 1757 -23 0 AA
## tailnum flight origin dest air_time distance hour min
## 1: N787AA 1 JFK LAX 324 2475 8 51
## 2: N795AA 3 JFK LAX 329 2475 12 20
## 3: N784AA 9 JFK LAX 326 2475 7 18
## 4: N791AA 19 JFK LAX 320 2475 10 24
## 5: N790AA 21 JFK LAX 326 2475 18 41
## 6: N785AA 117 JFK LAX 329 2475 14 54
data.table的语法跟dplyr类似,不需要加上数据框名(flights$origin
),大幅度简化。
另外一种写法flights.JFK <- flights[origin=="JFK" & month == 6L,,]
也是可以的。这种写法,在data frame是不行的。
行排序
可以利用order()
函数来完成。
flights.sort <- flights[order(origin,-dest),]
head(flights.sort)
## year month day dep_time dep_delay arr_time arr_delay cancelled carrier
## 1: 2014 1 5 836 6 1151 49 0 EV
## 2: 2014 1 6 833 7 1111 13 0 EV
## 3: 2014 1 7 811 -6 1035 -13 0 EV
## 4: 2014 1 8 810 -7 1036 -12 0 EV
## 5: 2014 1 9 833 16 1055 7 0 EV
## 6: 2014 1 13 923 66 1154 66 0 EV
## tailnum flight origin dest air_time distance hour min
## 1: N12175 4419 EWR XNA 195 1131 8 36
## 2: N24128 4419 EWR XNA 190 1131 8 33
## 3: N12142 4419 EWR XNA 179 1131 8 11
## 4: N11193 4419 EWR XNA 184 1131 8 10
## 5: N14198 4419 EWR XNA 181 1131 8 33
## 6: N12157 4419 EWR XNA 188 1131 9 23
order()
实际上调用了data.table的快速基数排序函数forder()
。
2.2选择列
flights.arr_delay <- flights[,arr_delay]
head(flights.arr_delay)
## [1] 13 13 9 -26 1 0
请注意,返回值是向量。
如果想返回值是data.table,需要在列名称前加一个list
flights.arr_delay.dt <- flights[,list(arr_delay)]
head(flights.arr_delay.dt)
## arr_delay
## 1: 13
## 2: 13
## 3: 9
## 4: -26
## 5: 1
## 6: 0
list()
也可以用.()
代替,提高写作效率。
flights.arr_delay.dt.2 <- flights[,.(arr_delay)]
head(flights.arr_delay.dt.2)
## arr_delay
## 1: 13
## 2: 13
## 3: 9
## 4: -26
## 5: 1
## 6: 0
继续选取两列,列变量名称用.()
包括,返回data.table类型。
flights.arr_delay.dep_delay <- flights[,.(arr_delay,dep_delay)]
head(flights.arr_delay.dep_delay)
## arr_delay dep_delay
## 1: 13 14
## 2: 13 -3
## 3: 9 2
## 4: -26 -8
## 5: 1 2
## 6: 0 4
另外一种形式:
flights.arr_delay.dep_delay <- flights[,c("arr_delay","dep_delay"),with=FALSE]
head(flights.arr_delay.dep_delay)
## arr_delay dep_delay
## 1: 13 14
## 2: 13 -3
## 3: 9 2
## 4: -26 -8
## 5: 1 2
## 6: 0 4
class(flights.arr_delay.dep_delay)
## [1] "data.table" "data.frame"
直接对列变量重新命名
flights.new <- flights[,.(delay_arr = arr_delay, delay_dep = dep_delay)]
2.3列运算
这是个新概念,理解起来有些困难。先看例子,计算有多少个航班没有延误。
具体的计算逻辑是这样的:到达和出发延迟时间均为负值,那么(arr_delay+dep_delay)<0
如果为真,表示该航班延误了,那么这个表达式的值是TRUE,同时也等于1,再进行sum()求和,实际上获得了延误航班的总数。
flights.nodelay.num <- flights[,sum((arr_delay+dep_delay)<0)]
flights.nodelay.num
## [1] 141814
从上式中,可以看出,sum((arr_delay+dep_delay)<0)
实际上是针对数据集的一个操作,只是在[]
内完成而已。
进一步看下一个例子:计算6月份从JFK机场起飞的航班中,起飞和延误的平均时间:
flights.JFK.stats <- flights[origin == "JFK" & month == 6L,
.(m_arr=mean(arr_delay),m_dep=mean(dep_delay))]
flights.JFK.stats
## m_arr m_dep
## 1: 5.839349 9.807884
class(flights.JFK.stats)
## [1] "data.table" "data.frame"
这种操作跟dplyr中的summarise函数的功能是类似的。虽然网上说dplyr更加容易操作和理解,但是就我本人而言,data.table这种操作方式更加简洁,好用。
来自 http://blog.csdn.net/smart_xiao_xiong/article/details/51658262 的解释(性能更好的原因,同时提取符合条件的列和行,而不是分步,先行后列):
- 我们首先在i参数里,找到所有符合 origin (机场)是”JFK”,并且 month (月份)是 6 这样条件的行。此时,我们还没有subset整个data.table。
- 现在,我们看看参数j,它只使用了两列。我们需要分别计算这两列的平均值 mean()。这个时候,我们才subset那些符合i参数里条件的列,然后计算它们的平均值。 因为这三个参数(i,j和by)都被指定在同一个方括号中,data.table能同时接受这三个参数,并在计算之前,选取最优的计算方法,而不是分步骤计算。所以,我们可以避免对整个data.table计算,同时,在计算速度和内存使用量这两方面,取得最优的效果。
如果我们想看一下,从JFK机场起飞的航班一共有多少架次。
flights.JFK.num <- flights[origin == "JFK" & month == 6L,length(dest)]
flights.JFK.num
## [1] 8422
其实,我只是需要知道符合条件的行数,length()
中的参数理论上可以是任何一列。data.table中定义了一个内建的变量.N
,表示当前的分组中对象的数目。因此上边的代码,可以写的更加简洁和可理解。
flights.JFK.num <- flights[origin == "JFK" & month == 6L, .N]
flights.JFK.num
## [1] 8422
3.聚合
3.1 分组
获得每个机场起飞航班数
flights.dep.num.per.airport <- flights[,.(.N),by=.(origin)]
flights.dep.num.per.airport
## origin N
## 1: JFK 81483
## 2: LGA 84433
## 3: EWR 87400
当参数j和by,也就是第2个和第3个参数只有1列时,可以不用.()
。上述代码可以简化为:
flights.dep.num.per.airport <- flights[,.N,by=origin]
flights.dep.num.per.airport
## origin N
## 1: JFK 81483
## 2: LGA 84433
## 3: EWR 87400
获取一个制定航空公司在每个机场的起飞航班数,譬如美航(AA)。
在i
参数中设置条件过滤需要的行。
flights.AA.dep.num.per.airport <- flights[carrier == "AA",.(.N),by=.(origin)]
flights.AA.dep.num.per.airport
## origin N
## 1: JFK 11923
## 2: LGA 11730
## 3: EWR 2649
获取AA航空公司在所有机场起飞和降落的航班数
flights.AA.dep.dest.num.per.airport <- flights[carrier == "AA",.(.N),by=.(origin,dest)]
flights.AA.dep.dest.num.per.airport
## 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
## 7: JFK SFO 1312
## 8: JFK BOS 1173
## 9: JFK ORD 432
## 10: JFK IAH 7
## 11: JFK AUS 297
## 12: EWR DFW 1618
## 13: LGA ORD 4366
## 14: JFK STT 229
## 15: JFK SJU 690
## 16: LGA MIA 3334
## 17: LGA DFW 3785
## 18: JFK LAS 595
## 19: JFK MCO 597
## 20: JFK EGE 85
## 21: JFK DFW 474
## 22: JFK SAN 299
## 23: JFK DCA 172
## 24: EWR PHX 121
## origin dest N
想进一步分析在所有机场,每个月起降的平均延误时间。
flights.AA.dep.dest.stats.per.airport <- flights[carrier == "AA",.(.N,MeanArrDelay=mean(arr_delay),MeanDepDelay=mean(dep_delay)),by=.(origin,dest,month)]
flights.AA.dep.dest.stats.per.airport
## origin dest month N MeanArrDelay MeanDepDelay
## 1: JFK LAX 1 249 6.590361 14.2289157
## 2: LGA PBI 1 58 -7.758621 0.3103448
## 3: EWR LAX 1 30 1.366667 7.5000000
## 4: JFK MIA 1 179 15.720670 18.7430168
## 5: JFK SEA 1 28 14.357143 30.7500000
## ---
## 196: LGA MIA 10 278 -6.251799 -1.4208633
## 197: JFK MIA 10 217 -1.880184 6.6774194
## 198: EWR PHX 10 31 -3.032258 -4.2903226
## 199: JFK MCO 10 62 -10.048387 -1.6129032
## 200: JFK DCA 10 31 16.483871 15.5161290
分组结果如何按照升降序排列。需要keyby参数代替by。
flights.AA.dep.dest.stats.per.airport <- flights[carrier == "AA",.(.N,MeanArrDelay=mean(arr_delay),MeanDepDelay=mean(dep_delay)),keyby=.(origin,dest,month)]
flights.AA.dep.dest.stats.per.airport
## origin dest month N MeanArrDelay MeanDepDelay
## 1: EWR DFW 1 159 6.427673 10.0125786
## 2: EWR DFW 2 136 10.536765 11.3455882
## 3: EWR DFW 3 163 12.865031 8.0797546
## 4: EWR DFW 4 164 17.792683 12.9207317
## 5: EWR DFW 5 164 18.487805 18.6829268
## ---
## 196: LGA PBI 1 58 -7.758621 0.3103448
## 197: LGA PBI 2 52 -7.865385 2.4038462
## 198: LGA PBI 3 61 -5.754098 3.0327869
## 199: LGA PBI 4 60 -13.966667 -4.7333333
## 200: LGA PBI 5 14 -10.357143 -6.8571429
3.2 管道符 chaining
类似于 %>% ,实现连续操作,避免中间变量的生成。这个是非常强大的操作。
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
可以多个表达式链接DT[...][...][...][...]
,另外一种形式,也是可以接受的:
DT[...
][...
][...
]
3.3 by参数表达式
参数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
这也是一个比较牛逼的用法。
3.4 对多列同时进行统一运算
譬如有1000列,要对每一列进行均值计算。传统可以通过lapply函数来进行。如果存在分组的情况,该如何操作?data.table提供了.SD
,表示Subset of Data,本身是一个data.table,包括通过by分组后的每一组。
实例来看一下,对于起飞机场的一个分组
flights.SD <- flights[,.(origin,dep_time,arr_time,air_time,distance,hour,min)][,.SD,by=origin]
flights.SD
## origin dep_time arr_time air_time distance hour min
## 1: JFK 914 1238 359 2475 9 14
## 2: JFK 1157 1523 363 2475 11 57
## 3: JFK 1902 2224 351 2475 19 2
## 4: JFK 1347 1706 350 2475 13 47
## 5: JFK 2133 37 338 2475 21 33
## ---
## 253312: EWR 1242 1549 344 2565 12 42
## 253313: EWR 2121 2224 100 719 21 21
## 253314: EWR 1049 1335 326 2454 10 49
## 253315: EWR 1653 1910 291 2227 16 53
## 253316: EWR 854 1147 189 1400 8 54
每个分组,按照origin整齐排列。 对于除了分组列外的其他列,统一求平均值,并且保留两位小数。
flights.SD.mean <- flights[,.(origin,dep_time,arr_time,air_time,distance,hour,min)][,lapply(.SD,mean),by=origin][,round(.SD,2),by=origin]
flights.SD.mean
## origin dep_time arr_time air_time distance hour min
## 1: JFK 1359.69 1488.90 195.28 1413.86 13.27 32.34
## 2: LGA 1315.07 1500.38 116.46 777.47 12.82 33.20
## 3: EWR 1340.62 1493.54 159.68 1117.36 13.10 30.22
如何进一步获取制定列的均值。答案是通过.SDcols参数。
flights.SD.mean <- flights[,.(origin,dep_time,arr_time,air_time,distance,hour,min)][,lapply(.SD,mean),.SDcols=c("dep_time","arr_time"),by=origin][,round(.SD,2),by=origin]
flights.SD.mean
## origin dep_time arr_time
## 1: JFK 1359.69 1488.90
## 2: LGA 1315.07 1500.38
## 3: EWR 1340.62 1493.54
实现类似melt的功能,把多列合并为一列。 首先生成一个数据集
DT = data.table(ID = c("b","b","b","a","a","c"), a = 1:6, b = 7:12, c=13:18)
DT
## 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
然后,把a,b,c三列合并为1列。
DT.new <- DT[,.(abc=c(a,b,c)),by=ID]
DT.new
## ID abc
## 1: b 1
## 2: b 2
## 3: b 3
## 4: b 7
## 5: b 8
## 6: b 9
## 7: b 13
## 8: b 14
## 9: b 15
## 10: a 4
## 11: a 5
## 12: a 10
## 13: a 11
## 14: a 16
## 15: a 17
## 16: c 6
## 17: c 12
## 18: c 18
j参数非常强大,也可以a,b,c三列的结果,作为一个列表返回。
DT.new.list <- DT[,.(abc=list(c(a,b,c))),by=ID]
DT.new.list
## ID abc
## 1: b 1,2,3,7,8,9,
## 2: a 4, 5,10,11,16,17
## 3: c 6,12,18
合并a,b,c三列,作为字符串。
DT.new.str <- DT[,.(abc=paste(a,b,c,sep = "")),by=ID]
DT.new.str
## ID abc
## 1: b 1713
## 2: b 2814
## 3: b 3915
## 4: a 41016
## 5: a 51117
## 6: c 61218
- 上一篇 身体锻炼记录
- 下一篇 data.table学习笔记2