本文介绍了如何变换均匀分布以便对特定分布进行抽样。

如果你要进行随机抽样,R语言提供了诸多现成的函数供你使用,比如:

  • runif: 均匀分布抽样
  • rbinom: 二项分布抽样
  • rpois: 泊松分布抽样
  • rnorm: 正态分布抽样
  • rexp: 指数分布抽样
  • rgamma: 伽马分布抽样

r语言画均匀分布图像 r语言均匀分布代码_抽样

那么,如果不用现成的函数,我们能自己实现抽样功能吗?比如,我们是否可以不用 rexp 函数而实现指数分布抽样?答案是肯定的,只需对均匀分布进行适当地变换即可。

指数分布抽样

下面的例子是对一个指数分布进行抽样,并绘制了三条 r语言画均匀分布图像 r语言均匀分布代码_指数分布_02

  • 用 runif 函数模拟指数分布抽样的 r语言画均匀分布图像 r语言均匀分布代码_r语言画均匀分布图像_03
  • 用R自带的 rexp 实现指数分布抽样的 r语言画均匀分布图像 r语言均匀分布代码_r语言画均匀分布图像_03
  • 指数分布的理论 r语言画均匀分布图像 r语言均匀分布代码_r语言画均匀分布图像_03

代码如下:

# Q1
N <- 100000
lambda <- 1   # 指数分布参数
set.seed(123)
x <- runif(N, 0, 1)
y <- log(1 - x) / -lambda   # 用runif模拟指数分布抽样
ey <- ecdf(y)
set.seed(123)
r <- rexp(N, lambda)     # R自带的rexp抽样函数
er <- ecdf(r)
plot(ey, xlim = c(0,3), col="red", 
     main="Generating Pseudo-Random Numbers Having\na Exponential Distribution with lambda=1",
     ylab="Cum prob F(x)")   # runif模拟抽样的c.d.f.曲线
lines(er, xlim=c(0,3), col="green")    # rexp模拟的c.d.f.曲线
curve(1 - exp(-lambda * x), from=0, to=3, add=T, col="blue")  # 指数分布的理论c.d.f.曲线
legend("bottomright", 
       legend=c("simulative (using runif)", "simulative (using rexp)", "theoretical"),
       col=c("red", "green", "blue"), lty=1, bty="n")

效果如下:

r语言画均匀分布图像 r语言均匀分布代码_R_06


可以看出,三条曲线几乎完全重合,说明用 runif 实现模拟指数分布抽样是可行的。实际上:

假设我们想要根据某个特定的概率分布(我们已经知道该分布的 r语言画均匀分布图像 r语言均匀分布代码_r语言画均匀分布图像_03r语言画均匀分布图像 r语言均匀分布代码_抽样_08)进行抽样,可以这样:首先我们对一个在r语言画均匀分布图像 r语言均匀分布代码_均匀分布_09上服从均匀分布的变量r语言画均匀分布图像 r语言均匀分布代码_r语言画均匀分布图像_10进行随机抽样,得到r语言画均匀分布图像 r语言均匀分布代码_均匀分布_11;然后我们构造一个变量r语言画均匀分布图像 r语言均匀分布代码_均匀分布_12,并据此计算得到 r语言画均匀分布图像 r语言均匀分布代码_r语言画均匀分布图像_13。那么,r语言画均匀分布图像 r语言均匀分布代码_r语言画均匀分布图像_13就是我们想要的抽样样本。因为实际上可以证明 r语言画均匀分布图像 r语言均匀分布代码_抽样_15r语言画均匀分布图像 r语言均匀分布代码_r语言画均匀分布图像_03 就是r语言画均匀分布图像 r语言均匀分布代码_抽样_08(证明过程在文末)。
上面r语言画均匀分布图像 r语言均匀分布代码_抽样_18其实就是 quantile 函数,值得注意的是当有很多个值r语言画均匀分布图像 r语言均匀分布代码_r语言画均匀分布图像_19时,r语言画均匀分布图像 r语言均匀分布代码_指数分布_20

也就是说,我们可以通过一种间接的方法,即变换均匀分布来对特定分布进行抽样。为什么要这样拐弯抹角呢?因为有时候我们碰到的分布不是很常见,R语言并没有提供相应的函数,这时候我们就可以用上述间接的方法自己写函数实现抽样。

变换均匀分布对一种特定分布抽样

