跳至主要内容

data.table R 教程:DT[i, j, by] 入门

这份免费的 data.table R 教程讲解 R 中 data.table 包的基础与语法。现在就掌握 data.table 的语法。
· 10分钟 阅读

使用 AI 探索

本篇 data.table R 教程讲解了 data.table 包核心命令 DT[i, j, by] 的基础用法。如果您想进一步学习 data.table 包,DataCamp 提供了关于 data.table 包的交互式 R 课程。课程包含 35 个以上的交互式 R 练习——全都可在您的浏览器中完成——并配有 data.table 包主要作者 Matt Dowle 以及重要贡献者 Arun Srinivasan 的多段视频。免费试用。

DataCamp 的 data.table R 教程

如果您曾在内存中处理过大型数据集(1GB 到 100GB 以上),就会知道 data.frame 可能会受限:有些操作耗时过长。Data.table 通过缩短计算时间来解决这个问题。更进一步,它还能让您用更少的输入完成更多工作。一旦通过本教程掌握了 data.table 的语法,您会惊讶于复杂操作可以如此简单。因此,您不仅能减少计算时间,还能缩短编程时间。

DT[i,j,by] 命令由三部分组成:i、j 和 by。如果用 SQL 来类比,i 对应 WHERE,j 对应 SELECT,by 对应 GROUP BY。我们通常这样理解这条命令:“取 DT,用 ‘i’ 子集化行,然后按 ‘by’ 分组计算 ‘j’。”下面用 hflights 数据集(便于您复现所有示例)的一个简单示例:

library(hflights)
library(data.table)
DT <- as.data.table(hflights)
DT[Month==10,mean(na.omit(AirTime)), by=UniqueCarrier]
UniqueCarrier V1
AA            68.76471
AS            255.29032
B6            176.93548
CO            141.52861
... 

这里我们将数据表子集化为仅保留一年中第 10 个月的行,计算实际飞行的航班的平均 AirTime(这就是为什么要用 na.omit(),被取消的航班没有 AirTime 值),然后按承运人进行分组。比如可以看到,AA(American Airlines,美国航空)的平均 AirTime 明显低于 AS(Alaska Airlines,阿拉斯加航空)。您也注意到了吗?R 的基础函数可以在 j 部分中使用。我们稍后会再谈。

The i Part

“i” 部分用于按行子集化,和 data frame 的用法类似。

DT[2:5]
 #selects the second to the fifth row of DT
Year Month DayofMonth DayOfWeek DepTime ArrTime UniqueCarrier FlightNum TailNum ActualElapsedTime AirTime
2011 1     2          7         1401    1501    AA            428       N557AA  60                45
2011 1     3          1         1352    1502    AA            428       N541AA  70                48
2011 1     4          2         1403    1513    AA            428       N403AA  70                39
2011 1     5          3         1405    1507    AA            428       N492AA  62                44

ArrDelay DepDelay Origin Dest Distance TaxiIn TaxiOut Cancelled CancellationCode Diverted
-9       1        IAH    DFW  224      6      9       0                          0
-8      -8        IAH    DFW  224      5      17      0                          0
 3       3        IAH    DFW  224      9      22      0                          0
-3       5        IAH    DFW  224      9      9       0                          0

但您也可以使用列名,因为它们会在 DT 的作用域内求值。

