0%

R语言学习(四)data.table

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.table

ans <- 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),
mean(dep_delay)), keyby=.(origin, dest, month)]

把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.函数形式
    DT[, `:=`(colA = valA, # valA is assigned to colA
    colB = valB, # valB is assigned to colB
    ...
    )]
    注意:我们没有把运算的结果赋值给一个变量.因为完全没必要。因为我们直接更新了data.table

例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]
foo <- function(DT) {
DT <- copy(DT) # deep copy
DT[, speed := distance / (air_time/60)] #不会修改flights
DT[, .(max_speed = max(speed)), by=month]
}
ans <- foo(flights)

主键,基于二分搜索的subset

Disqus评论区没有正常加载,请使用科学上网