【问题标题】:vectorized parallel selection that's random?随机的矢量化并行选择?
【发布时间】:2013-07-22 00:28:48
【问题描述】:

我有两个向量“H”和“L”,它们有 200 个数值。我想创建一个名为“HL”的第三个向量,其中包含来自 H 和 L 的 200 个随机样本。但是,我希望它们被并行选择,就像 pmin 和 pmax 函数一样。

简化示例:

H <- 1:5
L <- 6:10

# rbind(H,L)
#   [,1] [,2] [,3] [,4] [,5]
# H    1    2    3    4    5
# L    6    7    8    9   10
# intended result is then a random pick from each 'column' shown above, e.g:

HL <- c(6,2,8,4,10)

有没有办法在不使用循环的情况下做到这一点?

任何建议将不胜感激 谢谢

【问题讨论】:

  • 你能举一个你想要的输出的具体例子吗?例如:如果HL 分别是1:56:10,并且您想在HL 中获得5 个随机样本,您将从该分析中得到什么?
  • 另外有带换样的吗?

标签: r vectorization random-sample


【解决方案1】:

您只需要来自伯努利(即 0 或 1)分布的 N 个样本,其中 N 是 H/L 中值的数量。然后,您可以使用抽样分别从 H 或 L 中进行选择。使用 ifelse 确保您需要的“并行选择”。

set.seed(1)
N <- length(H)
HorL <- rbinom(N, 1, 0.5)

# the select
results <- ifelse(HorL, H, L)

results
# [1]  6  7  3  4 10

这一切都总结为一个很好的衬里:

ifelse( rbinom(H, 1, 0.5), H, L)

来自@Arun: 一种(相对)更快的实现方式(不再需要ifelse)是:

idx <- which(!as.logical(rbinom(H, 1, 0.5)))
vv <- H
vv[idx] <- L[idx]

解释

@Gabriel,这个想法是您从两个选项之一中进行选择。您可以有效地掷硬币,如果是正面,则从 H 中选择,如果是反面,则从 L 中选择。这是伯努利分布,更一般的形式是二项分布。 R 可以提供这种方式的随机数。

因此,我们向 R 询问其中许多 N,然后相应地从 H 或 L 中选择。

“从 .. 中选择”部分是 R 诡计。

  • 请注意,我们可以将0, 1 视为TRUE, FALSEA, B 等。

  • 使用ifelse 方法应该有点不言自明。如果为 TRUE,则从一个来源中选择,如果为 FALSE,则从另一个来源中选择。

Arun 的方法更有创意。他的方法使用相同的“抛硬币”机制在集合之间进行选择,但具有速度优势。 (我们说的是纳秒,但仍然如此)。 他的方法本质上说:

  • 从一组开始,比如说 H。
  • 掷硬币。
  • 只要硬币是尾巴,就将 H 的那个元素替换为 L 的相同索引元素。 (请注意,“相同索引”方面就是您所说的“并行选择”)

【讨论】:

  • @Arun - 非常好的改进!大约一半的时间!
  • @Arun 感谢您的帮助。我是该网站的新手和“r”。我实际上对这里的支持感到不知所措。我使用了 Arun 的代码,但仍在努力理解它。尽管如此,最终的输出是我想要的:)
  • 也感谢@RicardoSaporta
  • 啊,这让我明白了很多,谢谢。由于输出总是相同的数字,我对其随机性有点困惑。但我意识到我的“set.seed”导致对象包含相同的整数。
【解决方案2】:
library(data.table)
set.seed(1350)

# Create an example data table:
dt <- data.table(ID=1:200,H=sample(1:1000,200),L=sample(1001:2000,200),key="ID")
# (If you already have a data frame 'df', you can use):
# dt <- as.data.table(df)

set.seed(5655)
# Add a column that randomly samples between H and L:
dt[,HL:=sample(c(H,L),1),by=ID]
dt

#       ID   H    L   HL
#  1:   1 837 1391 1391
#  2:   2 999 1573 1573
#  3:   3 566 1275  566
#  4:   4 347 1709 1709
#  5:   5 129 1627  129
# ---                  
#196: 196  67 1879 1879
#197: 197 652 1811 1811
#198: 198 569 1160 1160
#199: 199  17 1026   17
#200: 200 221 1500 1500

编辑 2: 如 cmets 中所指出的,如果 H 有重复项,我的初始答案将给出不正确的值。我添加了显示data.table 更快的时间,但是当我更正答案时,它确实要慢得多,正如 cmets 中所建议的那样。 (错误答案会更快,因为它是按重复值分组的,因此要考虑的行数要少得多...)

简而言之,我错了,你最好选择另一个答案。


这是一个合适的基准:

set.seed(1350) 

H <- sample(1:200, 200) 
L <- sample(201:400, 200)

usingDataTable <- quote({
  dt <- data.table(H, L)
  dt[,HL:=sample(c(H,L),1),by=H]
})


dt2 <- data.table(H, L)
usingDataTable.NoInitialize <- quote({
  dt2[,HL:=sample(c(H,L),1),by=H]
})

usingVectors <- quote ({
  ifelse( rbinom(H, 1, 0.5), H, L)
})



microbenchmark(eval(usingVectors), eval(usingDataTable), eval(usingDataTable.NoInitialize), times=100L)

Unit: microseconds
                              expr      min       lq   median        uq      max neval
                eval(usingVectors)   55.021   61.148   66.760   69.4605 1682.163   100
              eval(usingDataTable) 1635.676 1745.437 1795.245 1851.0950 3629.179   100
 eval(usingDataTable.NoInitialize) 1458.573 1537.618 1596.237 1669.3750 3683.756   100

【讨论】:

  • 虽然我很欣赏data.table 的使用,但这实际上可能是一个很好的例子,说明这样做会比不这样做。另请注意,当H 中有重复值时,使用by 会给您带来麻烦。我建议至少使用dt[, HL := ifelse(rbinom(.N, 1, 0.5), H, L)]
  • @Gabriel 虽然这是一个解决方案,但 Ricardo 的效率更高。您不需要为每个组采样。
  • @RicardoSaporta 和@Arun,我认为data.table 更有效率,而不是更少。如果我遗漏了什么,请告诉我......编辑:我刚刚看到你编辑的评论,@RicardoSaporta,你对 H 中的重复值是正确的。
  • @dnlbrky,我现在认为您的解决方案错误。您只有 2000 个唯一值,而您已经生成了一百万个值。因此,您的子集/分组仅发生 2000 次..(您的采样也是如此)...例如:试试这个:dt &lt;- data.table(H=c(1,1,1,2,2), L=1:5) 并检查您的结果。
  • data.table 相对于data.frame*ply 样式函数的许多方面要高效得多。对于更大的数据,它也明显更快。至于时间,请尝试使用microbenchmark,因为我从 data.table 中得到了 20x~30x 的减少
猜你喜欢
  • 2016-11-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-18
  • 2019-08-19
  • 1970-01-01
  • 1970-01-01
  • 2015-06-08
相关资源
最近更新 更多