ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

COVID-19疫情分析流水线:可复用SEIR建模与DXY数据工程实践

COVID-19疫情分析流水线:可复用SEIR建模与DXY数据工程实践 简介本资源是一套面向数据分析师、公共卫生研究者及Python进阶学习者的COVID-19疫情数据分析与可视化实战代码集聚焦疫情传播建模、趋势预测与地理分布可视化等核心问题。包内共66个文件含15个Jupyter Notebook覆盖SEIR/SIR模型拟合、武汉/湖北疫情预测、动态参数估计等完整分析流程、12个Python脚本含数据清洗、模型封装、辅助函数等可复用模块、6个CSV原始与中间数据集如DXY_Chinese.csv、DXYArea.csv以及15张PNG结果图直观呈现模型输出另有requirements.txt、readme.txt及多份Markdown说明文档保障开箱即用。压缩包仅8.6MB结构分层清晰——Model 1/2/3对应三类主流建模方法data与image目录独立归档便于模块化学习与二次开发。目前已有333人下载学习提供从数据获取、清洗、建模到可视化的全链路Python实现特别适合作为教学案例、科研基线或疫情分析工具箱快速复用。1. 这不是又一个“疫情看板”它是一套可复现、可拆解、可嵌入业务流的 COVID-19 分析流水线你打开 GitHub 或 Gitee搜 “COVID-19 Python”十有八九看到的是一个 notebook 画几条折线图、调用plotly渲染个中国地图、再跑个sklearn.LinearRegression预测未来 7 天——然后戛然而止。这类项目常被称作“教学演示型”好看、易懂、但没法进真实分析流程数据源硬编码、模型参数写死、预测结果不带置信区间、可视化无法导出为可嵌入 BI 系统的 JSON 接口。而本项目不是这样。它由三套正交建模路径SEIR 动态传播模型 / Ridge 回归基线 / 多尺度动态拟合构成完整分析闭环每个.ipynb都对应一个可独立运行的分析单元所有.py脚本都封装成模块化函数helper_fun_epi_model.py中的fit_seir_with_bounds()支持参数约束优化所有 PNG 图片都不是截图而是plt.savefig(..., bbox_inchestight, dpi300)生成的出版级矢量替代图。它真正解决的是当疾控中心要求你明天上午 10 点前提交“武汉封城后 R₀ 演化趋势 控制措施敏感性分析”PPT 时你能否在 2 小时内从原始 DXY 数据拉取、清洗、建模、验证、出图、导出 PDF 并附上误差带说明项目面向的是需要交付分析结论的公共卫生工程师、疾控数据组成员、以及正在构建疫情响应决策支持系统的开发团队——不是 Python 新手练手玩具也不是纯学术论文复现包。2. 从 DXY 原始数据到结构化时间序列数据管道的三层清洗逻辑与DXY_AreaData_query.py实战2.1 为什么不能直接读DXY_Chinese.csv原始数据的三大“脏点”项目中data/DXY_Chinese.csv和data/DXYArea.csv是丁香园DXY2020 年 1–3 月公开爬取的疫情数据快照但直接pd.read_csv()会立刻翻车时间字段非标准 ISO 格式updateTime列值为2020-01-23 12:45:00但部分记录含中文“更新于”前缀或毫秒级时间戳如2020-01-24T08:23:12.000Zpd.to_datetime()默认解析失败地理编码混乱provinceName含“湖北省”“湖北”“Hubei”混用cityName存在空值、待确认、境外输入等非行政区划值确诊数逻辑矛盾同一日期同一城市出现多条记录因不同来源上报时间差且confirmedCount可能小于前一日值修正回溯。提示这不是数据质量问题而是疫情早期多源异构上报的真实映射。强行drop_duplicates()或fillna(0)会抹掉关键修正信号必须保留时间戳粒度做增量合并。2.2DXY_AreaData_query.py的核心设计以“区域时间”为键的幂等聚合器该脚本是整个项目的数据中枢其query_dxy_area_data()函数不返回 DataFrame而是返回一个dict键为(province, city, date)元组值为{confirmed: int, cured: int, dead: int, update_time: pd.Timestamp}。关键逻辑如下# DXY_AreaData_query.py 核心片段 def query_dxy_area_data(csv_path: str, start_date: str 2020-01-20, end_date: str 2020-03-31) - Dict[Tuple[str, str, str], Dict]: df pd.read_csv(csv_path, encodingutf-8) # 步骤1标准化时间列兼容多种格式 df[updateTime] pd.to_datetime( df[updateTime].str.replace(r[^\d\-:\sT], , regexTrue), errorscoerce ) # 步骤2提取 province/city优先用英文映射表fallback 到中文规则 df[provinceName] df[provinceName].map(chineseProvince_to_EN).fillna(df[provinceName]) df[cityName] df[cityName].map(chineseCity_to_EN).fillna(df[cityName]) # 步骤3按 (province, city, date) 分组取每组 update_time 最新的记录 df[date] df[updateTime].dt.date.astype(str) grouped df.groupby([provinceName, cityName, date]) result {} for (prov, city, date), group in grouped: latest_row group.loc[group[updateTime].idxmax()] result[(prov, city, date)] { confirmed: int(latest_row[confirmedCount]), cured: int(latest_row[curedCount]), dead: int(latest_row[deadCount]), update_time: latest_row[updateTime] } return result这段代码的精妙之处在于它不假设数据干净而是把“脏”当作信号来处理。例如group[updateTime].idxmax()保证取到最新修正值chineseProvince_to_EN.pkl映射表项目自带将“湖北省”→Hubei、“新疆维吾尔自治区”→Xinjiang避免后续pivot_table()时因名称不一致导致列缺失。参数start_date/end_date不是过滤条件而是定义分析窗口——因为DXYArea.csv包含全量历史快照直接df[df[date] start_date]会漏掉窗口起始日之前已存在、但在窗口期内被修正的记录。2.3 构建省级面板数据dataset.py中的build_province_panel()dataset.py将DXY_AreaData_query.py输出的字典转为标准 Pandas Panel三维结构但实际实现为MultiIndex DataFrame索引为(province, date)列为[confirmed, cured, dead, new_confirmed]。关键函数build_province_panel()自动计算日新增# dataset.py 片段 def build_province_panel(dxy_dict: Dict, provinces: List[str] None) - pd.DataFrame: if provinces is None: provinces [Hubei, Guangdong, Zhejiang, Beijing, Shanghai] records [] for (prov, city, date), data in dxy_dict.items(): if prov in provinces and city : # 仅取省级汇总city 为空表示省级 records.append({ province: prov, date: date, confirmed: data[confirmed], cured: data[cured], dead: data[dead] }) df pd.DataFrame(records).sort_values([province, date]) # 计算日新增按 province 分组对 confirmed 做 diff() df[new_confirmed] df.groupby(province)[confirmed].diff().fillna(0).astype(int) # 强制日期类型并设为 MultiIndex df[date] pd.to_datetime(df[date]) df df.set_index([province, date]).sort_index() return df # 使用示例 dxy_data query_dxy_area_data(data/DXYArea.csv) panel_df build_province_panel(dxy_data, provinces[Hubei, Guangdong]) print(panel_df.loc[(Hubei, 2020-02-12)]) # 输出confirmed 33466, new_confirmed 1921...注意city 这一判断——DXY 原始数据中省级汇总行cityName字段为空字符串而非NaN这是项目作者踩坑后硬编码的识别规则。若你替换为其他数据源如 WHO 或 ECDC需同步修改此判断逻辑。2.4 验证数据一致性Incorrect_confirmed_cases.ipynb的纠错机制项目包含Incorrect_confirmed_cases.ipynb专门检测confirmedCount的异常跃迁。其核心算法是对每个省份计算confirmed序列的二阶差分即新增数的变化率若某日|Δ²(confirmed)| 500且Δ(confirmed) 0则标记为“疑似回溯修正日”。该 notebook 会输出 CSV 文件incorrect_cases_log.csv列出所有可疑日期及前后 3 日数据快照供人工复核。这不是为了“修复”数据而是为模型训练提供掩码mask后续SEIR_model.py中的fit_seir()函数接受ignore_dates参数自动跳过这些日期的损失计算避免模型被异常点带偏。3. 三套建模路径深度拆解SEIR 动态传播模型、Ridge 回归基线、Dynamic SEIR 多尺度拟合3.1 Model 1Estimating_current_cases_in_Wuhan.ipynb—— 基于症状报告延迟的校正估计该 notebook 解决一个经典问题官方通报的“确诊数”严重滞后于真实感染数。作者采用症状报告延迟分布建模法假设从感染到出现症状平均 5.2 天文献值再到医院确诊平均 2.3 天总延迟约 7.5 天且服从 Gamma 分布。核心公式为$$ \text{TrueCases}(t) \sum_{\tau0}^{T} \text{ReportedCases}(t\tau) \cdot p(\tau) $$其中 $p(\tau)$ 是延迟 $\tau$ 天的概率质量函数。实现上help_function_model1.py提供estimate_true_cases()# help_function_model1.py from scipy.stats import gamma def estimate_true_cases(reported_series: pd.Series, delay_mean: float 7.5, delay_std: float 2.1) - pd.Series: reported_series: index 为日期value 为当日通报确诊数 delay_mean/std: 延迟分布的 Gamma 参数shape (mean/std)^2, scale std^2/mean shape (delay_mean / delay_std) ** 2 scale delay_std ** 2 / delay_mean # 构建延迟权重向量最多回溯 30 天 weights [gamma.pdf(t, ashape, loc0, scalescale) for t in range(1, 31)] weights np.array(weights) / sum(weights) # 归一化 # 卷积运算true[t] sum(report[ti] * weight[i]) true_cases np.zeros(len(reported_series)) for i, date in enumerate(reported_series.index): # 取未来 30 天的 reported 值 future_slice reported_series.iloc[i:i30] if len(future_slice) len(weights): w weights[:len(future_slice)] else: w weights true_cases[i] np.sum(future_slice.values[:len(w)] * w) return pd.Series(true_cases, indexreported_series.index) # 在 notebook 中调用 wuhan_confirmed panel_df.xs(Hubei)[confirmed] # 获取湖北确诊序列 wuhan_true estimate_true_cases(wuhan_confirmed)该函数输出wuhan_true即为“校正后真实感染数估计”比原始通报数高 2–3 倍2020 年 2 月峰值期。注意future_slice的取法它用未来数据反推当前真实值因此该估计仅适用于历史回溯分析不可用于实时预测因未来数据未知。3.2 Model 2SEIR_model.py与Forecast_Outbreak_Wuhan.ipynb—— 带干预参数的可解释传播模型SEIR 模型是本项目最重的模块SEIR_model.py实现了标准 SEIR 微分方程组$$ \begin{cases} \frac{dS}{dt} -\beta S I / N \ \frac{dE}{dt} \beta S I / N - \sigma E \ \frac{dI}{dt} \sigma E - \gamma I \ \frac{dR}{dt} \gamma I \end{cases} $$但项目做了关键增强引入时间依赖的接触率 $\beta(t)$将其建模为分段函数$$ \beta(t) \begin{cases} \beta_0, t t_c \ \beta_0 \cdot e^{-k(t-t_c)}, t \geq t_c \end{cases} $$其中 $t_c$ 是干预开始日如武汉封城日 2020-01-23$k$ 是控制强度。SEIR_model.py的fit_seir_with_control()函数使用scipy.optimize.least_squares同时拟合 $\beta_0, \sigma, \gamma, k$ 四个参数并强制 $\beta_0 \gamma$保证基本再生数 $R_0 \beta_0 / \gamma 1$# SEIR_model.py 片段 def seir_derivatives(y, t, beta_func, sigma, gamma, N): S, E, I, R y beta_t beta_func(t) dSdt -beta_t * S * I / N dEdt beta_t * S * I / N - sigma * E dIdt sigma * E - gamma * I dRdt gamma * I return [dSdt, dEdt, dIdt, dRdt] def fit_seir_with_control(times, observed_I, N, t_c, boundsNone): if bounds is None: bounds ([0.1, 0.05, 0.05, 0.01], [1.0, 0.5, 0.5, 1.0]) # beta0, sigma, gamma, k def objective(params): beta0, sigma, gamma, k params beta_func lambda t: beta0 if t t_c else beta0 * np.exp(-k*(t-t_c)) sol solve_ivp( lambda t, y: seir_derivatives(y, t, beta_func, sigma, gamma, N), [times[0], times[-1]], [N-10, 10, 0, 0], # 初始状态SN-10, E10, I0, R0 t_evaltimes, methodRK45 ) pred_I sol.y[2] # I(t) 是解的第 3 行 return pred_I - observed_I res least_squares(objective, x0[0.3, 0.2, 0.1, 0.3], boundsbounds) return res.x, res.cost # 在 Forecast_Outbreak_Wuhan.ipynb 中调用 t_c pd.to_datetime(2020-01-23).timestamp() / 86400 # 转为天数 params, cost fit_seir_with_control( timesdays_since_start, observed_Iwuhan_new_confirmed.values, N59000000, # 湖北人口 t_ct_c )拟合后params返回[beta0, sigma, gamma, k]k值越大表示干预越有效接触率衰减越快。项目中withControl.png与without_control.png对比图即由此生成前者用拟合的k0后者设k0无干预直观展示封城对传播曲线的压平效果。3.3 Model 3Dynamic_SEIR_model.py与Forecast_by_DynamicSEIR.ipynb—— 多尺度滚动拟合引擎Model 2 的 SEIR 是全局静态拟合而 Model 3 实现滚动窗口动态拟合每 7 天滑动一次用最近 14 天数据重新估计参数生成未来 7 天预测并将所有预测拼接成连续曲线。Dynamic_SEIR_model.py的dynamic_forecast()函数核心逻辑# Dynamic_SEIR_model.py def dynamic_forecast(observed_series: pd.Series, window_size: int 14, forecast_horizon: int 7, population: int 59000000) - pd.DataFrame: observed_series: index 为日期value 为日新增确诊数 返回 DataFrameindex 为日期columns 为 [predicted_I, lower_bound, upper_bound] dates observed_series.index predictions [] for i in range(window_size, len(dates) - forecast_horizon 1): train_dates dates[i-window_size:i] train_data observed_series.loc[train_dates].values train_times np.arange(len(train_data)) # 归一化时间轴 # 拟合 SEIR 参数同 Model 2但只用当前窗口 try: params, _ fit_seir_with_control( timestrain_times, observed_Itrain_data, Npopulation, t_c0 # 窗口内不设固定 t_c让 k 自由拟合 ) # 用拟合参数预测未来 forecast_horizon 天 t_pred np.arange(len(train_data), len(train_data)forecast_horizon) sol solve_ivp( lambda t, y: seir_derivatives(y, t, lambda t: params[0]*np.exp(-params[3]*t), params[1], params[2], population), [0, t_pred[-1]], [population-10, 10, 0, 0], t_evalt_pred ) pred_I sol.y[2] # 添加简单置信区间基于残差标准差 residuals train_data - sol.y[2][:len(train_data)] std_resid np.std(residuals) lower pred_I - 1.96 * std_resid upper pred_I 1.96 * std_resid for j, t in enumerate(t_pred): pred_date dates[ij] if ij len(dates) else dates[-1] pd.Timedelta(daysj-(len(dates)-i)) predictions.append({ date: pred_date, predicted_I: max(0, pred_I[j]), lower_bound: max(0, lower[j]), upper_bound: max(0, upper[j]) }) except Exception as e: # 拟合失败时用前 7 日均值填充 mean_val np.mean(train_data[-7:]) for j in range(forecast_horizon): pred_date dates[ij] if ij len(dates) else dates[-1] pd.Timedelta(daysj-(len(dates)-i)) predictions.append({ date: pred_date, predicted_I: mean_val, lower_bound: mean_val * 0.8, upper_bound: mean_val * 1.2 }) return pd.DataFrame(predictions).set_index(date) # 调用示例 wuhan_new panel_df.xs(Hubei)[new_confirmed] dyn_pred dynamic_forecast(wuhan_new, window_size14, forecast_horizon7)该函数输出dyn_pred包含预测值及 95% 置信区间SEIR_prediction.png即由其绘制。其价值在于当政策突变如复工、二次爆发导致传播动力学突变时静态模型会持续失效而滚动拟合能自动捕捉参数漂移。项目中SEIR_test_7days.png展示了该模型在 2020 年 2 月下旬的 7 天滚动预测 vs 实际值MAPE 低于 12%。4. 可视化系统从matplotlib基础图到seaborn多维度热力图的工程化封装4.1image/目录 PNG 文件的生成逻辑不是截图是savefig()的精确控制项目image/下所有 PNG如withControl.png,contact_rate.png均由 notebook 中plt.savefig()生成而非手动截图。关键参数组合确保出版级质量# 示例生成 contact_rate.png plt.figure(figsize(10, 6)) plt.plot(contact_rate_times, contact_rates, o-, linewidth2, markersize4, color#1f77b4) plt.xlabel(Date, fontsize12, fontweightbold) plt.ylabel(Effective Contact Rate β(t), fontsize12, fontweightbold) plt.title(Time-Varying Contact Rate in Wuhan (Jan-Feb 2020), fontsize14, fontweightbold) plt.grid(True, alpha0.3) plt.xticks(rotation45) # 关键tight_layout high DPI bbox_inches plt.tight_layout() plt.savefig(image/contact_rate.png, dpi300, bbox_inchestight) plt.close() # 防止内存泄漏bbox_inchestight自动裁剪空白边距避免标题被截断dpi300满足印刷要求屏幕显示用 150 dpi 即可plt.close()Jupyter 中不关闭 figure 会导致后续绘图叠加内存暴涨rotation45日期标签倾斜避免重叠。所有图片命名直指其信息核心contact_rate.png而非fig1.png便于在论文或报告中直接引用。4.2baseline_ridge.png与baseline_polynomial_regression.ipynb基线模型的可视化对比范式baseline_polynomial_regression.ipynb实现了三种基线模型对比Ridge 回归sklearn.linear_model.Ridge特征为[day_since_start, day_since_start^2, temp, humidity]多项式回归sklearn.preprocessing.PolynomialFeatures(degree3)LinearRegressionSARIMA 时间序列模型statsmodels.tsa.statespace.SARIMAX。baseline_ridge.png并非单一图表而是3×1 子图布局上图三种模型在训练集上的拟合曲线实线vs 真实值散点中图残差序列预测值 - 真实值标注 RMSE下图残差 QQ 图检验正态性。这种布局源自seaborn的subplots封装helper_fun_model.py提供plot_baseline_comparison()函数# helper_fun_model.py def plot_baseline_comparison(y_true, y_pred_ridge, y_pred_poly, y_pred_sarima, titleBaseline Model Comparison): fig, axes plt.subplots(3, 1, figsize(12, 10)) # 上图拟合曲线 axes[0].plot(y_true.index, y_true, o, labelObserved, markersize3, alpha0.7) axes[0].plot(y_true.index, y_pred_ridge, -, labelRidge, linewidth2) axes[0].plot(y_true.index, y_pred_poly, --, labelPolynomial, linewidth2) axes[0].plot(y_true.index, y_pred_sarima, -., labelSARIMA, linewidth2) axes[0].set_ylabel(New Cases) axes[0].legend() axes[0].grid(True, alpha0.3) # 中图残差 resid_ridge y_pred_ridge - y_true axes[1].plot(y_true.index, resid_ridge, o-, markersize2, alpha0.6, labelRidge Residuals) axes[1].axhline(0, colork, linestyle--) axes[1].set_ylabel(Residual) axes[1].legend() # 下图QQ 图 from scipy import stats stats.probplot(resid_ridge, distnorm, plotaxes[2]) axes[2].set_title(Q-Q Plot of Ridge Residuals) plt.suptitle(title, fontsize16, fontweightbold) plt.tight_layout() plt.savefig(image/baseline_ridge.png, dpi300, bbox_inchestight) plt.close() # 在 notebook 中调用 plot_baseline_comparison( y_truepanel_df.xs(Hubei)[new_confirmed], y_pred_ridgeridge_predictions, y_pred_polypoly_predictions, y_pred_sarimasarima_predictions )该函数强制统一坐标轴、字体大小、图例位置确保多图风格一致——这是工程化可视化的底线。4.3 地理可视化chineseProvince_to_EN.pkl与baselines_forecast_Hubei.png的省界映射baselines_forecast_Hubei.png是一张湖北全省地级市预测热力图。其底层依赖chineseProvince_to_EN.pkl和chineseCity_to_EN.pkl两个映射字典将中文地名转为英文缩写如武汉市→Wuhan再匹配geopandas的 GeoJSON 边界文件。关键步骤加载data/china-city-level.geojson项目未提供需自行下载但readme.txt有指引用chineseCity_to_EN将 GeoJSON 中的name字段标准化将预测数据pred_by_citydictkey 为英文城市名merge 到 GeoDataFramegdf.plot(columnpredicted_new, cmapYlOrRd, legendTrue, figsize(10,8))。若你本地无 GeoJSONForecast_China_total_by_DynamicSEIR.ipynb中的plot_china_map()函数会 fallback 到matplotlib的scatter绘制城市坐标点ChinaAreaCode.csv提供经纬度虽无省界但位置关系准确。5. 避坑指南15 个真实踩过的坑与血泪解决方案5.1 现象run_model1.py执行报错ModuleNotFoundError: No module named scipy但pip install scipy后仍失败原因项目requirements.txt中scipy1.4.1与 Python 3.9 不兼容scipy 1.4.1 仅支持 Python ≤3.8。解决升级 scipy 版本修改requirements.txt第 3 行为scipy1.7.0再pip install -r requirements.txt。若环境受限降级 Python 至 3.8。5.2 现象Forecast_Outbreak_Wuhan.ipynb中solve_ivp返回y的 shape 为(4, 0)即空解原因t_eval数组长度为 0通常因times输入为空或times[0] times[-1]日期未排序。解决在调用前加校验assert len(times) 0 and times[0] times[-1]并确保times是np.arange()生成的递增数组而非直接传入pd.Series.index可能含 NaT。5.3 现象Dynamic_SEIR_model.py滚动预测中某窗口拟合失败后续所有预测变为 NaN原因solve_ivp在参数不合理时返回sol.successFalse但代码未检查直接取sol.y[2]导致IndexError。解决在dynamic_forecast()内try块中添加if not sol.success: raise ValueError(IVP solver failed)触发except中的均值填充逻辑。5.4 现象Estimating_current_cases_in_Wuhan.ipynb输出的true_cases在早期2020-01为负数原因Gamma 分布权重在τ0处概率极低而reported_series在早期数据稀疏卷积结果受边界效应影响。解决在estimate_true_cases()开头添加reported_series reported_series.fillna(0).clip(lower0)并限制weights只取τ≥1避免t0时无未来数据可卷积。5.5 现象SEIR_fit.png中拟合曲线与真实值偏差巨大cost值 1e6原因初始参数x0过于随意如beta00.1而真实β₀在 0.3–0.5 区间优化陷入局部极小。解决改用网格搜索初始化x0 [0.4, 0.15, 0.08, 0.2]参考论文《The epidemiological characteristics of an outbreak of 2019 novel coronavirus diseases》或先用curve_fit粗略估计σ, γ再代入。6. 进阶技巧如何将 Model 2 的 SEIR 模型封装为 REST API 并接入 Grafana6.1 用 Flask 快速暴露预测接口run_model2.py本为命令行脚本但只需 10 行代码即可转为 Web API。新建api_server.py# api_server.py from flask import Flask, request, jsonify from SEIR_model import fit_seir_with_control, seir_derivatives from scipy.integrate import solve_ivp import numpy as np import pandas as pd app Flask(__name__) app.route(/predict/seir, methods[POST]) def predict_seir(): data request.json # data 格式: {province: Hubei, observed_new: [100, 120, ...], population: 59000000} observed_I np.array(data[observed_new]) N data[population] t_c pd.to_datetime(2020-01-23).timestamp() / 86400 # 拟合参数 params, _ fit_seir_with_control( timesnp.arange(len(observed_I)), observed_Iobserved_I, NN, t_ct_c ) # 预测未来 7 天 t_pred np.arange(len(observed_I), len(observed_I)7) sol solve_ivp( lambda t, y: seir_derivatives(y, t, lambda t: params[0] if t t_c else params[0]*np.exp(-params[3]*(t-t_c)), params[1], params[2], N), [0, t_pred[-1]], [N-10, 10, 0, 0], t_evalt_pred ) return jsonify({ predicted_new: sol.y[2].tolist(), parameters: {beta0: params[0], sigma: params[1], gamma: params[2], k: params[3]} }) if __name__ __main__: app.run(host0.0.0.0, port5000, debugFalse)启动后curl -X POST http://localhost:5000/predict/seir -H Content-Type: application/json -d {province:Hubei,observed_new:[100,120,150],population:59000000}即得 JSON 预测结果。6.2 Grafana 数据源配置用 SimpleJson 插件对接Grafana 本身不支持直接调用 Flask API需安装 SimpleJson 插件grafana-cli plugins install grafana-simple-json-datasource重启 Grafana在 Grafana UI 中添加数据源类型选SimpleJsonURL 填http://your-server-ip:5000创建 DashboardPanel 类型选Time seriesQuery 中Method:POSTURL:/predict/seirJSON Body:{province:Hubei,observed_new:${observed_array},population:59000000}observed_array为变量此时Grafana 可每 15 分钟自动拉取新预测生成实时疫情预测看板。6.3 生产环境加固参数校验与熔断机制上述 API 缺乏防护生产环境必须添加输入校验用pydantic定义PredictionRequestSchema拒绝非法observed_new如含负数、长度 5超时熔断requests.post()设置本文还有配套的精品资源点击获取
返回列表