我想从正态分布中采样50,000个值,其中mean = 0和sd -1。但我想将值限制为[-3,3]。我已编写代码来执行此操作,但不确定它是否最有效?希望得到一些建议。
lower <- -3
upper <- 3
x_norm<-rnorm(75000,0,1)
x_norm<-x_norm[which(x_norm >=lower & x_norm<=upper)]
repeat{
x_norm<-c(x_norm, rnorm(10000,0,1))
x_norm<-x_norm[which(x_norm >=lower & x_norm<=upper)]
if(length(x_norm) >= 50000){break}
}
x_norm<-x_norm[1:50000]
答案 0 :(得分:12)
您的代码之类的东西肯定会起作用,但是您大大高估了所需的值。鉴于它是一个已知的分布和相当多的样本,你知道有多少会出现多于或少于3个。
(1-pnorm(3))*2 * 50000
[1] 134.9898
所以,考虑到你可能只能在平局中获得大约135个超过50,000的范围,所以很容易得到更多,但仍然不是一个非常大的数字并修剪它。只需要少于或大于3的50,500中的前50,000个。
x <- rnorm(50500)
x <- x[x < 3 & x > -3]
x <- x[1:50000]
我跑了前2行40,000次,每次返回的长度大于50000。一个小的布尔检查可以保证它总是这样。
x <- 1
while (length(x) < 50000){
x <- rnorm(50500)
x <- x[x < 3 & x > -3]}
x <- x[1:50000]
对我来说,这几乎可以在6毫秒内执行100%的时间。这是一种在R中执行它的简单方法,它执行速度非常快,易于阅读,并且不需要添加。
答案 1 :(得分:11)
如果真的关心效率,那么这一小段Rcpp代码将难以击败。将以下内容存储在一个文件中,例如/tmp/rnormClamp.cpp
:
#include <Rcpp.h>
using namespace Rcpp;
// [[Rcpp::export]]
NumericVector rnormClamp(int N, int mi, int ma) {
NumericVector X = rnorm(N, 0, 1);
return clamp(mi, X, ma);
}
/*** R
system.time(X <- rnormClamp(50000, -3, 3))
summary(X)
*/
使用sourceCpp()
(也来自Rcpp)来构建并运行它。在我的电脑上实际绘制和夹紧大约需要4毫秒:
R> sourceCpp("/tmp/rnormClamp.cpp")
R> system.time(X <- rnormClamp(50000, -3, 3))
user system elapsed
0.004 0.000 0.004
R> summary(X)
Min. 1st Qu. Median Mean 3rd Qu. Max.
-3.00000 -0.67300 -0.00528 0.00122 0.68500 3.00000
R>
this previous SO answer by Romain中有clamp()
糖函数,其中还指出您需要Rcpp版本0.10.2。
编辑: Per Ben的暗示,我似乎误解了。这是C ++和R的混合:
// [[Rcpp::export]]
List rnormSelect(int N, int mi, int ma) {
RNGScope scope;
int N2 = N * 1.25;
NumericVector X = rnorm(N2, 0, 1);
LogicalVector ind = (X < mi) | (X > ma);
return List::create(X, ind);
}
哪一个可以附加到早期文件。然后:
R> system.time({ Z <- rnormSelect(50000, -3, 3);
+ X <- Z[[1]][ ! Z[[2]] ]; X <- X[1:50000]})
user system elapsed
0.008 0.000 0.009
R> summary(X)
Min. 1st Qu. Median Mean 3rd Qu. Max.
-3.00000 -0.68200 -0.00066 -0.00276 0.66800 3.00000
R>
我将重新审视逻辑索引和行子集,我必须查找它。明天吧。但是9毫秒仍然不算太糟糕:)
编辑2:看起来我们确实没有逻辑索引。我们必须添加这个。这个版本“手动”完成,但速度并不比R:
索引快// [[Rcpp::export]]
NumericVector rnormSelect2(int N, int mi, int ma) {
RNGScope scope;
int N2 = N * 1.25;
NumericVector X = rnorm(N2, 0, 1);
LogicalVector ind = (X >= mi) & (X <= ma);
NumericVector Y(N);
int k=0;
for (int i=0; i<N2 & k<N; i++) {
if (ind[i]) Y(k++) = X(i);
}
return Y;
}
输出:
R> system.time(X <- rnormSelect2(50000, -3, 3))
user system elapsed
0.004 0.000 0.007
R> summary(X)
Min. 1st Qu. Median Mean 3rd Qu. Max.
-2.99000 -0.66900 -0.00258 0.00223 0.66700 2.99000
R> length(X)
[1] 50000
R>
答案 2 :(得分:8)
John和Dirk给出了拒绝抽样的很好的例子,这对于给定的问题应该没问题。但是,为了给出另一种方法,当你有累积分布函数及其倒数(或其合理近似值)时,你只需从均匀分布和变换生成数据:
x <- qnorm( runif(50000, pnorm(-3), pnorm(3)) )
range(x)
hist(x)
对于给定的问题,我不认为这比拒绝采样方法好得多(如果更好),但如果你想从截断的法线0,1生成2到3之间的数据,那么这个方法会可能效率更高。它确实取决于累积及其倒数(在这种情况下为pnorm和qnorm),因此不会像没有那些容易获得的分布的拒绝抽样一样简单。