【问题标题】:Random numbers in a column based on another column's value基于另一列值的列中的随机数
【发布时间】:2019-06-06 09:48:46
【问题描述】:

大家好,

我正在努力创建一个列,该列将使用随机值生成值函数填充值,该值函数将另一列的值作为参数。

有点上下文 - 我有一个列中包含提前期的数据表:

library(data.table)

dt <- data.table(Item = rep(123456,each = 1000), LT = rnorm(1000,mean = 10, sd = 3))

还有一个功能:

rand_ddlt_norm <- function(Lt,mean,sd){
  sign(Lt) * ( sum( rnorm( floor(abs(Lt)), mean, sd) ) +
                 rnorm(1, mean, sd) * ( abs(Lt)%%1) )
}

上述函数旨在计算每行在提前期期间的需求。

很遗憾,我不能这样做:

dt[,ddlt := rand_ddlt_norm(LT, mean = 100, sd = 30)]

因为所有行都将填充相同的数字。

我显然可以将它放入一个循环中,但是对于 10,000 次迭代、20,000 多个产品和众多分布类型,计算时间变得越来越荒谬。

我非常欢迎有关如何在不运行循环的情况下优化此代码的任何建议。

【问题讨论】:

  • 您创建的函数请求变量LTest11est12。但是,当您尝试创建数据表时,您需要提供 LTmeansd。你的意思是在那儿也使用LTest11est12吗?
  • 拍得很好,谢谢,我的大脑已经不能工作了。这将教会我重新输入代码而不是粘贴

标签: r random data.table


【解决方案1】:

使用Vectorize() 向量化您的函数。

# data
library(data.table)

set.seed(1)

dt <- data.table::data.table(Item = rep(123456,each = 1000), LT = rnorm(1000,mean = 10, sd = 3))

# def function
rand_ddlt_norm <- function(Lt,est11,est12){
  sign(Lt) * ( sum( rnorm( floor(abs(Lt)), est11, est12) ) +
                 rnorm(1, est11, est12) * ( abs(Lt)%%1) )
}

rand_ddlt_norm <- Vectorize(rand_ddlt_norm) # vectorize it

dt[,ddlt := rand_ddlt_norm(LT, 100,30)]

结果:

> head(dt)
     Item        LT      ddlt
1: 123456  8.120639  845.6967
2: 123456 10.550930 1112.5837
3: 123456  7.493114  733.3808
4: 123456 14.785842 1516.8916
5: 123456 10.988523 1101.0449
6: 123456  7.538595  898.3760

【讨论】:

  • 建议set.seed(1) 使示例永远保持不变。
  • 这太棒了,我以前从未使用过 Vectorize()。我现在觉得它会成为我最好的朋友!
  • 问题是解决方案比直接对函数进行向量化要慢,这在数据很大的情况下会产生很大的影响
  • @denis 是的。您也可以使用purrr::map(),如dt[,ddlt := purrr::map(.x = LT, .f = function(x) rand_ddlt_norm(x, mean = 100, sd = 30))]。这比提供的两种解决方案都快。
  • 在创建dt之前复制set.seed(1),因为它也是随机生成的。
【解决方案2】:

我建议你直接矢量化你的函数:

rand_ddlt_norm_vec <- function(Lt,mean,sd){
  sign(Lt) * ( rowSums( t(sapply(1:length(Lt),function(x){rnorm(floor(abs(Lt)),mean,sd)})))  +
                 rnorm(length(Lt), mean, sd) * ( abs(Lt)%%1) )
}

Lt 现在是一个向量。这里

t(sapply(1:length(Lt),function(x){rnorm(floor(abs(Lt)),mean,sd)}))

创建一个行数与 Lt 相同,列数与floor(abs(Lt)) 相同的矩阵。然后使用Rowsum 获取向量。

与JdeMello的解决方案比较:

rand_ddlt_norm_vec2 <- Vectorize(rand_ddlt_norm)

library(microbenchmark)
library(data.table)

dt <- data.table(Item = rep(123456,each = 10000), LT = rnorm(10000,mean = 10, sd = 3))

    microbenchmark(
      denis = function(){dt[,ddlt := rand_ddlt_norm_vec(LT, mean = 100, sd = 30)]},
      jdeMello = function(){dt[,ddlt := rand_ddlt_norm_vec2(LT, mean = 100, sd = 30)]}
    )

Unit: nanoseconds
     expr min lq  mean median uq  max neval cld
    denis   0  0  0.24      0  0    1   100   a
 jdeMello   0  0 25.88      0  0 2566   100   a

此解决方案比 JdeMello 解决方案快 100 倍。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-16
    • 2021-09-01
    相关资源
    最近更新 更多