比如,如果我们想要对下面这一个分布进行抽样,其r语言画均匀分布图像 r语言均匀分布代码_r语言画均匀分布图像_21是:
r语言画均匀分布图像 r语言均匀分布代码_r语言画均匀分布图像_22

R语言并没有提供一个现成的函数可以对上面的分布进行抽样。但是我们可以自行编写代码实现:

首先我们知道上面分布的 r语言画均匀分布图像 r语言均匀分布代码_指数分布_02是:
r语言画均匀分布图像 r语言均匀分布代码_指数分布_24
那么:
r语言画均匀分布图像 r语言均匀分布代码_R_25
按照上面的间接方法,我们首先对r语言画均匀分布图像 r语言均匀分布代码_r语言画均匀分布图像_26上的均匀分布进行抽样,得到一系列 r语言画均匀分布图像 r语言均匀分布代码_指数分布_27

N <- 100000     # 抽样的样本大小
set.seed(123)
x <- runif(N, 0, 1)

然后根据r语言画均匀分布图像 r语言均匀分布代码_指数分布_28计算出相应的 r语言画均匀分布图像 r语言均匀分布代码_抽样_29

y <- sqrt(x)    # 用runif模拟实现特定分布抽样

计算出的一系列 r语言画均匀分布图像 r语言均匀分布代码_抽样_29 值就构成了我们想要的抽样样本。我们可以比较一下用这种间接方法得出的模拟 r语言画均匀分布图像 r语言均匀分布代码_指数分布_02 曲线和理论 r语言画均匀分布图像 r语言均匀分布代码_指数分布_02

ey <- ecdf(y)
plot(ey, xlim = c(0,1), col="red", 
    main="Generating Pseudo-Random Numbers Having\na Specified Distribution",
    ylab="Cum prob F(x)")   # 模拟抽样的c.d.f.曲线
curve(x ^ 2, from=0, to=1, add=T, col="blue")  # 上述特定分布的理论曲线
legend("bottomright", legend=c("simulative", "theoretical"),
              col=c("red", "blue"), lty=1, bty="n")

结果如下:

r语言画均匀分布图像 r语言均匀分布代码_r语言画均匀分布图像_33


从图中可以看出,模拟曲线和理论曲线几乎完全重合,也就是说我们的间接方法的确很好地模拟了特定分布抽样。

上述特定分布的完整代码如下:

N <- 100000     # 抽样的样本大小
set.seed(123)
x <- runif(N, 0, 1)
y <- sqrt(x)    # 用runif模拟实现特定分布抽样
ey <- ecdf(y)
plot(ey, xlim = c(0,1), col="red", 
    main="Generating Pseudo-Random Numbers Having\na Specified Distribution",
    ylab="Cum prob F(x)")   # 模拟抽样的c.d.f.曲线
curve(x ^ 2, from=0, to=1, add=T, col="blue")  # 上述特定分布的理论曲线
legend("bottomright", legend=c("simulative", "theoretical"),
              col=c("red", "blue"), lty=1, bty="n")

我们用两个例子说明了可以用一种间接抽样的方法对特定分布进行抽样,不过这种方法是有前提的,即我们要知道r语言画均匀分布图像 r语言均匀分布代码_均匀分布_34的解析表达式,以及r语言画均匀分布图像 r语言均匀分布代码_均匀分布_34要存在一个反函数。由于这些限制,该方法在实际应用中有诸多限制。

Box-Muller方法:变换均匀分布对标准正态分布抽样

上面只是对一个均匀分布变量进行变换,我们也可以对多个均匀分布变量进行变换。比如,如果要对标准正态分布抽样,我们可以对r语言画均匀分布图像 r语言均匀分布代码_指数分布_36上的两个均匀分布变量r语言画均匀分布图像 r语言均匀分布代码_r语言画均匀分布图像_37r语言画均匀分布图像 r语言均匀分布代码_R_38做如下变换:
r语言画均匀分布图像 r语言均匀分布代码_抽样_39

即可以得到一个标准正态分布的抽样样本,该方法被称作Box-Muller方法

具体代码如下:

