data.table是对R的原生包data.table的扩展版本,无论是编码效率和执行效率都要比data.table快的多
在本章中:
– subset特指对行的选择
– select特指对列的选择
创建
DT <- data.table(ID = c("b","b","b","a","a","c"), A = 1:6, B = 7:12,C=13:18) |
可见data.table的创建和data.frame类似,都是传入若干个向量,不同的是,data.frame中的character类型向量会自动转化为factor,data.table则不会,使用class(DT$ID)
查看该属性的类型,返回”character”
也可以根据已有的data.frame类型直接转化为data.table:A是data.frame类型B <- as.data.table(A)
subset行
data.table的通用选取格式是DT[i,j,k]
,类比sql语句的select j from DT where i group by k
例1:获取六月份所有从“JFK”机场起飞的航班ans <- flights[origin == "JFK" & month == 6L]
如果是data.table,写法则如下:ans <- flights[flights$origin == "JFK" && flights$month == 6L,]
可见:
- 省略了逗号
- 省略了前缀
例2: 获取 flights 开头的两行ans <- flights[1:2]
排序
例:先按 origin列 的升序,再按 dest 的降序排列。通过R语言的基础函数order()
来
完成这个功能。ans <- flights[order(origin, -dest)]
order()
函数是被优化过的- 我们可以对字符型的列使用减号”-“来实现降序排列。
- 函数
order()
其实调用了data.table的快速基数排序函数forder()
,它比base::order()
快很多。
select列
例1:选取 arr_delay 列,返回值是向量。ans <- flights[, arr_delay]
例2:选取 arr_delay 列,返回值是data.tableans <- flights[, list(arr_delay)]
用list将变量包起来,返回的则是data.table,也可以用list的别名简写为:ans <- flights[, .(arr_delay)]
如果选取多个列,则返回必须是data.table,所以用list封装变量考虑到了这一点。ans <- flights[, .(arr_delay, dep_delay)]
列重命名功能:选取arr_delay列和dep_delay列,并把列名改为delay_arr和delay_dep。ans <- flights[, .(delay_arr = arr_delay,delay_dep = dep_delay)]
在参数j中计算
例1:有多少航班没有延误ans <- flights[,sum(arr_delay+dep_delay<0)]
j除了能select还能处理表达式,即对列的计算,如果这样:ans <- flights[,arr_delay+dep_delay<0]
则返回一个布尔类型的向量,SUM()
处理一个布尔向量,则返回TRUE的数目
例2:在六月份,从”JFK”机场起飞的航班中,计算起飞和到达的平均延误时间。ans <- flights[origin == "JFK" & month == 6L, .(m_arr=mean(arr_delay), m_dep=mean(dep_delay))]
例3:在六月份,从”JFK”机场起飞的航班一共有多少?ans <- flights[origin == "JFK" & month == 6L, length(dest)]
j中length()
传入任意一个参数即可,所以还有一种简要的写法:ans <- flights[origin == "JFK" & month == 6L, .N]
例3:用data.frame的方式,选取arr_delay和dep_delay两列。(不写with参数也可)ans <- flights[, c("arr_delay", "dep_delay"), with=FALSE]
– 参数 with是根据 R里面的函数 with() 演变而来的。在data.table里,我们设置 with=FALSE
,使得我们不能再像变量一样引用列了。
– 等价于:flights[, .(arr_delay, dep_delay)]
我们还可以使用 -
或 !
来排除列。ans <- flights[, !c("arr_delay", "dep_delay"), with=FALSE]
#or
ans <- flights[, -c("arr_delay", "dep_delay"), with=FALSE]
聚合
例:如何获取每个机场起飞的航班数?ans <- flights[, .(.N), by=.(origin)]
例:如何获取美航在所有机场的起/降的数目?ans <- flights[carrier == "AA", .N, by=.(origin,dest)]
by分组可以接受多个列
keyby
ans <- flights[carrier == "AA", .(mean(arr_delay), |
把by改为了keyby。这会自动的将结果按照升序排列。注意keyby()
是在数据操作完成后才进行。
chaining表达式
例:美航在所有机场的起/降的数目,并让结果按origin的升序、按dest的降序排列。按照之前知识,可以这样用两条语句做:ans <- flights[carrier == "AA", .N, by = .(origin, dest)]
ans <- ans[order(origin, -dest)]
但是这么做会生成一个临时变量。可以通过添加chaining表达式,避免生成临时变量。ans <- flights[carrier == "AA", .N, by=.(origin, dest)][order(origin, -dest)]
我们可以一个接一个地添加表达式,做一系列操作,就像DT[...][...][...]...
by表达式
例:有多少航班起飞延误并且到达延误?有多少航班起飞延误和到达没延误……ans <- flights[, .N, .(dep_delay>0, arr_delay>0)]
说明by中也可以是表达式
如果根据ID每一列都要求平均,那就是:DT[,.(mean(col1),mean(col2),mean(col3),mean(col4)), by=ID]
写法复杂,如果对于每一列都应用相同的函数,则可以写成如下形式:DT[, lapply(.SD, mean), by=ID]
其中.SD
包含了所有列,如果需要指定那些列统一应用函数,可以这样:flights[carrier == "AA", lapply(.SD, mean), by=.(origin,
dest, month), .SDcols=c("arr_delay", "dep_delay")]
表示.SD
中只设置了arr_delay和dep_delay两列。
返回每个月的前两行:ans <- flights[, head(.SD, 2), by=month]
语义引用
data.table
的语义引用,允许通过引用reference来add/update/delete列
- a.左右等式的形式
DT[, c("colA", "colB", ...) := list(valA, valB, ...)]qq()
DT[, colA := valA] #只有一列时 - b.函数形式注意:我们没有把运算的结果赋值给一个变量.因为完全没必要。因为我们直接更新了data.table
DT[, `:=`(colA = valA, # valA is assigned to colA
colB = valB, # valB is assigned to colB
...
)]
例1:对每次航班,添加 speed (km/hr) 和 delay (minutes) 两列。flights[, `:=`(speed = distance / (air_time/60),
delay = arr_delay + dep_delay)]
通过给不存在的列赋值则会添加新列
例2:追加一列,用来保存某对起飞/到达机场间的最快飞行速度。flights[, max_speed := max(speed), by=.(origin,dest)]
例3:删除 delay列flights[, `:=`(delay = NULL)]
给已存在的列赋NULL则会删除列
例4:更新hour列,把24点变为0点flights[hour == 24L, hour := 0L]
:=
操作没有返回值,产看运行结果可以末尾加一个[]
:flights[hour == 24L, hour := 0L][]
例5:再追加两列,用于保存每个月的最大起飞延误时间和到达延误时间。in_cols = c("dep_delay", "arr_delay")
out_cols = c("max_dep_delay", "max_arr_delay")
flights[, c(out_cols) := lapply(.SD, max), by = month, .SDcols = in_cols]
为了更好的可读性,这里使用了左右等式,c()
不可省略,否则语法上就变成单个对象赋值了。
:=
的副作用
操作符:=
会更新原数据。当我们不想新原数据时,可以用函数copy()
例:想创建一个函数,用于返回每个月的最快速度。foo <- function(DT) {
DT[, speed := distance / (air_time/60)]
DT[, .(max_speed = max(speed)), by=month]
}
ans = foo(flights)
此时flights被增加了speed列,但没有max_speed列希望使用操作符“:=”的功能,但是不想改变原数据,可以用函数copy()
来做到这一点。函数copy()
对输入参数进行深度拷贝,因此对副本做的所有更新操作,都不会对原数据生效。
flights[, speed := NULL] |