plotly + Dash:探索深圳市400例新冠肺炎个案可视化

笔记内容:

  • 数据获取
  • 用plotly(python) 实现400个病例来深时间,发病时间,入院时间概览
  • 用dash实现上述概览,并添加各个例性别,年龄,居住地情况筛选。

注意:

  • 数据来源为深圳市政府数据开放平台,注册即可获取。个案详情是一个详细表格,不是一坨通告,感恩。
  • 400例患者个案信息是2月14日及其之前发布的,现在已经不是400例了。

结果大概这样,这只是个很简陋的想法,不能说明任何流行病学上的问题。所以本质上只是我个人强行使用dash的练手项目。

数据获取

主要使用了深圳市政府数据开放平台公布数据中的个案详情作图
就新冠肺炎市政府公布了每日确诊病例,每个行政区每日上报的病例数目,以及在本笔记中用到的个案详情。需要先注册账号,然后才能获取。

选中后点击下载。(是有API的,但我没看明白=_=,既然有csv,我就直接下载了csv格式的数据)

可以预览一下:

用plotly实现400个病例来深时间,发病时间,入院时间概览

先将plotly部分的代码写好,做一个Figure对象出来。dash可以直接调用这个Figure。
先整理数据,很多来深时间为null的病例,为密切接触者,或者没有发病,筛查中核酸阳性,仍然按病例处理入院。整理成一个包含了400个病例来深时间,发病时间,入院时间,年龄,性别,居住地的dataframe.

import pandas as pd

case_report_file = 'C://Users//XXX//20200215_CoVtry//深圳市“新型肺炎”-每日新增确诊病例个案详情_2920001503668.csv'
case_report = pd.read_csv(case_report_file,sep=',',engine='python',encoding='utf-8')
case_report.index = case_report.blh.tolist()

# 把lssj, fbingsj, rysj 三个column提出来,即来深时间,发病时间,入院时间
time3 = case_report.loc[:,['lssj','fbingsj','rysj']]
time3.columns = ['sz_arrive','onset','hospitalized']
print('sz_arrive null: {0}'.format(time3['sz_arrive'].isnull().sum()),
      'onset null: {0}'.format(time3['onset'].isnull().sum()),
      'hospitalized: {0}'.format(time3['hospitalized'].isnull().sum()))
# sz_arrive null: 69 onset null: 10 hospitalized: 0
# 很多sz_arrive为null的病例,为密切接触者,或者没有发病,筛查中核酸阳性,仍然按病例处理入院。

time3 = time3.apply(lambda x:pd.to_datetime(x, errors='coerce'))
time3_sub = pd.concat([time3,case_report.loc[:,['xb','jzd','nl']]],sort=False,axis=1)
time3_sub.columns = ['sz_arrive', 'onset', 'hospitalized','gender','residence','age']


# 对居住和年龄段粗分类
def resi_class_assign(single_cell):
    # assign 居住地 as hubei_others, hubei_wuhan, shenzhen, others
    if '湖北' in single_cell:
        if '武汉' in single_cell:
            return 'hubei_wuhan'
        else:
            return 'hubei_others'
    elif '深圳' in single_cell:
        return 'shenzhen'
    
    else:
        return 'others'

def age_range_assign(single_cell):
    # assgin age(int) as <=20, 21-55, >=55
    if single_cell > 20:
        if single_cell > 55:
            return 'age >= 55'
        elif single_cell < 55:
            return 'age: 21-55'
    else:
        return 'age <=20'

time3_sub['residence_class'] = [resi_class_assign(str(i)) for i in time3_sub['residence'].tolist()]
time3_sub['age_range'] = [age_range_assign(int(i)) for i in time3_sub['age'].tolist()]

time3_sub.head() #如下所示:

然后画图:
将y轴当作1-400个病例的ID,x轴为时间。于是每个病例的来深时间,发病时间,入院时间,都可以通过<=3个点连成的一条线描述出来。

import plotly
from plotly.subplots import make_subplots
import plotly.graph_objects as go

trace_list = []
color_dict = dict(zip(time3.columns,plotly.colors.colorbrewer.Set1))

for i in time3.columns:
    tra = go.Scatter(x=time3[i].tolist(),
                     y=time3.index.tolist(),
                     mode='markers',
                     name=i,
                     marker=dict(color=color_dict[i],
                     size=4.5))
    trace_list.append(tra)

