实验室近期需要采集地市级的疫情数据。目前能找到的大部分数据源是省级粒度的时间序列数据或地市级的实时截面数据,起初找到了这个项目
2019新型冠状病毒疫情实时爬虫https://github.com/BlankerL/DXY-COVID-19-Crawler
使用了丁香园的数据。但是发现了丁香园数据中的几个问题:
- 统计口径较混乱,同一地级市在不同的时间指向不同的字段名;
- 部分省(广东、四川、吉林、甘肃)没有单独统计境外输入数据,境外输入被归于各地级市中,该部分数据无法清洗;
- 部分数据在新时间中被修正,没有同步修正回旧时间,出现累计确诊数倒减的情况。
受问题2影响,最后还是找新数据源重新采集并清洗一遍数据。本项目使用腾讯新闻api获取并处理新冠疫情地市级时间序列数据,数据所有权为腾讯新闻,脚本及获取数据仅作参考与学习用,对数据质量不做担保。本人无法授权任何个人或团体在科研或商业项目中使用本数据,如有需要,希望您能够联系腾讯新闻并取得许可。如有建议或异常反馈,欢迎提交issue。
腾讯新闻疫情实时追踪页面:https://news.qq.com/zt2020/page/feiyan.htm#/global
祝愿大家一切都好!
下载文件。保证getAreaData.py和covid19_area_timeseries_data.py在同一路径下,运行covid19_area_timeseries_data.py即可
打开网页后开启浏览器的开发者工具,选中Network查看网页传输的文件,就可以比较顺利地找到腾讯的api,写了一个很简单的请求就抓齐了数据(见getAreaData.py)。
返回的json中时间序列不齐,只包含卫健委公布实时新闻当天的更新数据,因此在两次时间中的空缺数据需要我自行填补(两次公告之前的数据理论上不变,取时间靠前的数据填补缺失值)
api需要的参数是省份province和城市(直辖市的区)city,api的制作使用了一个睿智办法肉身编码。需要注意港澳台只有province没有city参数,所以另开了一个循环单独处理。地名字典不是完整字典,有部分是从百度疫情信息扒下来的,会有几个无确诊的地级市获取不到数据,视为"截至当前时间确诊为0"处理。
api请求和json解析使用的基本都是封装好的方法,略。
先观察一下原数据
Unnamed: 0 | province | city | y | date | confirm | dead | heal | suspect | confirm_add | |
---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 广东 | 广州 | 2020 | 01.28 | 51 | 0.0 | 0.0 | 0.0 | NaN |
1 | 1 | 广东 | 广州 | 2020 | 01.29 | 63 | 0.0 | 0.0 | 0.0 | NaN |
2 | 2 | 广东 | 广州 | 2020 | 01.30 | 94 | 0.0 | 3.0 | 0.0 | NaN |
3 | 3 | 广东 | 广州 | 2020 | 01.31 | 112 | 0.0 | 3.0 | 0.0 | NaN |
4 | 4 | 广东 | 广州 | 2020 | 02.01 | 150 | 0.0 | 3.0 | 0.0 | NaN |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
34526 | 34526 | 台湾 | 台湾 | 2021 | 09.11 | 16074 | 839.0 | 13742.0 | NaN | 5.0 |
34527 | 34527 | 台湾 | 台湾 | 2021 | 09.12 | 16088 | 839.0 | 13742.0 | NaN | 14.0 |
34528 | 34528 | 台湾 | 台湾 | 2021 | 09.13 | 16093 | 839.0 | 13742.0 | NaN | 5.0 |
34529 | 34529 | 台湾 | 台湾 | 2021 | 09.14 | 16098 | 839.0 | 13742.0 | NaN | 5.0 |
34530 | 34530 | 台湾 | 台湾 | 2021 | 09.15 | 16098 | 839.0 | 13742.0 | NaN | 5.0 |
关于时间序列的补齐,我的想法是城市列表与时间期限作笛卡尔积后,再连接原表。这样有记录时间的数据就会被填入,两次公告之间没有记录的数据留空。时间期限取所有数据记录中最早和最晚的两天。这时又发现一个小问题,出现了未来的数据(9-30),检查后发现是有些api返回数据的时间一直连续至未来,只是数值不变。过滤掉这部分假未来数据即可。
province | city | times | |
---|---|---|---|
0 | 广东 | 广州 | 2020-01-20 |
1 | 广东 | 广州 | 2020-01-21 |
2 | 广东 | 广州 | 2020-01-22 |
3 | 广东 | 广州 | 2020-01-23 |
4 | 广东 | 广州 | 2020-01-24 |
... | ... | ... | ... |
267835 | 台湾 | 台湾 | 2021-09-26 |
267836 | 台湾 | 台湾 | 2021-09-27 |
267837 | 台湾 | 台湾 | 2021-09-28 |
267838 | 台湾 | 台湾 | 2021-09-29 |
267839 | 台湾 | 台湾 | 2021-09-30 |
province | city | realdate | confirm | dead | heal | suspect | confirm_add | |
---|---|---|---|---|---|---|---|---|
0 | 广东 | 广州 | 2020-01-20 | NaN | NaN | NaN | NaN | NaN |
1 | 广东 | 广州 | 2020-01-21 | NaN | NaN | NaN | NaN | NaN |
2 | 广东 | 广州 | 2020-01-22 | NaN | NaN | NaN | NaN | NaN |
3 | 广东 | 广州 | 2020-01-23 | NaN | NaN | NaN | NaN | NaN |
4 | 广东 | 广州 | 2020-01-24 | NaN | NaN | NaN | NaN | NaN |
... | ... | ... | ... | ... | ... | ... | ... | ... |
267838 | 台湾 | 台湾 | 2021-09-26 | NaN | NaN | NaN | NaN | NaN |
267839 | 台湾 | 台湾 | 2021-09-27 | NaN | NaN | NaN | NaN | NaN |
267840 | 台湾 | 台湾 | 2021-09-28 | NaN | NaN | NaN | NaN | NaN |
267841 | 台湾 | 台湾 | 2021-09-29 | NaN | NaN | NaN | NaN | NaN |
267842 | 台湾 | 台湾 | 2021-09-30 | NaN | NaN | NaN | NaN | NaN |
接下来填补缺失值。重设index为['province','city','date']的组合,再以此index做groupby分组填补数据,不然会出现bfill中下一个城市的第一天填补上一个城市最后一天数据的情况。
先使用bfill填补"过去日期中有数据"的空缺,将这部分空缺视为"期间数据无变化",取过去时间最新数据;然后再用fillna将剩下的缺失值填0,因为此时的缺失值在过去日期中没有数据,说明可能是最早还没有进行新冠疫情公告的时候,此时默认为0。
最后做一个自表连接,计算每日新增。新建一列'yesterday'为date-1天,然后使用'date'和'yesterday'做自连接,计算两天差异即可。
realdate_x | confirm_x | dead_x | heal_x | suspect_x | yesterday_x | realdate_y | confirm_y | dead_y | heal_y | suspect_y | yesterday_y | ||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
province | city | ||||||||||||
广东 | 广州 | 2020-01-20 | 0.0 | 0.0 | 0.0 | 0.0 | 2020-01-19 | NaT | NaN | NaN | NaN | NaN | NaT |
广州 | 2020-01-21 | 0.0 | 0.0 | 0.0 | 0.0 | 2020-01-20 | 2020-01-20 | 0.0 | 0.0 | 0.0 | 0.0 | 2020-01-19 | |
广州 | 2020-01-22 | 0.0 | 0.0 | 0.0 | 0.0 | 2020-01-21 | 2020-01-21 | 0.0 | 0.0 | 0.0 | 0.0 | 2020-01-20 | |
广州 | 2020-01-23 | 0.0 | 0.0 | 0.0 | 0.0 | 2020-01-22 | 2020-01-22 | 0.0 | 0.0 | 0.0 | 0.0 | 2020-01-21 | |
广州 | 2020-01-24 | 0.0 | 0.0 | 0.0 | 0.0 | 2020-01-23 | 2020-01-23 | 0.0 | 0.0 | 0.0 | 0.0 | 2020-01-22 | |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
台湾 | 台湾 | 2021-09-11 | 16074.0 | 839.0 | 13742.0 | 0.0 | 2021-09-10 | 2021-09-10 | 16069.0 | 839.0 | 13742.0 | 0.0 | 2021-09-09 |
台湾 | 2021-09-12 | 16088.0 | 839.0 | 13742.0 | 0.0 | 2021-09-11 | 2021-09-11 | 16074.0 | 839.0 | 13742.0 | 0.0 | 2021-09-10 | |
台湾 | 2021-09-13 | 16093.0 | 839.0 | 13742.0 | 0.0 | 2021-09-12 | 2021-09-12 | 16088.0 | 839.0 | 13742.0 | 0.0 | 2021-09-11 | |
台湾 | 2021-09-14 | 16098.0 | 839.0 | 13742.0 | 0.0 | 2021-09-13 | 2021-09-13 | 16093.0 | 839.0 | 13742.0 | 0.0 | 2021-09-12 | |
台湾 | 2021-09-15 | 16098.0 | 839.0 | 13742.0 | 0.0 | 2021-09-14 | 2021-09-14 | 16098.0 | 839.0 | 13742.0 | 0.0 | 2021-09-13 |
最后去掉多余的列,调整列名。
realdate_x | confirm_x | dead_x | heal_x | suspect_x | confirm_cal_add | suspect_cal_add | dead_cal_add | heal_cal_add | ||
---|---|---|---|---|---|---|---|---|---|---|
province | city | |||||||||
广东 | 广州 | 2020-01-20 | 0.0 | 0.0 | 0.0 | 0.0 | NaN | NaN | NaN | NaN |
广州 | 2020-01-21 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | |
广州 | 2020-01-22 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | |
广州 | 2020-01-23 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | |
广州 | 2020-01-24 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 | |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
台湾 | 台湾 | 2021-09-11 | 16074.0 | 839.0 | 13742.0 | 0.0 | 5.0 | 0.0 | 0.0 | 0.0 |
台湾 | 2021-09-12 | 16088.0 | 839.0 | 13742.0 | 0.0 | 14.0 | 0.0 | 0.0 | 0.0 | |
台湾 | 2021-09-13 | 16093.0 | 839.0 | 13742.0 | 0.0 | 5.0 | 0.0 | 0.0 | 0.0 | |
台湾 | 2021-09-14 | 16098.0 | 839.0 | 13742.0 | 0.0 | 5.0 | 0.0 | 0.0 | 0.0 | |
台湾 | 2021-09-15 | 16098.0 | 839.0 | 13742.0 | 0.0 | 0.0 | 0.0 | 0.0 | 0.0 |
收工。
- 可能会搭个云服务器和数据库,定期把数据导进去
- 可能会搭个前端页面做一下可视化