使用来自另一个 DF 的中断在组内对数据进行分类

Bin data within a group using breaks from another DF

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

我不确定为什么你的 bktpts 格式是这样的?

但是这里有一个 data.table 解决方案,应该(至少)比您的 for-loop.

快一点
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 ) ][]

我得出了另一个 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] 是一个新的 table,在列表 中有 每个 groupidvalue 值。如果你 运行 独自一人,你就会明白我们要去哪里。

bks := V1df 中的变量 bks 赋值给 V1 中存在的任何内容,这是前一个 table 中列表列的名称。当然 on = "groupid" 是我们进行连接的变量。

除了 bks[[1]] 位之外,定义 bin 的代码几乎不需要解释。它需要 [[ 才能访问列表值并提供向量,如 cut 函数所要求的那样。

编辑添加:

所有 data.table 命令都可以链接在一个 - 相当难以理解的 - 单个调用中:

df[bktpts[, list(.(value)), by = groupid], 
   bks := V1, 
   on = "groupid"][, 
                bin := cut(value, 
                           bks[[1]], 
                           include.lowest = TRUE, 
                           labels = FALSE), 
                by = groupid][, 
                             bks := NULL]

另一种方式:

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