新发现的 R 里关于 data.table 的一些神奇用法
Jun 15, 2021
2 minute read

R 语言中的 data.table 包可以理解为 data.frame 的高级版本,它比较适合适合用来处理大型数据集。

载入包并且读取一些数据,当然也可以使用 nrow 参数来决定读取多少行:

library(data.table)

mydata <- fread("some_kind_of_data.csv")
mydata <- fread("some_kind_of_data.csv", nrows = 10)

data.table 包提供了一个非常简洁的通用格式:mydata[i, j, by],可以理解为:对于数据集mydata,选取子集行i,通过by分组计算j。只需要记住,i是用来在行上进行操作的(比如筛选行),j 是用来在列上进行操作的(比如选择列或者根据计算创建新列)。对比dplyr等包来说,data.table的运行速度更快。

选择列

如果只需要选择列的话,可以只代入 j,但需要记住给 i 留出位置:

mydata[, j]

首先,通过列名选择列是,是否需要打双引号是一个值得考虑的问题,因为两者各有优劣。 data.table 支持打双引号的原生 R 的做法:

data1 <- mydata[, c("columnA", "columnB", "columnC", "columnD")]

但也可以使用不打双引号,这样会更方便(就好像 tidyverse 里面的 select),只不过需要用到 list

data1 <- mydata[, list(columnA, columnB, columnC, columnD)]

这里,第一个小技巧就来了,我们可以使用一个点代替 list

data1 <- mydata[, .(columnA, columnB, columnC, column)]

data.table 的中括号里,.() 就是 list()的简写形式。

如果你想使用一个已经存在的列名向量,比如:

mycols <- c("columnA", "columnB", "columnC", "columnD")

直接套用是行不通的:mydata[, mycols],而是需要在这个列名向量前面加两个点:

data1 <- mydata[, ..mycols]

当然,你可以借用命令行里面 .. 的含义去理解,可以认为是从括号里面的命名空间上升到全局变量了。

计算 data.table 行数

下一个标记是 .N,代表行数。可以用这个标记来得到数据集的总行数和使用 by 分组后的各亚组行数。 比如我们返回 iris 数据集的总行数和按照 Species 分组的行数:

diris <- as.data.table(iris)
diris[, .N]
# 150

diris[, .N, Species]
#       Species  N
# 1:     setosa 50
# 2: versicolor 50
# 3:  virginica 50

当然,使用多个变量进行分组也是可以的,同样用到 .

mydata[, .N, .(columnA, columnB)]

并且你还可以在后面使用 order,让行数结果从高到低排列:

mydata[, .N, .(columnA, columnB)][order(columnA, -N)]

这样的语句顺序也非常符合数据处理的逻辑。 当然,如果你使用习惯了 dplyr,你喜欢的写法 data.table 也可以模仿:

mydata %>%
  count(columnA, columnB) %>%
  order(columnA, -n)

mydata[, .N,
  .(columnA, columnB)][
    order(columnA, -n)
  ]

向 data.table 增加列

比如我们想要在 iris 数据集增加一列,看品种是否是 Virginica,如果是则为 TRUE,否则为 FALSE, 那么可以这样写:

diris[, IfVirginica := ifelse(Species %like% "virgin", TRUE, FALSE)]

这个 %like% 的用法有点类似于 SQL 里面的 LIKE。 而 := 的用法可能在其他编程语言中比较常见,这里代表通过增加新列改变了目前的数据集 diris, 而并非存入另外的新数据集。

当然也可以同时增加两列:

diris[, `:=`(
  IfVirginica := ifelse(Species %like% "virgin", TRUE, FALSE),
  IfVensicolor := ifelse(Species %like% "vensi", TRUE, FALSE)
)]

更多的操作符

%between%:找到目标的取值区间进行筛选

mydata <- diris[Species == "virginica" & Sepal.Length %between% c(4, 6)]

%chin%:类似原生 R 的 %in%,但只面向字符变量且速度更快

mydata <- diris[Species %chin% c("virginica", "vensicolor")]

fcase() 函数

最后要介绍的 fcase() 函数和 SQL 里面的 CASE WHEN 语句和 dplyr 里面的 case_when() 语句 类似,基本语法是 fcase(condition1, "value1", condition2, "value2"),比如下面的例子:

diris[, newSpeices := fcase(
  IfVirginic & !IfVensicolor, "Species1",
  !IfVirginic & IfVensicolor, "Species2",
  IfVirginic & IfVensicolor, "Both",
  !IfVirginic & !IfVensicolor, "Neither")]

最后,更多特殊符号的用法可以参考 help("special-symbols"),虽然文档可能讲得不是很清楚,但依然全面。