使用来自另一个DF的中断将数据分组

时间:2019-06-14 12:50:56

标签: r group-by data.table cut

如何避免在以下代码中使用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)
}

3 个答案:

答案 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命令都可以在-难以理解的单个调用中链接:

[[