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

PyStan3预测结果提取与可视化问题求助

问题描述

在科研项目中,我已将R-Stan脚本转换为Python PyStan3代码,独立完成了Stan模型编写、PyStan调用及演示数据准备,模型可正常运行,但存在三个问题:

  • 不知如何绘制观测变量与预测变量的XY图表
  • 无法根据给定的x值提取对应的预测变量
  • 将fit对象转为DataFrame后,对各列含义模糊,无法区分因变量、自变量/预测变量
import stan
import numpy as np

model_code_2s = '''
        data {
          int<lower=0> N; // number of measurements
        
          vector[N] h;
          vector[N] Q_meas;
          vector<lower=0>[N] Q_error;
          
          real mu_a1;
          real sigma_a1;
          real mu_b1;
          real sigma_b1;
          real mu_c1;
          real sigma_c1;
          real mu_k1;
          real sigma_k1;
          real mu_a2;
          real sigma_a2;
          real mu_c2;
          real sigma_c2;
          
          int<lower=0> N_ext;
          vector[N_ext] h_ext;
        }
        
        parameters {
          real<lower=0> a1;
          real<upper=min(h)> b1;
          real<lower=0> c1;
          
          real<lower=0> a2;
          real<lower=0> c2;
          
          real<lower=b1> k1;
          
          real<lower=0> sigma;
        }
        
        transformed parameters {
        
          real b2 = k1 - pow(a1/a2,1/c2)*pow(k1-b1,c1/c2);
          
          vector[N] log_mu;
          
          for (i in 1:N) {
            if (h[i] <= k1) {
              log_mu[i] = log(a1) + c1*log(h[i]-b1);
            } else {
              log_mu[i] = log(a2) + c2*log(h[i]-b2);
            }
          }
          
          vector[N] mu = exp(log_mu);
          vector[N] sigma_mu = mu*sigma;
          
          vector[N_ext] mu_ext;
          
          for (i in 1:N_ext) {
            if (h_ext[i] <= k1) {
              mu_ext[i] = a1*(h_ext[i] - b1)^c1;
            } else {
              mu_ext[i] = a2*(h_ext[i] - b2)^c2;
            }
          }
        
        }
        
        model {
        
          // priors
          a1 ~ normal(mu_a1, sigma_a1);
          b1 ~ normal(mu_b1, sigma_b1);
          c1 ~ normal(mu_c1, sigma_c1);
          
          k1 ~ normal(mu_k1, sigma_k1);
          
          a2 ~ normal(mu_a2, sigma_a2);
          c2 ~ normal(mu_c2, sigma_c2);
          
          sigma ~ uniform(0,10000);
          
          // likelihood
          Q_meas ~ normal(mu, ((sigma_mu)^2 + Q_error^2)^0.5);
        
        }
        
        generated quantities {
          
          vector[N] Q_res; // residuals Q_res = Q_meas - Q_mu (accounting for uncertainty in Q_meas)
        
          for (i in 1:N) {
            Q_res[i] = normal_rng(Q_meas[i],Q_error[i]) - normal_rng(mu[i],sigma_mu[i]);
          }
          
          vector[N_ext] Q_ext;
          
          for (i in 1:N_ext) {
            if (h_ext[i] <= k1) {
              Q_ext[i] = normal_rng(a1*(h_ext[i]-b1)^c1,mu_ext[i]*sigma);
            } else {
              Q_ext[i] = normal_rng(a2*(h_ext[i]-b2)^c2,mu_ext[i]*sigma);
            }
          }
          
          
        }
'''
h = [3600.0, 2000.0, 1400.0, 1300.0]
Q_meas = [1110.2, 194.4, 90.6, 69.1]
Q_error = [61.3, 8.7, 4.8, 3.7]

N = len(h)
h_ext = np.arange(1300.0, 3600.0, 10) # min(h) to max(h) with 0.1 step
N_ext = len(h_ext)

