我有一个很大的data.frame(50M行(,需要找到与多列条件匹配的索引。
我做了一个玩具例子来说明这个问题。
我在这里读到搜索data.tables比data.frames更快,所以我将data.frame转换为data.table对象。
cases<-c(1,3,5)
women<-c("Julia", "Judith", "Juno", "Jane", "Joanna")
data.df<-data.frame("id" = 1:5, "age" = c(20, 30, 40, 50, 60), "name" = c("Joanna","Joe", "Julia", "Juno", "John"))
library(data.table)
data.dt<-as.data.table(data.df)
setkey(data.dt, "id")
我希望结果向量包含通过多列条件的记录的年龄值,在本例中为 20、40、NA。我使用 for 循环进行搜索(这可能是一种愚蠢的方法,任何提示都将不胜感激(
results<-vector()
for (i in 1:length(cases)){
which_id<-cases[i]
ind<-data.dt[id==which_id & name %in% women, which=TRUE]
if(length(ind)==0){results[i]<-NA}
else{results[i]<-data.dt$age[ind]}
}
这将在一个较小的数据集上完成,但是在案例中有500K条记录,在data.df中有50M条记录,这将需要超过12个小时才能运行。一定有更简单的方法,谁能给一个提示?
如果我理解正确,OP 希望首先通过id
过滤他的数据集,然后在women
中找到name
时返回age
(否则NA
(。
以下是返回预期结果的不同data.table
方法
20 40 NA
对于示例案例。但是,生产数据集的性能可能会有所不同。
1.按id
过滤,women
匹配
setkey(data.dt, id)
data.dt[cases][name %in% women, Age := age][, Age]
这里使用整数匹配,因为name
已被 OP 对data.frame()
的调用转换为因子。(如果name
是字符类型%chin%
则可以使用(。
为了确保在没有匹配的情况下返回NA
,使用默认NA
的更新。
请注意,Cole 的方法data.dt[J(cases)][name %in% women]
只会返回包含 2 行的过滤数据集,而不是预期的结果。
2.按id
过滤,与women
连接
这与上面类似,但使用连接而不是匹配:
setkey(data.dt, id)
data.dt[cases][.(women), on = .(name = V1), Age := age][, Age]
3. 加入women
,然后加入cases
此方法选择name
首先匹配women
的行(通过连接(,然后与cases
右连接,以便每个事例在结果向量中都有其相应的条目:
setkey(data.dt, id)
data.dt[.(women), on = .(name = V1), nomatch = 0L][cases, age]
讨论
OP 指出,速度是 500k 个元素(cases
(和 50M 行(data.df
(的 50M 行的生产数据集的一个问题。以上哪种方法对于生产数据集最快可能还取决于women
中的条目数。
如果没有适当的基准测试,我不愿意推荐其中一种方法。
我假设您希望ind
长度为 1 或 0(意味着 ID 都不同(。
然后,您可以使用 {dplyr} 非常快速地完成此操作:
library(dplyr)
results2 <- data.df %>%
slice(match(cases, id)) %>%
mutate(res = ifelse(name %in% women, age, NA)) %>%
pull(res)
也许你可以试试下面的代码
(v<-data.df[cases,])$age[match(v$name,women)>0]
或
(v<-data.dt[cases,])$age[match(v$name,women)>0]