下面给出“参数敏感性分析(蜘蛛图)”的完整教学模块,本科零门槛使用:
• 学生勾选 5 个作物参数 → 平台自动做局部分析(Local Sensitivity)
• 输出:蜘蛛图(Radar Chart) + 柱状图
• 算法:OAT(One-At-a-Time)±10% 扰动 → 计算标准化敏感度
• 全程 点击即运行,无需写代码
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>参数敏感性分析</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
body { font-family: Arial; margin: 40px; background: #f9f9f9; }
.container { max-width: 900px; margin: auto; background: #fff; padding: 30px; border-radius: 8px; box-shadow: 0 0 10px rgba(0,0,0,0.1); }
h1 { text-align: center; color: #2e7d32; }
.chk-group { display: flex; flex-wrap: wrap; gap: 15px; margin: 20px 0; }
.chk-group label { background: #e8f5e9; padding: 8px 12px; border-radius: 4px; cursor: pointer; }
.chk-group input { margin-right: 6px; }
button { padding: 10px 20px; background: #2e7d32; color: white; border: none; border-radius: 4px; cursor: pointer; }
button:hover { background: #1b5e20; }
.chart-container { margin-top: 40px; display: flex; gap: 40px; flex-wrap: wrap; justify-content: center; }
canvas { max-width: 400px; max-height: 400px; }
</style>
</head>
<body>
<div class="container">
<h1>🕷️ 参数敏感性分析 - 蜘蛛图</h1>
<div class="chk-group">
<label><input type="checkbox" value="MAXLUE" checked> MAXLUE</label>
<label><input type="checkbox" value="SPAN" checked> SPAN</label>
<label><input type="checkbox" value="SLA" checked> SLA</label>
<label><input type="checkbox" value="AMAXTB" > AMAXTB</label>
<label><input type="checkbox" value="TMNFTB" > TMNFTB</label>
</div>
<button onclick="runSensitivity()">开始分析</button>
<div class="chart-container">
<canvas id="radar"></canvas>
<canvas id="bar"></canvas>
</div>
</div>
<script>
const radarCtx = document.getElementById('radar').getContext('2d');
const barCtx = document.getContext('2d', 'bar');
let radarChart = new Chart(radarCtx, {
type: 'radar',
data: { labels: [], datasets: [] },
options: { responsive: true, plugins: { title: { display: true, text: '参数敏感性(蜘蛛图)' } } }
});
let barChart = new Chart(barCtx, {
type: 'bar',
data: { labels: [], datasets: [] },
options: { responsive: true, plugins: { title: { display: true, text: '敏感度柱状图' } } }
});
async function runSensitivity() {
const checks = Array.from(document.querySelectorAll('input[type=checkbox]:checked')).map(cb => cb.value);
if (checks.length === 0) { alert('至少选择一个参数'); return; }
const response = await fetch('/sensitivity', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ params: checks })
});
const data = await response.json();
// 蜘蛛图
radarChart.data.labels = data.params;
radarChart.data.datasets = [{
label: '标准化敏感度',
data: data.sensitivity,
backgroundColor: 'rgba(46,125,50,0.2)',
borderColor: 'rgba(46,125,50,1)',
pointBackgroundColor: 'rgba(46,125,50,1)'
}];
radarChart.update();
// 柱状图
barChart.data.labels = data.params;
barChart.data.datasets = [{
label: '敏感度',
data: data.sensitivity,
backgroundColor: 'rgba(33,150,243,0.7)'
}];
barChart.update();
}
</script>
</body>
</html> import numpy as np
from flask import Blueprint, request, jsonify
from pcse.fileinput import YAMLCropDataProvider
from pcse.base import ParameterProvider
from pcse.db import NASAPowerWeatherDataProvider
from pcse.models import Wofost71_WLP_FD
from pcse.engine import Engine
import pandas as pd
import datetime
import copy
sen_bp = Blueprint('sensitivity', __name__, url_prefix='/')
@sen_bp.route('/sensitivity')
def sen_page():
return render_template('sensitivity.html')
# ---------- 局部分析 ----------
@sen_bp.route('/sensitivity', methods=['POST'])
def sensitivity():
selected = request.json['params'] # 列表
base_crop = {
'VARIETY_NAME': 'SenWheat',
'CropType': 'wheat',
'MAXLUE': 2.5,
'SPAN': 30,
'SLA': 20,
'AMAXTB': [0, 1.0, 1.5, 1.0],
'TMNFTB': [0, 0.0, 1, 0.0]
}
def run_one(crop_dict):
cropd = YAMLCropDataProvider(crop_dict=crop_dict)
weather = NASAPowerWeatherDataProvider(latitude=52.0, longitude=5.0)
params = ParameterProvider(cropdata=cropd, soildata=None, sitedata=None)
wofost = Wofost71_WLP_FD(params, weather, [])
eng = Engine(wofost)
eng.run_till_terminate()
df = pd.DataFrame(eng.get_output())
return df['LAI'].max() # 敏感指标:最大 LAI
base_lai = run_one(base_crop)
sensitivity = []
for p in selected:
plus = copy.deepcopy(base_crop)
minus = copy.deepcopy(base_crop)
if isinstance(base_crop[p], list):
# 对列表类参数整体浮动 10%
plus[p] = [v * 1.1 if isinstance(v, float) else v for v in base_crop[p]]
minus[p] = [v * 0.9 if isinstance(v, float) else v for v in base_crop[p]]
else:
plus[p] = base_crop[p] * 1.1
minus[p] = base_crop[p] * 0.9
plus_lai = run_one(plus)
minus_lai = run_one(minus)
s = abs((plus_lai - minus_lai) / (0.2 * base_lai))
sensitivity.append(round(s, 3))
return jsonify({"params": selected, "sensitivity": sensitivity}) 1. 打开:http://localhost:5000/sensitivity
2. 勾选想分析的参数(默认 3 个)
3. 点击“开始分析”
4. 等待 10–20 秒 → 看到:
• 蜘蛛图(雷达)
• 柱状图(敏感度数值)
| 图表 | 如何理解 |
|---|---|
| 蜘蛛图 | 离圆心越远 → 参数越敏感 |
| 柱状图 | 数值越高 → 对输出影响越大 |
• ➕ 支持多输出指标(LAI、产量、水分利用效率)
• ➕ 全局敏感性(Sobol,需更多算力)
• ➕ 下载敏感度报告(PDF)
你想继续:
• ✅ 多指标蜘蛛图(LAI + 产量 + 水分)
• ✅ 下载 PDF 敏感性报告
• ✅ 教师后台(查看全班敏感度热力图)
我可以直接给你完整代码!