DT[UniqueCarrier=="AA"]
#Returns all those rows where the Carrier is American Airlines
Year Month DayofMonth DayOfWeek DepTime ArrTime UniqueCarrier FlightNum TailNum ActualElapsedTime
2011 1     1          6         1400    1500    AA            428       N576AA  60
2011 1     2          7         1401    1501    AA            428       N557AA  60
2011 1     3          1         1352    1502    AA            428       N541AA  70
2011 1     4          2         1403    1513    AA            428       N403AA  70
2011 1     5          3         1405    1507    AA            428       N492AA  62
---
2011 12    27         2         1021    1333    AA            2234      N3ETAA  132
2011 12    28         3         1015    1329    AA            2234      N3FJAA  134
2011 12    29         4         1023    1335    AA            2234      N3GSAA  132
2011 12    30         5         1024    1334    AA            2234      N3BAAA  130
2011 12    31         6         1024    1343    AA            2234      N3HNAA  139
AirTime ArrDelay DepDelay Origin Dest Distance TaxiIn TaxiOut Cancelled CancellationCode Diverted
40      -10       0       IAH    DFW  224       7     13      0                          0
45       -9       1       IAH    DFW  224       6      9      0                          0
48       -8      -8       IAH    DFW  224       5     17      0                          0
39        3       3       IAH    DFW  224       9     22      0                          0
44       -3       5       IAH    DFW  224       9      9      0                          0
---
112     -12       1       IAH    MIA  964       8     12      0                          0
112     -16      -5       IAH    MIA  964       9     13      0                          0
110     -10       3       IAH    MIA  964      12     10      0                          0
110     -11       4       IAH    MIA  964       9     11      0                          0
119      -2       4       IAH    MIA  964       8     12      0                          0 

请注意,在 data table 中进行行子集化不需要逗号。在 data.frame 中,写 DF[2:5] 会返回第 2 到第 5 列的所有行。相反(大家都知道),我们需要写 DF[2:5,]。同时也要注意,DT[,2:5] 对 data table 来说没有意义,详见 data.table 包的常见问题中的第一个问题。

既有点古怪又很实用:在子集化行时,您还可以在 DT[…] 命令中使用符号 .N,它代表行数或最后一行。您可以用它来选择最后一行或基于最后一行的偏移。

DT[.N-1]
#Returns the penultimate row of DT
Year Month DayofMonth DayOfWeek DepTime ArrTime UniqueCarrier FlightNum TailNum ActualElapsedTime AirTime
2011 12    6          2         656     812     WN            621       N727SW  76                64
ArrDelay DepDelay Origin Dest Distance TaxiIn TaxiOut Cancelled CancellationCode Diverted
-13      -4       HOU    TUL  453      3      9       0                          0 

The j Part

“j” 部分用于选择列并对其做一些处理。而所谓的处理几乎可以是任何操作。可以使用各种函数,这是 data.table 包的一大优势。

DT[, mean(na.omit(ArrDelay))] 
[1] 7.094334

注意这里的 “i” 留空,方括号里的第一个字符是逗号。乍看之下可能有些反直觉,但这仅仅表示我们不对子集行进行限制,即选择所有行。在 “j” 部分,我们计算了所有航班的平均到达延误。看来 hflights 数据集中的航班平均延误超过 7 分钟。下次赶飞机请做好心理准备!

当在 “j” 部分选择多列并对它们做处理时,您需要使用 “.()” 记法。该记法实际上是 “list()” 的别名。它返回一个 data table;而不使用 “.()” 时,如上所示只会返回一个向量。

DT[, .(mean(na.omit(DepDelay)), mean(na.omit(ArrDelay)))] 
V1       V2
9.444951 7.094334

另一个需要 “.()” 记法的实用功能是,您可以在 DT[…] 命令内部重命名列。

DT[, .(Avg_ArrDelay =
mean(na.omit(ArrDelay)))]
Avg_ArrDelay
7.094334
DT[, .(Avg_DepDelay = mean(na.omit(DepDelay)),
avg_ArrDelay = mean(na.omit(ArrDelay)))] 
Avg_DepDelay Avg_ArrDelay
9.444951     7.094334

当然,新的列名并非必需。

将上述 “i” 与 “j” 的内容结合起来:

DT[UniqueCarrier=="AA", .(Avg_DepDelay =
mean(na.omit(DepDelay)),
Avg_ArrDelay = mean(na.omit(ArrDelay)),
plot(DepTime,DepDelay,ylim=c(-15,200)),
abline(h=0))]
Avg_DepDelay Avg_ArrDelay V3    V4
6.390144     0.8917558    NULL  NULL

