You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在R中基于频率表从截断多元正态分布抽样的实现方案

高效抽取符合指定区间频率的多元正态分布样本

需求说明

需要从指定参数的二元正态分布中抽取样本,参数如下:

mu = c(32, 170) # 第一个元素为年龄均值,第二个为身高均值
sigma = matrix(c(9, 10,
                10, 100), nrow = 2)

要求样本年龄符合以下频率分布:

age区间样本数量
[25,26)20
[30,31)43
[35,36)66

原尝试的循环抽样代码存在筛选条件错误(误将样本数量作为区间阈值),且反复抽样筛选的方式效率极低,甚至可能出现无限循环。

高效解决方案:利用条件正态分布直接抽样

不需要反复抽样筛选,而是基于条件多元正态分布的性质,直接生成符合要求的样本,步骤如下:

1. 推导条件分布参数

对于二元正态分布N(μ, Σ):

  • 年龄(X₁)的边缘分布:N(μ₁=32, σ₁₁=9)
  • 当已知X₁=x₁时,身高(X₂)的条件分布为:
    • 条件均值:$\mu_{2|1} = \mu_2 + \frac{\sigma_{12}}{\sigma_{11}}(x_1 - \mu_1)$
    • 条件方差:$\sigma_{2|1}^2 = \sigma_{22} - \frac{\sigma_{12}^2}{\sigma_{11}}$

代入参数计算:

mu1 <- mu[1]
mu2 <- mu[2]
sigma11 <- sigma[1,1]
sigma12 <- sigma[1,2]
sigma22 <- sigma[2,2]

# 条件分布参数
cond_mean <- function(x1) mu2 + (sigma12 / sigma11) * (x1 - mu1)
cond_var <- sigma22 - (sigma12^2) / sigma11

2. 生成符合区间要求的年龄样本

使用截断正态分布生成每个区间内的年龄样本,这里用truncnorm包的rtruncnorm函数实现:

# 安装并加载truncnorm包(如果未安装)
# install.packages("truncnorm")
library(truncnorm)

freq_table <- data.frame(age_low = c(25, 30, 35), n = c(20, 43, 66))

# 生成年龄样本
age_samples <- pmap(freq_table, function(age_low, n) {
  rtruncnorm(n = n, a = age_low, b = age_low + 1, mean = mu1, sd = sqrt(sigma11))
}) %>% unlist()

3. 生成对应的身高样本

基于每个年龄样本值,生成对应的身高样本:

height_samples <- rnorm(length(age_samples), mean = cond_mean(age_samples), sd = sqrt(cond_var))

4. 组合成最终样本矩阵

final_samples <- cbind(age_samples, height_samples)
head(final_samples)

用dplyr实现的简洁版本

如果习惯用管道式写法,可以用dplyr+purrr组合实现:

library(dplyr)
library(purrr)
library(truncnorm)

freq_table <- tibble(age_low = c(25, 30, 35), n = c(20, 43, 66))

final_samples <- freq_table %>%
  mutate(
    age = map2(age_low, n, ~rtruncnorm(.y, a = .x, b = .x + 1, mean = mu1, sd = sqrt(sigma11))),
    height = map(age, ~rnorm(length(.x), mean = cond_mean(.x), sd = sqrt(cond_var)))
  ) %>%
  unnest(c(age, height)) %>%
  as.matrix()

原代码问题说明

原代码的核心错误是筛选条件写错:

# 原错误条件:把样本数量n当成了年龄区间
sample[sample[,1] >= frqtable[row, 'n'] & sample[,1] < frqtable[row, 'n'] + 1,]
# 正确应该是用age列:
sample[sample[,1] >= frqtable[row, 'age'] & sample[,1] < frqtable[row, 'age'] + 1,]

即使修正条件,反复抽样筛选的方式效率极低,尤其是当目标区间的概率较小时,会浪费大量内存和时间,甚至陷入无限循环。而基于条件分布的方法直接生成符合要求的样本,完全避免了筛选步骤,效率提升显著。

内容的提问来源于stack exchange,提问作者Aku-Ville Lehtimäki

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 19:05:22