用Python进行新型冠状病毒(COVID-19/2019-nCoV)疫情分析(上)
重要说明
分析文档:完成度:代码质量 3:5:2
其中分析文档是指你数据分析的过程中,对各问题分析的思路、对结果的解释、说明(要求言简意赅,不要为写而写)
由于数据过多,查看数据尽量使用head()或tail(),以免程序长时间无响应
=======================
本项目数据来源于丁香园。本项目主要目的是**通过对疫情历史数据的分析研究,以更好的了解疫情与疫情的发展态势,为抗击疫情之决策提供数据支持。**
关于本章使用的数据集,欢迎点击——>我的视频 在评论区获取。
一. 提出问题
从全国范围,你所在省市,国外疫情等三个方面主要研究以下几个问题:
(一)全国累计确诊/疑似/治愈/死亡情况随时间变化趋势如何?
(二)全国新增确诊/疑似/治愈/死亡情况随时间变化趋势如何?
(三)全国新增境外输入随时间变化趋势如何?
(四)你所在的省市情况如何?
(五)国外疫情态势如何?
(六)结合你的分析结果,对个人和社会在抗击疫情方面有何建议?
二. 理解数据
原始数据集:AreaInfo.csv,导入相关包及读取数据:
r_hex = '#dc2624' # red, RGB = 220,38,36
dt_hex = '#2b4750' # dark teal, RGB = 43,71,80
tl_hex = '#45a0a2' # teal, RGB = 69,160,162
r1_hex = '#e87a59' # red, RGB = 232,122,89
tl1_hex = '#7dcaa9' # teal, RGB = 125,202,169
g_hex = '#649E7D' # green, RGB = 100,158,125
o_hex = '#dc8018' # orange, RGB = 220,128,24
tn_hex = '#C89F91' # tan, RGB = 200,159,145
g50_hex = '#6c6d6c' # grey-50, RGB = 108,109,108
bg_hex = '#4f6268' # blue grey, RGB = 79,98,104
g25_hex = '#c7cccf' # grey-25, RGB = 199,204,207
import numpy as np
import pandas as pd
import matplotlib,re
import matplotlib.pyplot as plt
from matplotlib.pyplot import MultipleLocator
data = pd.read_csv(r'data/AreaInfo.csv')
**查看与统计数据,以对数据有一个大致了解**
data.head()
三. 数据清洗
(一)基本数据处理
数据清洗主要包括:**选取子集,缺失数据处理、数据格式转换、异常值数据处理**等。
国内疫情数据选取(最终选取的数据命名为china)
1. 选取国内疫情数据
2. 对于更新时间(updateTime)列,需将其转换为日期类型并提取出年-月-日,并查看处理结果。(提示:dt.date)
3. 因数据每天按小时更新,一天之内有很多重复数据,请去重并只保留一天之内最新的数据。
> 提示:df.drop_duplicates(subset=['provinceName', 'updateTime'], keep='first', inplace=False)
> 其中df是你选择的国内疫情数据的DataFrame
分析:选取countryName一列中值为中国的行组成CHINA。
CHINA = data.loc[data['countryName'] == '中国']
CHINA.dropna(subset=['cityName'], how='any', inplace=True)
#CHINA
分析:取出含所有中国城市的列表
cities = list(set(CHINA['cityName']))
分析:遍历取出每一个城市的子dataframe,然后用sort对updateTime进行时间排序
for city in cities:
CHINA.loc[data['cityName'] == city].sort_values(by = 'updateTime')
分析:去除空值所在行
CHINA.dropna(subset=['cityName'],inplace=True)
#CHINA.loc[CHINA['cityName'] == '秦皇岛'].tail(20)
#检查部分数据
分析:将CHINA中的updateTime列进行格式化处理
CHINA.updateTime = pd.to_datetime(CHINA.updateTime,format="%Y-%m-%d",errors='coerce').dt.date
#CHINA.loc[data['cityName'] == '秦皇岛'].tail(15)
#部分数据检查
CHINA.head()
分析:每日数据的去重只保留第一个数据,因为前面已经对时间进行排序,第一个数据即为当天最新数据
分析:考虑到合并dataframe需要用到concat,需要创建一个初始china
real = CHINA.loc[data['cityName'] == cities[1]]
real.drop_duplicates(subset='updateTime', keep='first', inplace=True)
china = real
分析:遍历每个城市dataframe进行每日数据的去重,否则会出现相同日期只保留一个城市的数据的情况
for city in cities[2:]:
real_data = CHINA.loc[data['cityName'] == city]
real_data.drop_duplicates(subset='updateTime', keep='first', inplace=True)
china = pd.concat([real_data, china],sort=False)
查看数据信息,是否有缺失数据/数据类型是否正确。
提示:若不会处理缺失值,可以将其舍弃
分析:有的城市不是每日都上报的,如果某日只统计上报的那些城市,那些存在患者却不上报的城市就会被忽略,数据就失真了,需要补全所有城市每日的数据,即便不上报的城市也要每日记录数据统计,所以要进行插值处理补全部分数据,处理方法详见数据透视与分析
china.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 32812 entries, 96106 to 208267
Data columns (total 19 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 continentName 32812 non-null object
1 continentEnglishName 32812 non-null object
2 countryName 32812 non-null object
3 countryEnglishName 32812 non-null object
4 provinceName 32812 non-null object
5 provinceEnglishName 32812 non-null object
6 province_zipCode 32812 non-null int64
7 province_confirmedCount 32812 non-null int64
8 province_suspectedCount 32812 non-null float64
9 province_curedCount 32812 non-null int64
10 province_deadCount 32812 non-null int64
11 updateTime 32812 non-null object
12 cityName 32812 non-null object
13 cityEnglishName 31968 non-null object
14 city_zipCode 32502 non-null float64
15 city_confirmedCount 32812 non-null float64
16 city_suspectedCount 32812 non-null float64
17 city_curedCount 32812 non-null float64
18 city_deadCount 32812 non-null float64
dtypes: float64(6), int64(4), object(9)
memory usage: 5.0+ MB
china.head()
你所在省市疫情数据选取(最终选取的数据命名为myhome)
此步也可在后面用到的再做
myhome = china.loc[data['provinceName'] == '广东省']
myhome.head()
国外疫情数据选取(最终选取的数据命名为world)
此步也可在后面用到的再做
world = data.loc[data['countryName'] != '中国']
world.head()
数据透视与分析
分析:对china进行插值处理补全部分数据
分析:先创建省份列表和日期列表,并初始化一个draft
province = list(set(china['provinceName']))#每个省份
#p_city = list(set(china[china['provinceName'] == province[0]]['cityName']))#每个省份的城市
date_0 = []
for dt in china.loc[china['provinceName'] == province[0]]['updateTime']:
date_0.append(str(dt))
date_0 = list(set(date_0))
date_0.sort()
start = china.loc[china['provinceName'] == province[0]]['updateTime'].min()
end = china.loc[china['provinceName'] == province[0]]['updateTime'].max()
dates = pd.date_range(start=str(start), end=str(end))
aid_frame = pd.DataFrame({'updateTime': dates,'provinceName':[province[0]]*len(dates)})
aid_frame.updateTime = pd.to_datetime(aid_frame.updateTime,format="%Y-%m-%d",errors='coerce').dt.date
#draft = pd.merge(china.loc[china['provinceName'] == province[1]], aid_frame, on='updateTime', how='outer').sort_values('updateTime')
draft = pd.concat([china.loc[china['provinceName'] == province[0]], aid_frame], join='outer').sort_values('updateTime')
draft.province_confirmedCount.fillna(method="ffill",inplace=True)
draft.province_suspectedCount.fillna(method="ffill", inplace=True)
draft.province_curedCount.fillna(method="ffill", inplace=True)
draft.province_deadCount.fillna(method="ffill", inplace=True)
分析:补全部分时间,取前日的数据进行插值,因为有的省份从4月末开始陆续就不再有新增病患,不再上报,所以这些省份的数据只能补全到4月末,往后的数据逐渐失去真实性
分析:同时进行日期格式化
for p in range(1,len(province)):
date_d = []
for dt in china.loc[china['provinceName'] == province[p]]['updateTime']:
date_d.append(dt)
date_d = list(set(date_d))
date_d.sort()
start = china.loc[china['provinceName'] == province[p]]['updateTime'].min()
end = china.loc[china['provinceName'] == province[p]]['updateTime'].max()
dates = pd.date_range(start=start, end=end)
aid_frame = pd.DataFrame({'updateTime': dates,'provinceName':[province[p]]*len(dates)})
aid_frame.updateTime = pd.to_datetime(aid_frame.updateTime,format="%Y-%m-%d",errors='coerce').dt.date
X = china.loc[china['provinceName'] == province[p]]
X.reset_index(drop= True)
Y = aid_frame
Y.reset_index(drop= True)
draft_d = pd.concat([X,Y], join='outer').sort_values('updateTime')
draft = pd.concat([draft,draft_d])
draft.province_confirmedCount.fillna(method="ffill",inplace=True)
draft.province_suspectedCount.fillna(method="ffill", inplace=True)
draft.province_curedCount.fillna(method="ffill", inplace=True)
draft.province_deadCount.fillna(method="ffill", inplace=True)
#draft['updateTime'] = draft['updateTime'].strftime('%Y-%m-%d')
#draft['updateTime'] = pd.to_datetime(draft['updateTime'],format="%Y-%m-%d",errors='coerce').dt.date
china = draft
china.head()
四. 数据分析及可视化
在进行数据分析及可视化时,依据每个问题选取所需变量并新建DataFrame再进行分析和可视化展示,这样数据不易乱且条理更清晰。
基础分析
基础分析,只允许使用numpy、pandas和matplotlib库。
可以在一张图上多个坐标系展示也可以在多张图上展示
请根据分析目的选择图形的类型(折线图、饼图、直方图和散点图等等),实在没有主意可以到百度疫情地图或其他疫情分析的站点激发激发灵感。
(一)全国累计确诊/疑似/治愈/死亡情况随时间变化趋势如何?
分析:要获得全国累计情况随时间变化趋势,首先需要整合每日全国累计确诊情况做成date_confirmed
分析:要整合每日全国累计确诊情况,首先得提取每个省份每日当天最新累计确诊人数,省份数据求和后形成dataframe,for循环拼接到date_confirmed中
date = list(set(china['updateTime']))
date.sort()
china = china.set_index('provinceName')
china = china.reset_index()
分析:循环遍历省份和日期获得每个省份每日累计确诊,因为需要拼接,先初始化一个date_confirmed
list_p = []
list_d = []
list_e = []
for p in range(0,32):
try:
con_0 = china.loc[china['updateTime'] == date[2]].loc[china['provinceName'] == province[p]].iloc[[0]].iloc[0]
list_p.append(con_0['province_confirmedCount'])#该日每省的累计确诊人数
except:
continue
list_d.append(sum(list_p))
list_e.append(str(date[0]))
date_confirmed = pd.DataFrame(list_d,index=list_e)
date_confirmed.index.name="date"
date_confirmed.columns=["China_confirmedCount"]
#date_confirmed
分析:遍历每个省份拼接每日的总确诊人数的dataframe
l = 0
for i in date[3:]:
list_p = []
list_d = []
list_e = []
l +=1
for p in range(0,32):
try:
con_0 = china.loc[china['updateTime'] == date[l]].loc[china['provinceName'] == province[p]].iloc[[0]].iloc[0]
list_p.append(con_0['province_confirmedCount'])#该日每省的累计确诊人数
except:
continue
#con_0 = china.loc[china['updateTime'] == date[0]].loc[china['provinceName'] == '河北省'].loc[[0]].iloc[0]
#list_p.append(con_0['province_confirmedCount'])#该日每省的累计确诊人数
list_d.append(sum(list_p))
list_e.append(str(date[l]))
confirmed = pd.DataFrame(list_d, index=list_e)
confirmed.index.name="date"
confirmed.columns=["China_confirmedCount"]
date_confirmed = pd.concat([date_confirmed,confirmed],sort=False)
#date_confirmed
分析:去除空值和不全的值
date_confirmed.dropna(subset=['China_confirmedCount'],inplace=True)
date_confirmed.tail(20)

分析:数据从4月末开始到5月末就因为缺失过多省份的数据(部分省份从4月末至今再也没有新增病患)而失真,自2020-06-06起完全失去真实性,所以我删除了2020-06-06往后的数据
date_confirmed = date_confirmed.drop(['2020-06-06','2020-06-07','2020-06-08','2020-06-09','2020-06-10','2020-06-11','2020-06-12','2020-06-13','2020-06-14',
'2020-06-15','2020-06-16','2020-06-19','2020-06-18','2020-06-20','2020-06-17','2020-06-21'])
分析:构造拼接函数
def data_frame(self,china,element):
l = 0
for i in date[3:]:
list_p = []
list_d = []
list_e = []
l +=1
for p in range(0,32):
try:
con_0 = china.loc[china['updateTime'] == date[l]].loc[china['provinceName'] == province[p]].iloc[[0]].iloc[0]
list_p.append(con_0[element])
except:
continue
#con_0 = china.loc[china['updateTime'] == date[0]].loc[china['provinceName'] == '河北省'].loc[[0]].iloc[0]
#list_p.append(con_0['province_confirmedCount'])
list_d.append(sum(list_p))
list_e.append(str(date[l]))
link = pd.DataFrame(list_d, index=list_e)
link.index.name="date"
link.columns=["China"]
self = pd.concat([self,link],sort=False)
self.dropna(subset=['China'],inplace=True)
self = self.drop(['2020-06-06','2020-06-07','2020-06-08','2020-06-09','2020-06-10','2020-06-11','2020-06-12','2020-06-13','2020-06-14',
'2020-06-15','2020-06-16','2020-06-19','2020-06-18','2020-06-20','2020-06-17','2020-06-21'])
return self
分析:初始化各个变量
#累计治愈人数 date_curedCount
list_p = []
list_d = []
list_e = []
for p in range(0,32):
try:
con_0 = china.loc[china['updateTime'] == date[2]].loc[china['provinceName'] == province[p]].iloc[[0]].iloc[0]
list_p.append(con_0['province_curedCount'])
except:
continue
list_d.append(sum(list_p))
list_e.append(str(date[0]))
date_cured = pd.DataFrame(list_d, index=list_e)
date_cured.index.name="date"
date_cured.columns=["China"]
#累计死亡人数 date_dead
list_p = []
list_d = []
list_e = []
for p in range(0,32):
try:
con_0 = china.loc[china['updateTime'] == date[2]].loc[china['provinceName'] == province[p]].iloc[[0]].iloc[0]
list_p.append(con_0['province_deadCount'])
except:
continue
list_d.append(sum(list_p))
list_e.append(str(date[0]))
date_dead = pd.DataFrame(list_d, index=list_e)
date_dead.index.name="date"
date_dead.columns=["China"]
#累计确诊患者 date_confirmed
plt.rcParams['font.sans-serif'] = ['SimHei'] #更改字体,否则无法显示汉字
fig = plt.figure( figsize=(16,6), dpi=100)
ax = fig.add_subplot(1,1,1)
x = date_confirmed.index
y = date_confirmed.values
ax.plot( x, y, color=dt_hex, linewidth=2, linestyle='-' )
ax.set_title('累计确诊患者',fontdict={
'color':'black',
'size':24
})
ax.set_xticks( range(0,len(x),30))

#累计治愈患者 date_curedCount
date_cured = data_frame(date_cured,china,'province_curedCount')
fig = plt.figure( figsize=(16,6), dpi=100)
ax = fig.add_subplot(1,1,1)
x = date_cured.index
y = date_cured.values
ax.set_title('累计治愈患者',fontdict={
'color':'black',
'size':24
})
ax.plot( x, y, color=dt_hex, linewidth=2, linestyle='-' )
ax.set_xticks( range(0,len(x),30))

分析:累计疑似无法通过补全数据得到
#累计死亡患者 date_dead
date_dead = data_frame(date_dead,china,'province_deadCount')
fig = plt.figure( figsize=(16,6), dpi=100)
ax = fig.add_subplot(1,1,1)
x = date_dead.index
y = date_dead.values
ax.plot( x, y, color=dt_hex, linewidth=2, linestyle='-' )
x_major_locator=MultipleLocator(12)
ax=plt.gca()
ax.set_title('累计死亡患者',fontdict={
'color':'black',
'size':24
})
ax.xaxis.set_major_locator(x_major_locator)
ax.set_xticks( range(0,len(x),30))

分析:疫情自1月初开始爆发,到2月末开始减缓增速,到4月末趋于平缓。治愈人数自2月初开始大幅增加,到3月末趋于平缓,死亡人数自1月末开始增加,到2月末趋于平缓,到4月末因为统计因素死亡人数飙升后趋于平缓。
分析总结:确诊人数数据和治愈数据从4月末开始到5月末就因为缺失过多省份的数据(部分省份至今再也没有新增病患)导致失真,其他数据尽量通过补全,越靠近尾部数据越失真。死亡数据补全较为成功,几乎没有错漏。