Python+Plotly实现疫苗接种数据动态可视化

Python+Plotly实现疫苗接种数据动态可视化 1. 项目概述用代码看清疫苗接种的“进度条”你有没有盯着新闻里那些“全球已接种XX亿剂”“某国覆盖率超80%”的数字却总觉得像隔着一层毛玻璃数据是真实的但光看总量和百分比根本看不出背后的真实节奏——比如一个国家是靠前半年狂打猛冲还是后半年才慢悠悠补上又比如老年人接种率是不是被年轻人的高覆盖率“平均”掉了再比如相邻两个省份一个靠城市集中发力一个靠乡村网格化推进路径完全不同但最终数字可能差不多。这个项目就是为了解决这个问题用Python写几段实在的代码把冷冰冰的疫苗接种数据变成一张张会说话的动态图表让进度、瓶颈、差异一目了然。核心关键词是COVID-19 Vaccination Progress、Python、Plotly它不追求预测未来也不做政策评论就专注一件事——把公开的、结构化的接种数据用最直观的方式“翻译”出来。适合刚学完Pandas基础、想找个真实项目练手的数据新手也适合公共卫生领域的从业者需要快速生成内部汇报图表甚至适合社区工作者想给居民讲清楚本街道的接种情况。我试过直接拿世界卫生组织WHO和Our World in Data的原始CSV文件跑通整个流程从下载、清洗到出图全程不到20分钟而且每一步都能看到数据在屏幕上“动起来”的效果这种即时反馈比看一百页PDF报告都管用。2. 整体设计思路与方案选型解析2.1 为什么选Python而不是Excel或Tableau很多人第一反应是“这不就是画个折线图吗Excel拖两下不就完了”实话说初期我也这么想直到我真正处理起真实数据。Excel面对几十个国家、上百个时间点、多维度分组按年龄、按剂次、按地区时公式嵌套深得让人头皮发麻更别说数据源一更新就得手动重拉一遍。而Tableau虽然交互强但学习成本高且对非IT背景的基层工作人员来说安装、授权、服务器配置都是门槛。Python则完全不同它是一把“瑞士军刀”Pandas负责把杂乱数据理成整齐的表格Plotly负责把表格变成带缩放、悬停、筛选的动态图表最后用几行代码就能一键导出HTML发给同事点开就能看连浏览器都不用装插件。更重要的是可复现性——我把整个脚本发给你你换台电脑、换份新数据只要运行一遍结果绝对一模一样。这在需要反复验证、向上汇报的场景里是Excel永远做不到的硬优势。我见过太多团队因为Excel版本不同、宏设置差异导致同一份报表在领导电脑上显示错位最后耽误事。Python脚本没有这种“玄学”。2.2 为什么Plotly是可视化环节的唯一选择在Matplotlib、Seaborn、Plotly三者之间我毫不犹豫选Plotly理由非常实际悬停信息Hover是理解疫苗数据的灵魂。想象一下你在看一条全球接种总量的折线图鼠标移过去如果只显示“2021-03-15: 356,789,012”这信息量太单薄。但Plotly能让你定义悬停内容比如显示“2021-03-15 | 全球累计3.57亿剂 | 单日新增1,245万剂 | 覆盖率12.3%按总人口| 主力国家中、美、印”。这一行信息就把单点数据的上下文全补上了。再比如热力图横轴是国家纵轴是月份颜色深浅代表当月接种量鼠标悬停立刻弹出该国当月具体数值环比变化关键事件备注如“该国于当月启动加强针”。这种“所见即所得”的深度交互在Matplotlib里要写几十行代码才能勉强模拟在Plotly里就是hover_data[total_vaccinations, daily_vaccinations, people_fully_vaccinated_per_hundred]这一行参数的事。而且Plotly的动画功能能让“接种进度”真正“动起来”——比如用px.scatter()画气泡图X轴是时间Y轴是覆盖率气泡大小代表接种总量颜色代表国家大洲再加一句animation_framedate整个世界疫苗接种的“追赶赛”就活生生在你眼前上演。这种叙事能力是静态图表无法替代的。2.3 数据源选型为什么坚持用Our World in Data而非各国官网数据源是整个项目的地基选错了后面全是白忙活。我对比过WHO官方数据库、美国CDC、中国国家卫健委、欧盟ECDC等多个来源最终锁定Our World in DataOWID原因有三第一标准化程度高。各国上报数据的口径千差万别——有的报“剂次”有的报“人次数”有的把单剂疫苗如强生和双剂疫苗如辉瑞混在一起算。OWID团队花了大量人力对所有原始数据进行统一清洗、单位换算、缺失值插补并明确标注每条数据的来源和置信度。第二更新及时且稳定。他们每天UTC时间凌晨自动抓取并更新我设了个定时任务每天早上8点自动下载最新CSV脚本里一行pd.read_csv(https://raw.githubusercontent.com/owid/covid-19-data/master/public/data/vaccinations/vaccinations.csv)就能拿到全量数据比手动去十几个官网翻找省心百倍。第三字段设计直击痛点。除了基础的total_vaccinations总剂次、people_vaccinated至少一剂人数它还提供了people_fully_vaccinated完成全程接种人数、total_boosters加强针总数、daily_vaccinations单日新增、people_fully_vaccinated_per_hundred每百人全程接种数等十余个关键衍生字段。这些不是简单计算出来的而是经过严谨逻辑校验的。比如people_fully_vaccinated不会超过people_vaccinated一旦发现异常OWID会标记并暂停该国数据更新直到核实。这种“数据洁癖”正是我们做分析时最需要的底气。3. 核心细节解析与实操要点3.1 数据清洗别让“脏数据”毁掉你的图表拿到OWID的原始CSV第一印象往往是“哇字段好多”——足足50多个列。但真正能用的可能就10个左右。清洗不是删减而是“精准手术”。我通常按三步走过滤、补全、校验。第一步是过滤无关国家和无效时间。OWID数据包含全球200多个国家和地区但很多小岛国数据稀疏连续几个月为0或者政治实体状态存疑。我的做法是先用df[location].value_counts()统计各国数据点数量设定阈值比如少于30个有效日期的国家直接剔除再人工核对重点国家名单G20、金砖五国、周边邻国等。代码很简单# 保留数据点超过30天的国家并排除World、International等聚合项 valid_countries df.groupby(location).size().where(lambda x: x 30).dropna().index df df[df[location].isin(valid_countries) ~df[location].isin([World, International])]第二步是处理缺失值。疫苗数据最大的坑是“零值陷阱”——某天没上报系统默认记为0但这和“当天确实一针都没打”有本质区别。OWID用NaN表示“无数据”这是好事。但people_fully_vaccinated这类字段早期很多国家是空的不能简单用0填充。我的经验是对total_vaccinations这类累积量用前向填充ffill最合理因为总量不会倒退对daily_vaccinations这类日增量必须用插值interpolate否则会把“断更”误读为“零接种”。这里有个关键技巧插值前先按国家分组避免把A国的断更和B国的正常数据混在一起算# 按国家分组对日增量进行线性插值更符合实际接种节奏 df[daily_vaccinations] df.groupby(location)[daily_vaccinations].apply( lambda x: x.interpolate(methodlinear, limit_directionboth) )第三步是逻辑校验。这是最容易被忽略却最致命的一步。比如people_fully_vaccinated必须小于等于people_vaccinatedtotal_vaccinations必须大于等于people_vaccinated因为有人打了两针。我写了个校验函数每次清洗后必跑def validate_vaccination_logic(df): errors [] for idx, row in df.iterrows(): if pd.notna(row[people_fully_vaccinated]) and pd.notna(row[people_vaccinated]): if row[people_fully_vaccinated] row[people_vaccinated]: errors.append(fLogic error at {row[location]}, {row[date]}: fully vaccinated) if pd.notna(row[total_vaccinations]) and pd.notna(row[people_vaccinated]): if row[total_vaccinations] row[people_vaccinated]: errors.append(fLogic error at {row[location]}, {row[date]}: total vaccinated) return errors实测下来OWID数据校验错误率低于0.01%但哪怕只有1条错误也可能导致你画出的“覆盖率曲线”在某天突然暴跌引发不必要的恐慌。所以这一步宁可多花两分钟绝不能跳过。3.2 图表类型选型什么问题配什么图拒绝“万能折线图”很多人一上来就想画“全球接种总量随时间变化”觉得这是最“大气”的图。但从业务角度看这种图信息密度极低还容易误导。我根据实际分析场景把图表分成四类每类解决一个核心问题第一类看“节奏”——用面积图Area Chart替代折线图。问题某个国家的接种是平稳推进还是前期爆发后期乏力为什么选面积图因为面积图的“底边”天然代表“从零开始”视觉上能清晰看出增长的加速度。比如中国数据2021年Q2的面积斜率明显陡于Q3说明Q2是真正的攻坚期而美国数据2021年Q1面积暴涨Q2后趋于平缓反映出“快速铺开-逐步饱和”的典型路径。Plotly实现只需把px.line()换成px.area()并确保X轴是日期Y轴是total_vaccinations。关键参数是line_grouplocation这样多国数据不会混成一团。第二类看“结构”——用堆叠条形图Stacked Bar拆解剂次构成。问题一个国家打了1亿剂其中多少是第一剂、多少是第二剂、多少是加强针这直接关系到免疫屏障的牢固程度。OWID的total_vaccinations是总和但people_vaccinated至少一剂和people_fully_vaccinated全程可以推算出各剂次大致分布。我的算法是第一剂 people_vaccinated第二剂 people_fully_vaccinated-people_vaccinated注意这里假设全程两剂实际需根据疫苗类型调整加强针 total_boosters。然后用px.bar()xlocation,y[first_dose, second_dose, booster],barmodestack。实操中发现一个坑有些国家people_fully_vaccinated数据缺失但total_boosters有值这时第二剂就得用total_vaccinations-people_vaccinated-total_boosters来反推必须加try-except捕获计算异常。第三类看“公平性”——用分位数散点图Quantile Scatter暴露年龄差异。问题老年人接种率是不是真的落后光看“全国平均70%”没意义。OWID有按年龄分组的数据如people_vaccinated_per_hundred_60plus但直接画柱状图20个国家排下来密密麻麻。我的解法是把所有国家的“60岁以上接种率”和“18-59岁接种率”作为XY坐标画散点图再加一条YX的参考线。所有落在参考线下方的点都意味着该国老年人接种率低于中青年。更进一步用px.scatter()的size参数映射该国总人口color映射大洲一张图就把“谁在拖后腿”“拖后腿的是大国还是小国”“是哪个大洲的普遍现象”全说清了。这个图我在给社区做科普时居民一看就懂“哦原来不是我们这儿老人不愿打是全球都这样。”第四类看“效率”——用气泡图Bubble Chart结合时间动画。问题哪个国家接种“又快又省”既要速度快日均接种量又要覆盖广每百人接种数。X轴设为daily_vaccinations_per_million百万人口日均接种量Y轴设为people_fully_vaccinated_per_hundred每百人全程接种数气泡大小设为population总人口颜色设为continent。加上animation_framedate你就能看到气泡如何随时间在图中移动——以色列早期气泡小但飞速上移小国高效印度气泡巨大但缓慢爬升大国稳步推进。这种图一眼抓住“效率”和“规模”的平衡点比单纯排名有意义得多。提示所有图表都必须加templateplotly_white去掉默认的灰色背景和粗边框让数据本身成为视觉焦点。这是专业图表和“学生作业图”的分水岭。4. 实操过程与核心环节实现4.1 环境准备与依赖安装三行命令搞定别被“Python环境”吓住现在根本不用折腾。我推荐两种零失败方案方案一推荐给新手用Anaconda Navigator图形界面。下载Anaconda官网免费安装时勾选“Add Anaconda to my PATH”装完打开Navigator新建一个叫vaccine-analysis的环境Python版本选3.9兼容性最好然后在环境的“Not installed”标签页里搜索pandas、plotly、jupyter勾选后点“Apply”一键安装。全程鼠标操作5分钟搞定连命令行都不用碰。方案二推荐给老手用pip命令行。打开终端Mac/Linux或命令提示符Windows依次执行# 创建独立虚拟环境避免包冲突 python -m venv vaccine_env # 激活环境Windows vaccine_env\Scripts\activate.bat # 激活环境Mac/Linux source vaccine_env/bin/activate # 安装核心包国内用户加清华镜像源加速 pip install pandas plotly jupyter -i https://pypi.tuna.tsinghua.edu.cn/simple/ # 额外安装Plotly离线支持避免图表加载慢 pip install kaleido关键点在于虚拟环境venv。我踩过的最大坑就是没建环境直接pip install全局安装结果某天想跑个旧项目发现新版本Plotly把API全改了旧代码全报错修都修不过来。虚拟环境就像给每个项目发个“独立办公室”互不干扰。激活环境后终端提示符前面会多出(vaccine_env)这就是安全的信号。4.2 核心代码实现从数据加载到动态地图下面这段代码是我压箱底的“黄金模板”删掉注释后不到50行但能跑通全部核心功能。我把它拆解成可复制粘贴的模块第一步加载并初筛数据import pandas as pd import plotly.express as px import plotly.graph_objects as go # 直接从OWID GitHub读取最新数据无需本地下载 url https://raw.githubusercontent.com/owid/covid-19-data/master/public/data/vaccinations/vaccinations.csv df pd.read_csv(url) # 转换日期列为datetime类型方便后续时间序列操作 df[date] pd.to_datetime(df[date]) # 过滤掉数据点过少的国家实测30天是合理阈值 min_days 30 country_counts df.groupby(location).size() valid_locations country_counts[country_counts min_days].index df df[df[location].isin(valid_locations)] # 去掉World等聚合项 df df[~df[location].isin([World, International, Upper middle income, Lower middle income])]第二步计算关键衍生指标# 计算每百万人口日均接种量消除国家规模影响 df[daily_vaccinations_per_million] ( df[daily_vaccinations] / df[population] * 1000000 ).round(2) # 计算全程接种覆盖率每百人 df[people_fully_vaccinated_per_hundred] ( df[people_fully_vaccinated] / df[population] * 100 ).round(2) # 计算第一剂覆盖率每百人 df[people_vaccinated_per_hundred] ( df[people_vaccinated] / df[population] * 100 ).round(2)第三步绘制“全球接种节奏”面积图# 筛选重点国家可自定义 top_countries [United States, India, Brazil, Indonesia, Pakistan] df_top df[df[location].isin(top_countries)] # 绘制面积图突出“从零开始”的累积感 fig_area px.area( df_top, xdate, ytotal_vaccinations, colorlocation, titleTop 5 Countries: Total Vaccinations Over Time, labels{total_vaccinations: Total Doses (Millions), date: Date}, templateplotly_white ) # 优化Y轴以百万为单位显示提升可读性 fig_area.update_yaxes(tickprefix, tickformat.0f, tickvals[i*1e8 for i in range(0, 11)]) # 0到10亿每1亿一格 # 导出为独立HTML文件双击即可查看交互图表 fig_area.write_html(vaccination_area_chart.html)第四步生成“接种公平性”散点图含动画# 准备年龄分组数据OWID提供需单独下载 age_url https://raw.githubusercontent.com/owid/covid-19-data/master/public/data/vaccinations/country_data/United_States.csv # 实际项目中需合并多国年龄数据此处简化为US示例 us_age pd.read_csv(age_url) us_age[date] pd.to_datetime(us_age[date]) # 取最新一天的有效数据避免空值 latest_date us_age[date].max() us_latest us_age[us_age[date] latest_date].dropna( subset[people_vaccinated_per_hundred_60plus, people_vaccinated_per_hundred_18to59] ).iloc[0] # 绘制单点散点图实际项目中应循环所有国家 fig_scatter px.scatter( x[us_latest[people_vaccinated_per_hundred_18to59]], y[us_latest[people_vaccinated_per_hundred_60plus]], size[100], # 固定气泡大小 titlefUS Vaccination Coverage by Age Group (as of {latest_date.date()}), labels{ x: 18-59 Years Coverage (%), y: 60 Years Coverage (%) }, templateplotly_white ) # 添加YX参考线 fig_scatter.add_shape( typeline, linedict(dashdash), x00, x1100, y00, y1100 ) fig_scatter.write_html(age_equity_scatter.html)第五步终极武器——交互式世界地图# 使用OWID的地理编码数据内置在vaccinations.csv中 # Plotly内置世界地图无需额外shapefile fig_map px.choropleth( df, locationsiso_code, # OWID数据自带ISO国家代码 colorpeople_fully_vaccinated_per_hundred, hover_namelocation, animation_framedate, # 关键让地图随时间播放 titleGlobal Full Vaccination Coverage (% of Population), color_continuous_scaleViridis, # 渐变色更易读 range_color[0, 100], templateplotly_white ) # 优化地图投影避免格陵兰岛等变形过大 fig_map.update_geos(projection_typenatural earth) # 导出为HTML文件体积稍大但交互完美 fig_map.write_html(vaccination_world_map.html)运行完这五段代码你会得到三个HTML文件vaccination_area_chart.html面积图、age_equity_scatter.html散点图、vaccination_world_map.html世界地图。双击任意一个用Chrome或Edge打开就能体验真正的交互式分析——缩放、拖拽、悬停看详情、点击图例筛选国家、拖动时间轴看历史演变。这种“所见即所得”的成就感是任何静态报告无法比拟的。注意第一次运行fig_map.write_html()可能较慢因为Plotly要打包所有地图数据。耐心等待完成后文件可离线使用不依赖网络。4.3 性能优化技巧让大数据跑得飞起来当你的数据量超过10万行比如合并了所有国家所有日期Plotly渲染可能变卡。我总结了三条实战经验第一预聚合再绘图。不要直接把原始数据喂给px.choropleth()。比如画世界地图你真正需要的只是“每个国家在最新日期的覆盖率”而不是所有历史记录。用Pandas先聚合# 只取每个国家的最新有效数据 df_latest df.sort_values(date).groupby(location).tail(1) # 再用df_latest绘图数据量瞬间减少90%第二禁用不必要的动画帧。animation_frame很酷但每增加一帧HTML文件体积就指数级增长。我的做法是分析阶段用完整动画发布给领导看时用frame_duration500每帧半秒transition_duration200过渡0.2秒控制节奏再用fig.layout.updatemenus[0].buttons[0].args[1][frame][duration] 500精确调优避免卡顿。第三用kaleido导出静态图备用。有时需要插入PPT或打印HTML不合适。kaleido能将Plotly图导出为高清PNG/SVG# 导出为300dpi PNG印刷级清晰度 fig_area.write_image(vaccination_area.png, width1200, height600, scale2)这招在我给疾控中心做汇报时救了大命——现场网络不稳提前导出的PNG图直接插入PPT丝滑播放。5. 常见问题与排查技巧实录5.1 “数据加载失败HTTPSConnectionPool”怎么办这是新手最高频的报错90%是因为网络问题。别急着搜“Python requests SSL error”先试试这三个步骤第一步确认URL是否可访问。把代码里的url变量值比如https://raw.githubusercontent.com/...直接复制粘贴到浏览器地址栏回车。如果浏览器打不开说明是本地网络问题不是代码问题。此时要么换网络比如用手机热点要么用国内镜像。OWID数据有清华镜像站把URL中的raw.githubusercontent.com替换成raw.fastgit.org免费公益镜像亲测速度提升5倍。第二步检查Pandas版本。旧版Pandas1.3对HTTPS支持不完善。运行pip list | grep pandas如果版本低于1.3立刻升级pip install --upgrade pandas。升级后重启Python内核问题常迎刃而解。第三步添加SSL上下文终极方案。如果前两步无效说明你的系统SSL证书库老旧。在代码开头加这三行import ssl ssl._create_default_https_context ssl._create_unverified_context这行代码告诉Python“别校验证书”虽有微小安全风险但在分析本地数据的场景下利远大于弊。我所有生产脚本都加了这行十年没出过事。5.2 “图表一片空白控制台没报错”怎么破这比报错更折磨人。我的排查清单如下检查数据是否为空。在绘图代码前加一行print(df.shape)。如果输出(0, 50)说明数据在清洗环节被全干掉了。回头检查valid_locations筛选逻辑很可能country_counts min_days条件太苛刻把你想分析的国家筛出去了。临时把min_days改成10试试。检查列名是否拼错。Plotly对列名大小写和空格极其敏感。OWID数据列名是total_vaccinations下划线不是totalVaccinations驼峰。用print(df.columns.tolist())打印所有列名逐字核对。我曾因把people_fully_vaccinated写成people_full_vaccinated调试两小时最后发现是少了一个l。检查日期格式。px.choropleth()的animation_frame必须是字符串类型日期如2021-03-15不能是datetime64。如果df[date]是datetime类型必须转df[date] df[date].dt.strftime(%Y-%m-%d)。这个坑我栽过三次每次都在深夜。5.3 “悬停信息显示NaN不显示数字”如何修复这是Plotly的“温柔陷阱”。默认情况下hover_data只显示原始列值如果该列有NaN悬停就显示空白。解决方案有两个方案一推荐用hover_data指定计算列。不要写hover_data[daily_vaccinations]而是写hover_data{ daily_vaccinations: :,.0f, # 格式化为千分位整数 people_fully_vaccinated_per_hundred: :.2f% # 格式化为百分比保留两位小数 }这里的:,.0f是Python格式化语法:开头,表示千分位.0f表示0位小数。Plotly会自动处理NaN显示为空白或“N/A”。方案二预填充NaN。在绘图前对关键悬停列做安全填充df[daily_vaccinations] df[daily_vaccinations].fillna(0).astype(int)但要注意填0可能误导所以方案一更优雅。5.4 “世界地图不显示中国/印度等国家”怎么解决这不是Bug是OWID的数据策略。OWID将中国、印度等大国的数据单独维护在country_data/子目录下主vaccinations.csv里只有汇总值。所以iso_code列里中国的代码是CHN但主文件里可能没有对应行。解决方案很简单不要用主文件画地图改用各国独立CSV。比如分析中国直接读china_url https://raw.githubusercontent.com/owid/covid-19-data/master/public/data/vaccinations/country_data/China.csv df_china pd.read_csv(china_url) df_china[date] pd.to_datetime(df_china[date]) # 然后用df_china画折线图、面积图效果更精准OWID为每个国家都提供了独立文件路径规律是country_data/{CountryName}.csv把空格换成下划线如United_States.csv。这个技巧让我在给地方政府做定制分析时数据精度提升了不止一个档次。6. 实战心得与避坑指南6.1 我踩过的五个“看似聪明实则愚蠢”的坑坑一试图用机器学习预测接种量。刚学完LSTM热血上头想用历史数据预测下周打多少针。结果模型R²只有0.3远不如用过去7天平均值预测准。后来才明白疫苗接种不是自然规律它受政策如某天突然开放60岁以上预约、物流冷链车到没到、舆情某网红发帖后接种点排长队等强人为因素驱动。数据科学的第一课是分清“可预测”和“不可预测”。对接种数据描述性分析Descriptive Analytics的价值远大于预测性分析Predictive Analytics。坑二过度追求“炫酷动画”。为了展示技术我把所有图表都加了animation_frame结果生成的HTML文件超过200MB发邮件被拒收微信发不了。后来悟了动画是手段不是目的。真正有价值的动画是能揭示趋势的如世界地图随时间演变而不是为了转圈圈。现在我的原则是只对核心洞察图加动画其他一律静态导出。坑三忽略数据延迟。OWID数据有1-3天延迟而各国官网可能当天更新。有次我用OWID数据做日报结论是“某国接种放缓”结果第二天该国官网发新闻“单日破纪录”。尴尬至极。现在我的脚本里加了醒目标注# NOTE: OWID data lags official sources by 1-3 days并在所有图表标题里写明“Data as of [date]”对读者负责也对自己负责。坑四硬套“标准配色”。一开始用Plotly默认蓝黄配色结果领导说“看着像医院通知”。后来研究色彩心理学绿色代表安全、健康蓝色代表信任、科技红色要慎用易联想到危险。现在我的标准配色方案是主图用blues渐变px.colors.sequential.Blues强调数据用emerald翡翠绿警示区域用gold金色比红色温和。一个配色方案让报告通过率提高了50%。坑五忘了“导出为图片”的终极需求。所有分析最终都要进PPT、进简报、进海报。我曾花三天做了个惊艳的交互地图结果汇报当天会议室电脑没装ChromeIE打不开HTML。血泪教训任何交互式图表必须同步导出一份静态高清图。现在我的工作流是write_html()write_image()一步不落文件命名带日期如map_20231015.png永远有备无患。6.2 给不同角色的实操建议给数据新手别一上来就挑战世界地图。从px.line()画自己国家的折线图开始把xdate,ytotal_vaccinations跑通看到图出来那一刻信心就建立了。然后加colorlocation画中美对比再加hover_data一步步来。记住完成比完美重要十倍。给公共卫生从业者你们最懂业务缺的是工具。把OWID数据和你们本地的社区接种台账Excel用pd.merge()合并比如用community_df.merge(owid_df, onlocation, howleft)就能把国家级宏观数据和街道级微观数据打通。我帮一个区疾控中心做过他们用这个方法精准定位出3个接种率低于全市均值的社区定向派发宣传车两周后覆盖率追平。给社区工作者别怕代码。把我的脚本下载下来只需要改三处url换成你们省的公开数据链接很多省份卫健委网站提供CSV下载top_countries改成你们市的几个区title改成“XX街道疫苗接种进度”。运行一次生成的HTML发到居民群大家点开就能看自己街道的实时进度比发十张截图都有说服力。最后分享一个小技巧Plotly图表右上角有个“下载”按钮↓图标点击就能把当前视图保存为PNG。开会时领导问“能把这张图截下来吗”你点一下3秒搞定全场目光聚焦你——这种时刻就是技术人最朴素的高光。