如何使用R包“sensitivity”执行Sobol敏感性分析?

20jt8wwn  于 2023-06-19  发布在  其他
关注(0)|答案(1)|浏览(247)

我想使用R包“sensitivity”执行Sobol敏感性分析。但是,我不确定如何在sobol函数中创建第一个和第二个随机样本(X1, X2)。我假设X1X2都是输入数据的子集,然而,最终结果似乎是不正确的。

library(tidymodels)
library(sensitivity)

# Example data
set.seed(123)
x1 = runif(100)
x2 = runif(100)
x3 = runif(100)
y = 3 * x1 + 2 * x2 + x3 + rnorm(100)
data <- data.frame(x1, x2, x3, y)

# Split data into training and testing sets
set.seed(234)
data_split <- initial_split(data, prop = 0.8)
train_data <- training(data_split)
test_data <- testing(data_split)

# Create a linear regression model using tidymodels
lm_spec <- linear_reg() %>%
  set_engine("lm") %>%
  set_mode("regression")

lm_fit <- lm_spec %>%
  fit(y ~ x1 + x2 + x3, data = train_data)

# Define a model function
model_function <- function(x) {
  new_data <- data.frame(x1 = x[, 1], x2 = x[, 2], x3 = x[, 3])
  predict(lm_fit, new_data)$`.pred`
}

# Perform Sobol sensitivity analysis
set.seed(345)
X_index1 = sample(x=1:100, size = 50, replace = FALSE)
X_index2 = c(1:length(data$x1))[-X_index1]

sobol_results <- sobol(model = model_function, 
                       X1 = data[X_index1, -4], 
                       X2 = data[X_index2, -4], 
                       nboot = 1000, order = 2)

sobol_results

sobol_results表明灵敏度按以下顺序排列:x2> x1> x3。根据函数y = 3 * x1 + 2 * x2 + x3 + rnorm(100),“x1”应该具有更高的灵敏度,因为它是3 * x1。我应该如何纠正我的代码?谢谢你。

wkyowqbh

wkyowqbh1#

根据我对sensitivity#sobol() function的理解,它需要以特定的方式编写模型。它期望一个函数接受一个输入矩阵并返回一个输出向量。在您的示例中,model_function的定义似乎是正确的。参见RDocumentation
但是sobol函数还需要两组样本,X1X2,它们是矩阵,其中每行是输入的独立实现。它们应该从输入的联合分布中生成,而不是现有数据的子集。
您当前的代码将X1X2视为数据的子集,这是不正确的。
在您的原始代码中,创建X1X2如下:

set.seed(345)
X_index1 = sample(x=1:100, size = 50, replace = FALSE)
X_index2 = c(1:length(data$x1))[-X_index1]

sobol_results <- sobol(model = model_function, 
                       X1 = data[X_index1, -4], 
                       X2 = data[X_index2, -4], 
                       nboot = 1000, order = 2)

这意味着X_index1X_index2是通过对原始数据集的索引进行采样而创建的。X_index1是50个索引的随机样本,X_index2是所有剩余索引的集合。然后使用这些索引将原始数据集子集划分为X1X2
因此,X1X2不是来自输入变量联合分布的独立样本。相反,它们是原始数据的子集。
但是,sensitivity包中的sobol函数要求X1X2是来自输入变量联合分布的独立样本。这是必要的,因为sobol函数使用X1X2来系统地改变每个输入变量,同时保持其他变量不变,以估计模型输出对每个输入变量的敏感度。
X1X2是数据的子集时,它们不能独立地覆盖输入变量的联合分布,这可能导致不正确的敏感性估计。这就是您获得的Sobol敏感性分析结果与基于模型系数的预期不匹配的原因。
您应该从输入变量的联合分布中生成X1X2作为独立样本。如果你的输入变量均匀分布在0和1之间(就像你的例子中一样),你可以生成X1X2,如下所示:

n <- 1000  # number of samples to generate
X1 <- matrix(runif(3*n), ncol = 3)
X2 <- matrix(runif(3*n), ncol = 3)

然后可以将这些作为X1X2参数传递给sobol函数。
类似于:

library(tidymodels)
library(sensitivity)

# Example data
set.seed(123)
x1 = runif(100)
x2 = runif(100)
x3 = runif(100)
y = 3 * x1 + 2 * x2 + x3 + rnorm(100)
data <- data.frame(x1, x2, x3, y)

# Split data into training and testing sets
set.seed(234)
data_split <- initial_split(data, prop = 0.8)
train_data <- training(data_split)
test_data <- testing(data_split)

# Create a linear regression model using tidymodels
lm_spec <- linear_reg() %>%
  set_engine("lm") %>%
  set_mode("regression")

lm_fit <- lm_spec %>%
  fit(y ~ x1 + x2 + x3, data = train_data)

# Define a model function
model_function <- function(x) {
  new_data <- data.frame(x1 = x[, 1], x2 = x[, 2], x3 = x[, 3])
  predict(lm_fit, new_data)$`.pred`
}