N <- 100000     # 抽样的样本大小
set.seed(123)
x <- runif(N, 0, 1)
y <- runif(N, 0, 1)
z <- cos(2 * pi * x) * sqrt(log(1 / y ^ 2))    # 用runif模拟实现标准正态分布抽样
ez <- ecdf(z)
set.seed(123)
r <- rnorm(N, 0, 1)     # 用rnorm模拟实现标准正态分布抽样
er <- ecdf(r)
plot(ez, col="red", 
     main="Generating Pseudo-Random Numbers Having\na Standard Normal Distribution",
     ylab="Cum prob F(x)")   # runif模拟抽样的c.d.f.曲线
lines(er, col="blue")   # 用rnorm模拟抽样的c.d.f.曲线
legend("bottomright", legend=c("simulative (using runif)", "simulative (using rnorm)"),
       col=c("red", "blue"), lty=1, bty="n")

r语言画均匀分布图像 r语言均匀分布代码_指数分布_02 曲线的结果如下:

r语言画均匀分布图像 r语言均匀分布代码_r语言画均匀分布图像_41


可以看出,两条曲线几乎完全重叠,说明变换的效果是成功的。

Probability Integral Transformation

最后,我们简单介绍一下Probability Integral Transformation,就是将上述间接方法的过程反过来,以实现一种伪随机数生成工具。具体来说,就是:

如果一个连续型变量r语言画均匀分布图像 r语言均匀分布代码_r语言画均匀分布图像_10r语言画均匀分布图像 r语言均匀分布代码_r语言画均匀分布图像_03r语言画均匀分布图像 r语言均匀分布代码_抽样_08,那么变量r语言画均匀分布图像 r语言均匀分布代码_r语言画均匀分布图像_45r语言画均匀分布图像 r语言均匀分布代码_均匀分布_09上服从均匀分布。

补充证明

最后,我们给出上文的一个结论的证明:

假设我们想要根据某个特定的概率分布(我们已经知道该分布的 r语言画均匀分布图像 r语言均匀分布代码_r语言画均匀分布图像_03r语言画均匀分布图像 r语言均匀分布代码_抽样_08)进行抽样,可以这样:首先我们对一个在r语言画均匀分布图像 r语言均匀分布代码_均匀分布_09上服从均匀分布的变量r语言画均匀分布图像 r语言均匀分布代码_r语言画均匀分布图像_10进行随机抽样,得到r语言画均匀分布图像 r语言均匀分布代码_均匀分布_11;然后我们构造一个变量r语言画均匀分布图像 r语言均匀分布代码_均匀分布_12,并据此计算得到 r语言画均匀分布图像 r语言均匀分布代码_r语言画均匀分布图像_13。那么,r语言画均匀分布图像 r语言均匀分布代码_r语言画均匀分布图像_13就是我们想要的抽样样本。因为实际上可以证明 r语言画均匀分布图像 r语言均匀分布代码_抽样_15r语言画均匀分布图像 r语言均匀分布代码_r语言画均匀分布图像_03 就是r语言画均匀分布图像 r语言均匀分布代码_抽样_08
上面r语言画均匀分布图像 r语言均匀分布代码_抽样_18其实就是 quantile 函数,值得注意的是当有很多个值r语言画均匀分布图像 r语言均匀分布代码_r语言画均匀分布图像_19时,r语言画均匀分布图像 r语言均匀分布代码_指数分布_20

证明:
因为r语言画均匀分布图像 r语言均匀分布代码_r语言画均匀分布图像_37r语言画均匀分布图像 r语言均匀分布代码_指数分布_36上服从均匀分布,所以其r语言画均匀分布图像 r语言均匀分布代码_指数分布_02 是:
r语言画均匀分布图像 r语言均匀分布代码_r语言画均匀分布图像_64

因此,r语言画均匀分布图像 r语言均匀分布代码_R_38r语言画均匀分布图像 r语言均匀分布代码_指数分布_02 是:
r语言画均匀分布图像 r语言均匀分布代码_R_67

注意,从第二个等式到第三个等式成立是因为r语言画均匀分布图像 r语言均匀分布代码_均匀分布_34是一个r语言画均匀分布图像 r语言均匀分布代码_指数分布_02,所以r语言画均匀分布图像 r语言均匀分布代码_均匀分布_34是一个单调不减函数,再加上r语言画均匀分布图像 r语言均匀分布代码_抽样_71

也就是说,r语言画均匀分布图像 r语言均匀分布代码_R_38r语言画均匀分布图像 r语言均匀分布代码_指数分布_02 就是r语言画均匀分布图像 r语言均匀分布代码_均匀分布_34。证毕。