DepDelay DepTime 图

这里我们在 “i” 部分选取了承运人为 AA 的所有行,在 “j” 部分计算了起飞与到达的平均延误,并绘制了起飞时间与起飞延误的关系图。

小结一下,“j” 部分用于对其中指定的列进行计算。由于 data table 的列被视为变量,而 “j” 的各部分会作为表达式求值,几乎任何操作都可以在 “j” 中完成。这能显著缩短您的编程时间。

The by part

本教程的最后一部分聚焦 “by”。当我们希望按某个特定变量(或该变量的某种变换)对 “j” 的结果进行分组时,就使用 “by”。您会看到 “j” 表达式会对每个 “by” 分组重复执行。用法很简单:只需在 “by” 参数中指定要分组的列。

DT[,mean(na.omit(DepDelay)),by=Origin] 
Origin  V1
IAH     8.436951
HOU    12.837873 

这里我们计算了起飞前的平均延误,但按航班的始发地分组。看起来从 HOU 起飞的航班平均延误要高于从 IAH 出发的航班。

和 “j” 一样,您也可以在 “by” 中做很多处理。可以在 “by” 中使用函数,从而让 “j” 的运算结果按我们在 DT[…] 命令内指定的内容分组。在 DT[…] 内使用函数使这一行代码非常强大。同样地,当在 “by” 中使用多列时,需要使用 “.()” 记法。

DT[,.(Avg_DepDelay_byWeekdays = mean(na.omit(DepDelay))), by=.(Origin,Weekdays = DayOfWeek<6)] 
Origin Weekdays Avg_DepDelay_byWeekdays
IAH    FALSE     8.286543
IAH    TRUE      8.492484
HOU    FALSE    10.965384
HOU    TRUE     13.433994

这里(“i” 部分未作子集化,故选择了所有行),首先计算了所有航班的起飞前平均延误,然后按始发地以及是否为工作日进行分组。周末时 Weekdays 为 False。结果显示,从 HOU 起飞的平均起飞延误比 IAH 更大,且出乎意料的是,周末的延误更小。

将一切合到一起,一个典型的 DT[i,j,by] 命令如下:

DT[UniqueCarrier=="DL", .(Avg_DepDelay =
mean(na.omit(DepDelay)),
Avg_ArrDelay = mean(na.omit(ArrDelay)),
Compensation = mean(na.omit(ArrDelay - DepDelay))), by = .(Origin, Weekdays = DayOfWeek<6)]
Origin Weekdays Avg_DepDelay Avg_ArrDelay Compensation
IAH    FALSE     8.979730     4.116751    -4.825719
HOU    FALSE     7.120000     2.656566    -4.555556
IAH    TRUE      9.270948     6.281941    -2.836609
HOU    TRUE     11.631387    10.406593    -1.278388

这里对达美航空(Delta Air Lines)执飞的航班子集(在 “i” 中选择)按始发地与是否为工作日(在 “by” 中)进行了分组。同时还计算了空中弥补的时间(在 “j” 中)。结果显示,在周末,无论始发地是 IAH 还是 HOU,空中弥补的时间(即通过飞得更快而弥补的时间)更长。

data.table 包还有更多可探索的内容,但这篇文章展示了基础的 DT[i,j,by] 命令。DataCamp 课程对该包做了全面讲解。您可以在浏览器中按自己的节奏完成练习,并获得提示与反馈,随时回看视频与幻灯片。这种交互式学习方式能让您深入掌握 data.table 的知识与实战经验。免费试用。同时查看 DataCamp 的data.table R 包速查表。

希望通过本篇 data.table R 教程,您已经理解了 data.table 的基本语法,并准备好自行实践。如果您对 data.table 包有疑问,请到这里看看。Matt 和 Arun 非常活跃。接下来关于 data.table 的博客文章将更偏技术,深入探讨 data.table 的广泛用法,敬请期待!

主题