【发布时间】:2021-08-04 10:52:33
【问题描述】:
在这个例子中,我有一个带有两个变量的小标题:
- 组变量
gr - 感兴趣的变量
val
set.seed(123)
df <- tibble(gr = rep(1:3, each = 10),
val = gr + rnorm(30))
目标
我想使用函数findInterval 生成val 的离散化版本,但断点应该是gr 特定的,因为在我的实际数据以及本示例中,val 的分布取决于在gr。使用val 的四分位数在每个组内确定断点。
我做了什么
我首先构造一个嵌套的 tibble,其中包含 gr 的每个值的断点向量:
df_breakpoints <- bind_cols(gr = 1:3,
purrr::map_dfr(1:3, function(gr) {
c(-Inf, quantile(df$val[df$gr == gr], c(0.25, 0.5, 0.75)), Inf)
})) %>%
nest(bp = -gr) %>%
mutate(bp = purrr::map(.$bp, unlist))
然后我和df一起加入:
df <- inner_join(df, df_breakpoints, by = "gr")
我对定义离散化变量lvl 的第一个猜测是
df %>% mutate(lvl = findInterval(x = val, vec = bp))
它会产生错误
Error : Problem with `mutate()` input `lvl2`.
x 'vec' must be sorted non-decreasingly and not contain NAs
ℹ Input `lvl` is `findInterval(x = val, vec = bp)`.
然后我尝试了
df$lvl <- purrr::imap_dbl(1:nrow(df),
~findInterval(x = df$val[.x], vec = df$bp[[.x]]))
或
df %>% mutate(lvl = purrr::map2_int(df$val, df$bp, findInterval))
确实有效。然而,它是非常低效的。使用我的实际数据(120 万行)需要几分钟才能运行。我想有比在行上迭代更好的方法。有什么想法吗?
【问题讨论】:
-
几个替代方案:How to quickly form groups (quartiles, deciles, etc) by ordering column(s) in a data frame,包括下面显示的替代方案,以及便利功能
dplyr::ntile。先做group_by。