使用来自另一个 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,在列表 中有 每个 groupid
的 value
值。如果你 运行 独自一人,你就会明白我们要去哪里。
bks := V1
将 df
中的变量 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
。
如何避免在下面的代码中使用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,在列表 中有 每个 groupid
的 value
值。如果你 运行 独自一人,你就会明白我们要去哪里。
bks := V1
将 df
中的变量 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
。