如何在RStan中实现贝叶斯足球模型?RJAGS可行但RStan报错
贝叶斯足球进球模型的RStan实现修正
原代码核心问题总结
- Stan不允许变量名包含
.,需替换为下划线(如att.star→att_star) att、def为球队级参数,需声明为向量而非单个实数- for循环缺少大括号,导致部分代码未纳入循环范围
- Stan正态分布参数为均值+标准差,而非JAGS的均值+精度,需做转换
- 参数中心化逻辑需放在
transformed parameters块实现,不能直接在model块赋值 - 未提前声明
lambda_home、lambda_away的变量类型
修正后的完整RStan代码(保存为football_model.stan)
// 数据块 data { int<lower=1> n_games; int<lower=1> n_teams; int<lower=0> FTHG[n_games]; // 主场进球数 int<lower=0> FTAG[n_games]; // 客场进球数 int<lower=1, upper=n_teams> HomeTeam[n_games]; // 主场球队索引 int<lower=1, upper=n_teams> AwayTeam[n_games]; // 客场球队索引 } // 参数块:声明原始参数 parameters { real home_adv; // 主场优势 real mu; // 进攻/防守参数的先验均值 real<lower=0> tau; // 进攻/防守参数的先验精度(转换为标准差使用) vector[n_teams] att_star; // 未中心化的进攻参数 vector[n_teams] def_star; // 未中心化的防守参数 } // 转换参数块:实现中心化约束与进球率计算 transformed parameters { vector[n_teams] att; // 中心化后的进攻参数 vector[n_teams] def; // 中心化后的防守参数 matrix[n_teams, n_teams] lambda_home; // 主场进球率矩阵 matrix[n_teams, n_teams] lambda_away; // 客场进球率矩阵 // 中心化:让所有球队的进攻/防守参数均值为0 att = att_star - mean(att_star); def = def_star - mean(def_star); // 计算每场比赛的进球率 for (home_i in 1:n_teams) { for (away_i in 1:n_teams) { lambda_home[home_i, away_i] = exp(home_adv + att[home_i] - def[away_i]); lambda_away[home_i, away_i] = exp(att[away_i] - def[home_i]); } } } // 模型块:定义似然和先验 model { // 似然:进球数服从泊松分布 for (i in 1:n_games) { FTHG[i] ~ poisson(lambda_home[HomeTeam[i], AwayTeam[i]]); FTAG[i] ~ poisson(lambda_away[HomeTeam[i], AwayTeam[i]]); } // 先验分布:转换JAGS的精度为Stan的标准差 home_adv ~ normal(0.2, sqrt(1/0.0625)); mu ~ normal(0, sqrt(1/0.0625)); tau ~ gamma(0.01, 0.01); att_star ~ normal(mu, sqrt(1/tau)); def_star ~ normal(mu, sqrt(1/tau)); }
对应的R预处理与运行代码
# 1. 获取并整理数据 data_set = read.csv('https://www.football-data.co.uk/mmz4281/2223/E0.csv') data_set = data_set[, c('HomeTeam', 'AwayTeam', 'FTHG', 'FTAG')] # 2. 预处理为Stan输入格式 teams = unique(c(data_set$HomeTeam, data_set$AwayTeam)) stan_data = list( n_games = nrow(data_set), n_teams = length(teams), FTHG = data_set$FTHG, FTAG = data_set$FTAG, HomeTeam = as.numeric(factor(data_set$HomeTeam, levels = teams)), AwayTeam = as.numeric(factor(data_set$AwayTeam, levels = teams)) ) # 3. 编译并运行Stan模型 library(rstan) rstan_options(auto_write = TRUE) options(mc.cores = parallel::detectCores()) model = stan_model("football_model.stan") fit = sampling(model, data = stan_data, chains = 3, warmup = 1000, iter = 2000, thin = 10) # 查看模型结果 print(fit, pars = c("home_adv", "att", "def", "mu", "tau"))
关键修正说明
- 命名规范:严格遵循Stan变量命名规则,替换所有
.为_ - 维度匹配:将球队级参数声明为
vector[n_teams],匹配球队数量 - 分布转换:JAGS的
dnorm(均值, 精度)对应Stan的normal(均值, 1/sqrt(精度)) - 逻辑拆分:将参数中心化、进球率计算移到
transformed parameters块,确保Stan正确识别计算逻辑 - 语法严谨:给for循环添加大括号,避免代码执行范围错误
- 变量声明:提前声明矩阵类型的进球率变量,消除编译时的未定义错误
内容的提问来源于stack exchange,提问作者Juan
相关产品推荐
相关产品推荐