data_to_fit = {
        'N': N,
        'h': h,
        'Q_meas': Q_meas,
        'Q_error': Q_error,
        'mu_a1': 20, 
        'sigma_a1': 10,
        'mu_b1': 0,
        'sigma_b1': 5,
        'mu_c1': 2,
        'sigma_c1': 0.5,
        'mu_k1': 4,
        'sigma_k1': 2,
        'mu_a2': 20,
        'sigma_a2': 10,
        'mu_c2': 2,
        'sigma_c2': 0.5,      
        'N_ext': N_ext,
        'h_ext': h_ext
        }

posterior = stan.build(model_code_2s, data = data_to_fit, random_seed = 42)
fit = posterior.sample(num_chains = 4, num_samples = 5000, num_warmup = 1000)

解决方案

1. 绘制观测变量与预测变量的XY图表

使用matplotlib绘制观测值、拟合值及扩展预测区间,代码如下:

import matplotlib.pyplot as plt

# 提取观测点拟合均值的统计量
mu_mean = fit['mu'].mean(axis=0)
mu_low = np.percentile(fit['mu'], 2.5, axis=0)
mu_high = np.percentile(fit['mu'], 97.5, axis=0)

# 提取扩展预测的统计量
q_ext_mean = fit['Q_ext'].mean(axis=0)
q_ext_low = np.percentile(fit['Q_ext'], 2.5, axis=0)
q_ext_high = np.percentile(fit['Q_ext'], 97.5, axis=0)

plt.figure(figsize=(10,6))
# 绘制原始观测点
plt.scatter(h, Q_meas, color='red', label='观测值')
# 绘制观测点的拟合置信区间与均值线
plt.fill_between(h, mu_low, mu_high, alpha=0.2, color='blue')
plt.plot(h, mu_mean, color='blue', label='观测点拟合均值')
# 绘制扩展预测的置信区间与均值线
plt.fill_between(h_ext, q_ext_low, q_ext_high, alpha=0.2, color='green')
plt.plot(h_ext, q_ext_mean, color='green', label='扩展预测均值')

plt.xlabel('h(自变量)')
plt.ylabel('Q(因变量)')
plt.legend()
plt.show()

2. 根据给定x值提取对应预测变量

通过匹配目标x值在h_ext中的位置,提取对应预测值的采样数据:

# 设定目标h值
h_target = 2500.0

# 找到h_ext中最接近目标值的索引
target_idx = np.argmin(np.abs(h_ext - h_target))

# 提取该位置的所有预测采样值
q_target_samples = fit['Q_ext'][:, target_idx]
# 计算均值与95%置信区间
q_target_mean = q_target_samples.mean()
q_target_ci = np.percentile(q_target_samples, [2.5, 97.5])

print(f"h={h_target}时,Q的预测均值:{q_target_mean:.2f},95%置信区间:{q_target_ci}")

3. 理解fit转DataFrame后的列含义

先将fit对象转为DataFrame,再按Stan代码块区分列的含义:

import pandas as pd

df = fit.to_frame()
# 查看所有列名
print(df.columns.tolist())

列名对应规则:

  • 模型参数列:对应Stan代码parameters块中的变量,如a1、b1、k1、sigma等
  • 转换参数列:对应transformed parameters块中的变量,如b2、mu[i](第i个观测点的拟合均值)、mu_ext[j](第j个扩展h值的预测均值)
  • 生成量列:对应generated quantities块中的变量,如Q_res[i](第i个观测点的残差)、Q_ext[j](第j个扩展h值的预测采样值)

变量类型区分:

  • 自变量:你输入的h、h_ext(不属于fit的列,是外部数据)
  • 因变量观测值:你输入的Q_meas(外部数据)
  • 因变量预测值:mu(观测点拟合均值)、Q_ext(扩展点预测采样)
  • 模型参数:a1、b1、c1等用于构建模型的参数

内容的提问来源于stack exchange,提问作者DG_

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 16:36:18