如何避免在以下代码中使用for循环来加快计算速度(实际数据大约大1e6倍)
id = rep(1:5, 20)
v = 1:100
df = data.frame(groupid = id, value = v)
df = dplyr::arrange(df, groupid)
bkt = rep(seq(0, 100, length.out = 4), 5)
id = rep(1:5, each = 4)
bktpts = data.frame(groupid = id, value = bkt)
for (i in 1:5) {
df[df$groupid == i, "bin"] = cut(df[df$groupid == i, "value"],
bktpts[bktpts$groupid == i, "value"],
include.lowest = TRUE, labels = F)
}
答案 0 :(得分:2)
我不确定bktpts
的格式为何?
但这是一个data.table解决方案,它应该(至少要比for循环快)。
library( data.table )
setDT(df)[ setDT(bktpts)[, `:=`( id = seq_len(.N),
value_next = shift( value, type = "lead", fill = 99999999 ) ),
by = .(groupid) ],
bin := i.id,
on = .( groupid, value >= value, value < value_next ) ][]
答案 1 :(得分:2)
另一种方式:
library(data.table)
setDT(df); setDT(bktpts)
bktpts[, b := rowid(groupid) - 1L]
df[, b := bktpts[copy(.SD), on=.(groupid, value), roll = -Inf, x.b]]
# check result
df[, any(b != bin)]
# [1] FALSE
有关滚动联接如何工作的信息,请参见?data.table
。
答案 2 :(得分:1)
我想到了另一个data.table
答案:
library(data.table) # load package
# set to data.table
setDT(df)
setDT(bktpts)
# Make a join
df[bktpts[, list(.(value)), by = groupid], bks := V1, on = "groupid"]
# define the bins:
df[, bin := cut(value, bks[[1]], include.lowest = TRUE, labels = FALSE), by = groupid]
# remove the unneeded bks column
df[, bks := NULL]
解释代码:
bktpts[, list(.(value)), by = groupid]
是一个新表,列表中有 每个value
的{{1}}值。如果您单独运行它,您将了解我们的前进方向。
groupid
会为bks := V1
中的变量bks
分配df
中存在的变量,这是上表中列表列的名称。当然V1
是我们进行连接的变量。
除了on = "groupid"
位之外,定义垃圾桶的代码几乎不需要解释。根据{{1}}函数的要求,它必须为bks[[1]]
才能访问列表值并提供向量。
编辑要添加:
所有data.table命令都可以在-难以理解的单个调用中链接:
[[