# Perform Sobol sensitivity analysis
set.seed(345)
n <- 1000  # number of samples to generate
X1 <- data.frame(matrix(runif(3*n), ncol = 3))
X2 <- data.frame(matrix(runif(3*n), ncol = 3))

sobol_results <- sobol(model = model_function, 
                       X1 = X1, 
                       X2 = X2, 
                       nboot = 1000, order = 2)

sobol_results

此更新的代码使用runif functionX1X2生成代表输入变量的均匀随机数(假设它们均匀分布)。矩阵X1X2是来自输入变量联合分布的独立样本,这是sobol函数所需要的。
请记住,这样做的目的是了解输入变量的变化如何影响模型的输出。sobol函数通过一次系统地改变一个参数,同时保持其他参数不变来实现这一点,这些独立的样本X1X2实现了这一过程。
user10085在评论中写道:
我想知道data.frame(x1 = x[, 1], x2 = x[, 2], x3 = x[, 3])matrix(runif(3*n), ncol = 3)如何自动适应任何数据集?
要使此代码更灵活并适应任何数据集,您可以:

  • 定义为任意数量的变量生成独立随机样本的函数,
  • 另一个函数用于构造与输入数据结构相同的新数据框。

首先,您将添加num_varsfeature_names的计算:这些是更新代码中的新步骤。num_vars(输入变量的数量)计算为训练数据中的列数减1(以排除响应变量),feature_names(输入变量的名称)定义为训练数据中不包括响应变量的列名。
它们用于生成具有正确列数的X1X2,并确保在模型函数中创建的新数据框具有正确的列名。这些步骤使代码适用于具有任意数量输入变量的任何数据集。

num_vars <- ncol(train_data) - 1  # number of input variables
feature_names <- colnames(train_data)[1:num_vars]

然后更新X1X2的生成:在前面的校正代码中,X1X2是用硬编码的列数(3)生成的。在更新后的代码中,需要生成列数等于数据中输入变量数的列。这确保X1X2具有用于Sobol敏感性分析的正确结构,而不管数据中的输入变量的数量。

# Previous code
X1 <- matrix(runif(3*n), ncol = 3)
X2 <- matrix(runif(3*n), ncol = 3)

# Updated code
num_vars <- ncol(train_data) - 1  # number of input variables
X1 <- matrix(runif(num_vars*n), ncol = num_vars)
X2 <- matrix(runif(num_vars*n), ncol = num_vars)

最后,你可以修改model函数:在先前的校正代码中,模型函数是用三个输入变量的假设来定义的。它显式地构造了一个新的 Dataframe ,列为x1x2x3

在更新的代码中,模型函数现在将构造一个新的 Dataframe ,其列数等于数据中输入变量的数量,并使用feature_names向量分配正确的列名。这允许模型函数与任何数量的输入变量一起工作。

# Previous code
model_function <- function(x) {
  new_data <- data.frame(x1 = x[, 1], x2 = x[, 2], x3 = x[, 3])
  predict(lm_fit, new_data)$`.pred`
}

# Updated code
model_function <- function(x, model, feature_names) {
  new_data <- data.frame(matrix(x, ncol = length(feature_names)))
  colnames(new_data) <- feature_names
  predict(model, new_data)$`.pred`
}

最终结果将类似于:

model_function <- function(x, model, feature_names) {
  new_data <- data.frame(matrix(x, ncol = length(feature_names)))
  colnames(new_data) <- feature_names
  rownames(new_data) <- NULL  # Reset row names to ensure they are unique
  predict(model, new_data)$`.pred`
}

因此,完整的更新代码将是:

library(tidymodels)
library(sensitivity)

# Example data
set.seed(123)
x1 = runif(100)
x2 = runif(100)
x3 = runif(100)
y = 3 * x1 + 2 * x2 + x3 + rnorm(100)
data <- data.frame(x1, x2, x3, y)

# Split data into training and testing sets
set.seed(234)
data_split <- initial_split(data, prop = 0.8)
train_data <- training(data_split)
test_data <- testing(data_split)

# Create a linear regression model using tidymodels
lm_spec <- linear_reg() %>%
  set_engine("lm") %>%
  set_mode("regression")

lm_fit <- lm_spec %>%
  fit(y ~ ., data = train_data)  # Use . to include all other variables in the formula

# Define a model function
model_function <- function(x, model, feature_names) {
  new_data <- x
  colnames(new_data) <- feature_names
  predict(model, as.data.frame(new_data))$`.pred`
}

# Set up for Sobol sensitivity analysis
n <- 1000  # Number of samples
set.seed(345)
num_vars <- ncol(train_data) - 1  # number of input variables
feature_names <- colnames(train_data)[1:num_vars]

X1 <- matrix(runif(num_vars*n), ncol = num_vars)
X2 <- matrix(runif(num_vars*n), ncol = num_vars)

# Perform Sobol sensitivity analysis
sobol_results <- sobol(model = function(x) model_function(x, lm_fit, feature_names), 
                       X1 = X1, 
                       X2 = X2, 
                       nboot = 1000, order = 2)
sobol_results

这些修改使代码更加灵活,并可适应具有不同数量的输入变量的不同数据集。

相关问题