trace_line = []
for p in time3.index.tolist():
    trace_line.append(go.Scatter(x=time3.loc[p,:].tolist(),
                                 y=[p,p,p],
                                 mode='lines',
                                 name=p,
                                 line = dict(color='rgb(231,107,243)', width=2),
                                 connectgaps=True))


fig = go.Figure(data=trace_line+trace_list) 
fig.show()

上面是一个主图,给它加上副图,包含每个样本的性别,年龄段,居住地粗分类情况,都用不同的颜色来表示。

gender_color_dict = {'男': plotly.colors.sequential.Purpor[6], # dark purple
                     '女': plotly.colors.sequential.Burg[0]} # pink

residence_color_dict = dict(zip(['shenzhen','hubei_wuhan','hubei_others','others'],
                                plotly.colors.qualitative.T10))

age_color_dict = dict(zip(['age <=20','age: 21-55','age >= 55',None],
                          [plotly.colors.sequential.Agsunset[0],
                           plotly.colors.sequential.Agsunset[3],
                           plotly.colors.sequential.Agsunset[6],'black']))

def single_class_trace_fun(df_filter,cc):
    # cc: string in 'gender','age_range','residence_class','time3'
    cc_color= {'gender':gender_color_dict,
               'age_range':age_color_dict,
               'residence_class':residence_color_dict}
    single_class_trace = []
    for c in df_filter[cc].unique():
        single_class_df = df_filter.loc[df_filter[cc]==c,:]
        single_class_trace.append(go.Scatter(x=[cc]*len(single_class_df.index.tolist()),
                                             y=single_class_df.index.tolist(),
                                             mode='markers',
                                             name = c,
                                             marker=dict(color=cc_color[cc][c],
                                                         size=4.5)))
    return single_class_trace


# 把副图和主图合在一起
sub_traces = []
for i in ['gender','age_range','residence_class']:
    sub_traces += single_class_trace_fun(time3_sub,i)

fig_sub = make_subplots(rows=1, cols=2,column_widths=[0.2, 0.8],
                        shared_yaxes=True,
                        horizontal_spacing=0.01)
for i in sub_traces:
    fig_sub.add_trace(i,row=1,col=1)
for i in trace_line+trace_list:
    fig_sub.add_trace(i,row=1,col=2)

fig_sub.update_layout(height=1600, width=850)

fig_sub.show()

用dash实现上述概览,并添加各个例性别,年龄,居住地情况筛选

=_=这里用dash只是为自己练练手,写成一个app.py,然后在终端运行,Running on http://127.0.0.1:8050/...
在浏览器中访问http://127.0.0.1:8050/即可。
app.py代码在https://github.com/CS0000/shenzhen_400case_reports_overview

得到如文章开头所见的结果。

此外:

  1. dash官网及其教程:https://dash.plot.ly/
  2. 注意时间格式,以及时间空值NaT
  3. 有个全国的丁香园爬虫数据:
    是为人数记录,即每日累计病例,累计死亡等,不包含每个患者的情况。而深圳市政府公开数据包含除了总人数情况以外,还有每个病例的个案详情及每个行政区域的人数分布。
    丁香园爬虫及API (感恩造轮子的人):
    API获取:https://lab.isaaclin.cn/nCoV/ 这个接口为从丁香园上爬取的数据,包括全国和各省市的数据。原github项目为https://github.com/BlankerL/DXY-COVID-19-Crawler

请求并保存数据:

import requests
import picke
gd = requests.get('https://lab.isaaclin.cn/nCoV/api/area',params={'latest':0,'province':'广东省'})
gd.json() #看一下

with open('C://Users//XX//Desktop//gd_20200208.pickle','wb') as f:
    pickle.dump(gd,f) # 保存下来
  1. 还想做的:
  • 把各个病例的在外地活动时间段,出院时间加进来;
  • 给每个病例一个家族聚集的标签,同一个标签则为一家人(或一群朋友),
  • 做年龄的rangeslider,可以随意拖一个年龄区间,看这个年龄区间内的患者情况。
  • 优化筛选后的图,不是简单的扣掉不符合条件的病例。(go.Sactter(y=...))
  • ...再说吧

你可能感兴趣的:(plotly + Dash:探索深圳市400例新冠肺炎个案可视化)