欢迎来到 SO。
我建议查看dplyr 和data.table 包,它们专注于快速和内存高效的实现。特别是我会建议this question 的惊人答案,这将很好地理解这两个包的功能。
data.table 确实倾向于优于 dplyr 随着组和重复子集数量的增长,因为它使用索引和键控子集,但对于大多数情况来说,它归结为偏好。专注于子集,我将提供一个可重复的示例和一些速度比较。
可重现的例子
set.seed(1)
df <- data.frame(group = sample(LETTERS, 1e7, TRUE),
random_numbers = rnorm(1e7),
random_binaries = rbinom(1e7, 1, 0.3))
# size = 152.6 MiB
format(object.size(df), units = "MiB")
方法:
Base-R 方法
现在在 base-R 中,子集可以通过多种方式执行,一种是您自己展示的方式。 df[df == ..]。另一种方法是使用subset 函数,但这是一个实用函数,并且专注于可读性而不是速度,并且通常会执行得更差。下面给出了它们的使用示例。但是,可以使用which 函数将逻辑向量转换为索引,这样做可能会提高性能。
df[df$group == "C",]
#Equivalent
df[which(df$group == "C"),]
#Equivalent
subset(df, group == "C")
dplyr 方法
另一种选择是 dplyr 包。 dplyr 是语法糖,提供管道选项与其他一些包(例如magrittr 包)不同,但不同的基准测试(显示在first link 中)表明该包可用于提高各个方面的性能。但是我不是这个包的专家,因为我倾向于使用data.table 包。该软件包提供了%>% 管道函数和一些实用函数,例如filter,可用于子集数据
library(dplyr)
df %>% filter(group == "C")
# subsetting two columns
df %>% filter(group == "C", random_binaries == TRUE) #Equivalent to group == "C" & random_binaries == TRUE
Data.table 方法:
最后一个流行的包是data.table 包。该软件包专为提高性能和内存效率而设计,例如dplyr。该语法被设计为类似于 SQL 语句(select、from、where、group by),但从语法开始可能会有点混乱。该包提供了一个新的 data.table 类,而不是 data.frame 类,后者的子集化速度非常慢。
但是,几乎可以完全忽略包的语法,因为data.table 在大多数情况下使用data.frame 语法,并且在任何情况下都可以用作data.frame。
library(data.table)
#Convert the data.frame to data.table
setDT(df)
data.table 有两种标准方法:使用 indices 和使用 keys。如果使用与data.frame 方法类似的方法,则使用索引:
df1 <- df[random_binaries == TRUE]
df2 <- df[group == "C"]
索引在第一次使用时的速度大致相同,但在每次后续使用时会提高性能。
Keys 用于对data.table 进行预排序,从而实现智能子集。设置密钥确实需要一些时间,并且语法略有不同,但优于其他方法(尽管索引速度相似)
#Set the key using either setkey, or setkeyv (multiple columns)
setkeyv(df, c("group", "random_binaries"))
#Subset on group
df[.("C")]
#subset on random_binaries
df[CJ(group, TRUE, unique = TRUE)]
df[.(unique(group), TRUE)]
# Subset on multiple conditions
df[.(c("C", "H"), c(TRUE, TRUE))]
语法可能令人困惑,但可以查看它们有用的 wiki page 或许多 stackoverflow posts(截至今天为 8968 个),它们提供了大多数问题的答案。
性能对比
我检查了所提供的子集方法的性能,如下所示。可视化显示了使用所示方法的group == "C" 和group == "H" & random_binaries == TRUE" 子集的各种方法。 x 轴以毫秒为单位表示运行时间,y 轴显示方法。
斑点的宽度表示范围,而斑点的大小表示范围内的次数密度。
从可视化中可以看出,对于 2 列的数据集,在 1 列和 2 列上进行子集化,使用键的 data.table 方法要快得多(标记为 data.table_.._keyed),而使用索引则略胜一筹其余方法。使用subset 比标准方法慢,令人惊讶的是,对于这个插图dplyr 比base-R 慢,但这可能是由于我对包没有经验。