【Dash搭建可视化网站】项目10:疫情数据可视化大屏制作步骤详解

news2024/11/15 20:36:01

疫情数据可视化大屏制作步骤详解

  • 1 项目效果图
  • 2 项目架构
  • 3 文件介绍和功能完善
    • 3.1 assets文件夹介绍
    • 3.2 app.py和index.py文件完善
    • 3.3 header.py文件完善
    • 3.4 cards.py文件完善
    • 3.5 api.py和api.ipynb文件完善
      • 3.5.1 数据获取
      • 3.5.2 数据处理
      • 3.5.3 接口数据导入header.py和cards.py文件中使用
    • 3.6 indicators文件完善
    • 3.7 piechart.py文件完善
    • 3.8 barlinechart.py文件完善
    • 3.9 mapchart.py文件完善
  • 4 样式修改
    • 4.1 整体样式的修改
    • 4.2 index.py中样式的修改
    • 4.3 header.py中样式修改
    • 4.4 indicators.py中样式修改
    • 4.5 piechart.py中样式修改
    • 4.6 barlinechart.py中样式修改
    • 4.7 mapchart.py中样式修改

手动反爬虫:原博地址 https://blog.csdn.net/lys_828/article/details/128605640

 知识梳理不易,请尊重劳动成果,文章仅发布在CSDN网站上,在其他网站看到该博文均属于未经作者授权的恶意爬取信息

1 项目效果图

整个项目的页面包含四部分,由头部信息,卡片数据,多图表以及动态地图可视化组成,项目代码已上传 个人Github仓库。
请添加图片描述

2 项目架构

项目中各文件名称与对应功能见下表
在这里插入图片描述

3 文件介绍和功能完善

3.1 assets文件夹介绍

assets文件夹中就是加载的样式和图片,样式这里可以使用之前已经下载好的bootstrap.min.css样式(也可以按照 项目6.2bootstrap组件 中的介绍下载其它的样式模板),图片就是Covid的一个模型图片,对应如下。
在这里插入图片描述

3.2 app.py和index.py文件完善

主框架就是app.py项目初始化文件和index.py主程序运行文件,其中app.py文件中的信息较为简单,就是创建一个dash的应用,代码如下

from dash import Dash

app = Dash('__name__')

index.py文件中引入初始化后的应用,然后进行布局及运行初始化设置。

from app import app
import dash_bootstrap_components as dbc


from header import HeaderInfo
from cards import CardInfo
from indicators import IndicatorInfo
from piechart import PieInfo
from barlinechart import BarLineInfo
from mapchart import MapInfo


app.layout = dbc.Col(
    [
        dbc.Row([HeaderInfo()]),
        dbc.Row([CardInfo()]),
        dbc.Row([IndicatorInfo(),PieInfo(),BarLineInfo()]),
        dbc.Row([MapInfo()]),
    ]
)

if __name__ == '__main__':
    app.run_server(debug=True)

对应的index.py文件截图如下。
在这里插入图片描述

然后在对应的文件中创建函数,搭建基本的框架,各文件的基础信息如下。
在这里插入图片描述

3.3 header.py文件完善

项目的头部信息包含了三个部分,可以按照一行三列的布局设置,第一列放置图片居左,第二列放置标题居中,第三列放置更新时间居右。

import dash_bootstrap_components as dbc

def HeaderInfo():
    return dbc.Row(
        [
            dbc.Col([None]),
            dbc.Col([None]),
            dbc.Col([None]),
        ]
    )

其中图片的加载和位置设置,以及标题文字的大小及距离的设置相对简单,最主要解决第三列更新时间的问题,首先给定一个今天的时间作为样例,后续根据接口数据获取的具体时间再更换,在api.ipynb文件中将时间转化为目标样式的代码如下。

import pandas as pd

today = pd.to_datetime('today').strftime('%B %d %Y')
today

输出结果为:
在这里插入图片描述

将上述代码复制粘贴到api.py文件中并通过语句导入到header.py文件,完善此文件的信息,代码如下。

import dash_bootstrap_components as dbc
from dash import html
from api import today

def HeaderInfo():
    return dbc.Row(
        [
            dbc.Col(
                [
                    html.Img(src='assets/corona-logo-1.jpg',height='100px',width='100px')
                ],style = {'textAlign':'left','paddingLeft':'6rem','paddingTop':'1rem'}
            ),
            dbc.Col(
                [
                    html.H1('Covid - 19'),
                    html.H6('Track Covid - 19 Cases',className='p-2')
                ],style = {'textAlign':'center','paddingTop':'1rem'}
            ),
            dbc.Col(
                [
                    html.H5(f'Last Updated: {today} (UTC)'),
                ],style = {'textAlign':'right','paddingRight':'6rem','paddingTop':'3rem'}
            ),
        ]
    )

保存修改后,回到index.py文件执行,并刷新网址:http://127.0.0.1:8050/。此时界面输出内容如下:
在这里插入图片描述
左右两侧的距离浏览器的距离也太远了,问题就在于最开始使用了dbc.Container()容器,在默认加载css样式中,导致添加的内容自动居中,因此可以把容器调整为html.Div(),修改后的index.py文件中的代码内容如下:
在这里插入图片描述

保存后刷新网址,此时左右两侧的内容距离浏览器的距离正常了。
在这里插入图片描述

3.4 cards.py文件完善

通过观察最终的图片样式,卡片数据部分的内容基本布局一致,包含了上中下三个部分,第一部分就是文字说明统计类型,中间为该类型的数据,最下面是增加数量和百分比。可封装一个函数card(),保留数据接口,设置参数为typenumdiffdiff_per四个参数,先用简单示例数据进行赋值,设置完毕后,再处理数据。

def card(type,num,diff,diff_per,color):

    return dbc.Col(
        [
            html.H6(f'Global {type}'),
            html.H3(num,style={'color':color}),
            html.Span(f'New {diff} ({diff_per})%',style={'color':color})
        ]
    )

接着完善该文件,此时全部代码如下。

在这里插入图片描述

保存文件后,刷新网址,此时界面显示信息如下。
在这里插入图片描述

3.5 api.py和api.ipynb文件完善

其中api.py文件是为提供数据接口,api.iypnb文件方便进行数据处理和调式工作。前面已经用来处理更新的时间格式,这里就针对真实的Covid数据进行数据导入。此项目使用的数据来自于约翰霍普金斯大学系统科学与工程中心(JHU CSSE)运营的2019年新型冠状病毒可视化仪表盘的数据存储库,数据来源网址:COVID-19

3.5.1 数据获取

Covid数据中的确诊、死亡、治愈信息所在网址:

url_confirmed = 'https://raw.githubusercontent.com/CSSEGISandData/COVID-19/master/csse_covid_19_data/csse_covid_19_time_series/time_series_covid19_confirmed_global.csv'
url_deaths = 'https://raw.githubusercontent.com/CSSEGISandData/COVID-19/master/csse_covid_19_data/csse_covid_19_time_series/time_series_covid19_deaths_global.csv'
url_recovered = 'https://raw.githubusercontent.com/CSSEGISandData/COVID-19/master/csse_covid_19_data/csse_covid_19_time_series/time_series_covid19_recovered_global.csv'

使用pandas可以直接读取网址链接中的数据,但是前提是用户可以正常访问github。如果无法正常访问Github网址,后续会把读取到的数据保存到本地,并放置在assets文件夹下新建的data文件夹下, 读者可以在下载项目案例后使用本地csv文件读取数据。读取数据的代码如下:

#读取数据
confirmed = pd.read_csv(url_confirmed)
deaths = pd.read_csv(url_deaths)
recovered = pd.read_csv(url_recovered)

数据存放在confirmeddeathsrecovered三个变量中,分别查看前5条数据,输出结果如下:
在这里插入图片描述

根据输出结果发现对于治愈人数最近的时期中前5行数据均为0,需要核实一下是不是所有的国家的数值均为0,如果是,说明对于治愈的数据该数据库就不再进行统计。比如选取最近20天的数据,进行数值汇总,发现输出结果中个字段的总和均为0,说明该数据库已经不再更新治愈数据。
在这里插入图片描述

考虑进一步提取有治愈数据的信息,首先确定何时该数据库不再统计此部分数据。此处判断的方式就是对每个数值字段进行求和汇总,如果连续5个字段的汇总值都是0,说明连续5个字段中的第一个字段就是数据库停止更新的日期。

ls = recovered[recovered.columns[4:]].sum().to_list()
for i in range(len(ls)):
    if ls[i] == ls[i+1] == ls[i+2] == ls[i+3] == ls[i+4]:
        print(i)
        break

输出结果如下:(字段计算是从第5列开始,因为前4个字段都不是数值字段)
在这里插入图片描述

输出结果中显示从第561+4也就是565个字段位置开始停止数据更新,可以进一步核实情况,对该字段的前5个字段和后5个字段计数汇总输出如下。
在这里插入图片描述

为了更严谨一些,可以对2021年8月5日往后的数据进行汇总值计算,核实是否为0。结果为-8,有点出人意料,进一步,找出汇总值不为0的字段的统计值,发现结果都是-1,代表着未收录数据。因此可以断定治愈数据在2021年8月5日停止更新。
在这里插入图片描述

可以把数据保存到本地assets文件夹下的data文件夹中,由于没有data文件夹,需要先创建,可以手动在assets文件夹中创建,也可以使用代码进行创建,并把数据保存到此文件夹中,代码如下及输出结果如下。

#首先把执行路劲转换到项目的assets文件夹下
import os
os.chdir(r'D:\Data Science\plotly学习\个人\【10. Covid data visualizing】Dropdown-pie-line-map-indicator-bar\assets')

#判断该路径下是否有data文件夹,如果没有就创建
if not os.path.exists('data'):
    os.mkdir('data')

#把数据生成到对应的文件中,并取消index列,这里index就是数值序号
confirmed.iloc[:,:565].to_csv('data/confirmed.csv',index=False)
deaths.iloc[:,:565].to_csv('data/death.csv',index=False)
recovered.iloc[:,:565].to_csv('data/recovered.csv',index=False)

对应的界面和data文件中的内容如下。
在这里插入图片描述

3.5.2 数据处理

重新读取本地文件数据(当然也可以直接使用iloc[:,:565]获取的数据。由于已经在data文件生成了数据文件,所以在api.py文件中就不需要再重新获取数据,直接读入数据就行,此处的重新读取本地文件数据就是为了方便把代码直接复制粘贴到api.py文件中)

在这里插入图片描述
由于获取的数据都是“长数据”,需要转化为“短数据”,即把全部日期的字段变成一个字段,实现数据的逆透视。借助pandas下的melt()函数功能可以实现。

#数据逆透视:把在标题上的信息转化成为单元格中信息(即行转列)
total_confirmed = pd.melt(confirmed,id_vars=confirmed.columns[:4],value_vars=confirmed.columns[4:],var_name='date',value_name='confirmed')
total_deaths = pd.melt(deaths,id_vars=deaths.columns[:4],value_vars=deaths.columns[4:],var_name='date',value_name='death')
total_recovered = pd.melt(recovered,id_vars=recovered.columns[:4],value_vars=recovered.columns[4:],var_name='date',value_name='recovered')

输出结果如下:(函数中的第一个参数是要进行转化的DataFrame,第二个参数就是指定的不变的字段,第三个参数就是变化的字段,第四个参数就是个变化的字段进行合并成一个字段后的字段名称,最后一个参数就是值汇总后的字段名称)
在这里插入图片描述
同样也可以输出转化后的死亡和治愈数据,但是发现治愈数据的记录条数要比确诊和死亡的数据量要少一些。
在这里插入图片描述

为了保证同一性,把三个数据进行合并,最终的数据量以死亡和确诊数据量为准,把治愈数据合并进来,最终形成完整数据covid_data

#数据合并
covid_data = total_confirmed.merge(right = total_deaths, how = 'left', on = ['Province/State', 'Country/Region', 'date', 'Lat', 'Long'])
covid_data = covid_data.merge(right = total_recovered, how = 'left', on = ['Province/State', 'Country/Region', 'date', 'Lat', 'Long'])
covid_data

代码及输出结果如下。
在这里插入图片描述
接着创建衍生字段,也就是项目卡片数据中的第四个卡片的内容数据,即active,该字段的数据是确诊数据-死亡数据-治愈数据。由于治愈字段在合并之前没有那么多数据量,所以在合并后系统默认以缺失值标记,因此在创建字段之前需要先处理缺失值。

#缺失值处理
covid_data['recovered'] = covid_data['recovered'].fillna(0)

#衍生字段创建
covid_data['active'] = covid_data['confirmed'] - covid_data['death'] - covid_data['recovered']
covid_data

代码及输出结果如下。
在这里插入图片描述
按照日期字段进行汇总,求解comfirmed、death、recovered和active字段的数据。

#将日期转化为时间格式(会自动进行解析)
covid_data['date'] = pd.to_datetime(covid_data['date'])

#获取按照日期汇总得到的确诊,死亡和治愈和active的人员
covid_data_1 = covid_data.groupby('date')[['confirmed','death','recovered','active']].sum().reset_index()
covid_data_1

代码及输出结果如下:(为了方便后续的时间字段的操作,可以将date字段转变datetime数据类型)
在这里插入图片描述
卡片数据中的汇总数据就得到了,只需要获取最后一行数据即可,接着就是求解卡片下方的增加值和百分比数据。

#获取最近一天的确诊,死亡和治愈和active的人员值
confirmed_num = covid_data_1['confirmed'].iloc[-1]
rate_confirmed = (confirmed_num - covid_data_1['confirmed'].iloc[-2])

death_num = covid_data_1['death'].iloc[-1]
rate_death = death_num - covid_data_1['death'].iloc[-2]

recovered_num = 'No data available' if covid_data_1['recovered'].iloc[-1] ==0 else covid_data_1['recovered'].iloc[-1]
rate_recovered = 'No data available' if recovered_num == 'No data available' else  recovered_num - covid_data_1['recovered'].iloc[-2]

active_num = covid_data_1['active'].iloc[-1]
rate_active = active_num - covid_data_1['active'].iloc[-2]

代码及输出结果如下:(百分比数据是基于倒数第二行和倒数第一行之间数据之比,但是需要强调的是,数据为0 代表着没有数据可获取,此外,增幅百分比的计算,需要考虑分母为0的情况,因此百分比计算就不放在接口文件中,而是在cards.py进行计算)
在这里插入图片描述

3.5.3 接口数据导入header.py和cards.py文件中使用

在api.ipynb文件中核实无误后,把代码转移到api.py文件中,此时api.py文件中的代码如下。

import pandas as pd

today = pd.to_datetime('today').strftime('%B %d %Y')

import os
os.chdir(r'D:\Data Science\plotly学习\个人\【10. Covid data visualizing】Dropdown-pie-line-map-indicator-bar\assets')
confirmed = pd.read_csv('data/confirmed.csv')
deaths = pd.read_csv('data/death.csv')
recovered = pd.read_csv('data/recovered.csv')

#数据逆透视:把在标题上的信息转化成为单元格中信息(即行转列)
total_confirmed = pd.melt(confirmed,id_vars=confirmed.columns[:4],value_vars=confirmed.columns[4:],var_name='date',value_name='confirmed')
total_deaths = pd.melt(deaths,id_vars=deaths.columns[:4],value_vars=deaths.columns[4:],var_name='date',value_name='death')
total_recovered = pd.melt(recovered,id_vars=recovered.columns[:4],value_vars=recovered.columns[4:],var_name='date',value_name='recovered')

#数据合并
covid_data = total_confirmed.merge(right = total_deaths, how = 'left', on = ['Province/State', 'Country/Region', 'date', 'Lat', 'Long'])
covid_data = covid_data.merge(right = total_recovered, how = 'left', on = ['Province/State', 'Country/Region', 'date', 'Lat', 'Long'])

#缺失值处理
covid_data['recovered'] = covid_data['recovered'].fillna(0)

#衍生字段创建
covid_data['active'] = covid_data['confirmed'] - covid_data['death'] - covid_data['recovered']

#将日期转化为时间格式(会自动进行解析)
covid_data['date'] = pd.to_datetime(covid_data['date'])

#获取按照日期汇总得到的确诊,死亡和治愈和active的人员
covid_data_1 = covid_data.groupby('date')[['confirmed','death','recovered','active']].sum().reset_index()

#获取最近一天的确诊,死亡和治愈和active的人员值
confirmed_num = covid_data_1['confirmed'].iloc[-1]
rate_confirmed = (confirmed_num - covid_data_1['confirmed'].iloc[-2])

death_num = covid_data_1['death'].iloc[-1]
rate_death = death_num - covid_data_1['death'].iloc[-2]

recovered_num = 'No data available' if covid_data_1['recovered'].iloc[-1] ==0 else covid_data_1['recovered'].iloc[-1]
rate_recovered = 'No data available' if recovered_num == 'No data available' else  recovered_num - covid_data_1['recovered'].iloc[-2]

active_num = covid_data_1['active'].iloc[-1]
rate_active = active_num - covid_data_1['active'].iloc[-2]

然后将api.py中的数据导入到cards.py文件和header.py文件中,进行数据替换。首先更换header.py文件中的数据更新时间,为简化header.py文件内容,数据处理工作在api.py文件中完成后再传入,保证后续使用时候只需要修改数据接口文件,而不用动其它文件。在api.py文件中添加如下代码获取数据最近的更新时间。

#更新header.py中的数据获取时间
updata_time = covid_data_1['date'].iloc[-1].strftime('%B %d %Y')

然后把update_time导入到header.py文件中,顺带修改一下文件中样式,使之和最终效果的布局一致。

import dash_bootstrap_components as dbc
from dash import html
from api import updata_time

def HeaderInfo():
    return dbc.Row(
        [
            dbc.Col(
                [
                    html.Img(src='assets/corona-logo-1.jpg',height='100px',width='100px')
                ],style = {'textAlign':'left','paddingLeft':'6rem','paddingTop':'1rem'}
            ),
            dbc.Col(
                [
                    html.H1('Covid - 19'),
                    html.H6('Track Covid - 19 Cases',className='p-2')
                ],style = {'textAlign':'center','paddingTop':'1rem'}
            ),
            dbc.Col(
                [
                    html.H5(f'Last Updated: {updata_time}'),
                    html.H5('00:01(UTC)',style={'paddingRight':'5rem'})
                ],style = {'textAlign':'right','paddingRight':'6rem','paddingTop':'2rem'}
            ),
        ]
    )

保存文件修改后,刷新网址,当前界面中头部信息更新时间完美实现数据更新。
在这里插入图片描述
接着处理卡片中的数据和样式。考虑到缺失值的问题,在card()函数中删掉百分比参数diff_per,而是进一步判断后再确定,防止因缺失数据导致程序运行保存的现象,最终cards.py中的代码如下。


from dash import html
import dash_bootstrap_components as dbc
from api import *

def card(type,num,diff,color):

    if num == 'No data available':
        diff_per = 'No data available'
    else:
        diff_per = diff/num * 100

    return dbc.Col(
        [
            html.H6(f'Global {type}'),
            html.H2(num,style={'color':color}),
            html.P(f'New {int(diff)} ({diff_per:.2f})%',style={'color':color,'fontSize':5})
        ]
    )

def CardInfo():
    return dbc.Row(
        [
            card('cases',confirmed_num,rate_confirmed,'orange'),
            card('death',death_num,rate_death,'red'),
            card('recovered',recovered_num,rate_recovered,'green'),
            card('active',active_num,rate_active,'black')
        ],style = {'textAlign':'center','paddingTop':'1rem'}
    )

保存代码后,刷新网页,界面输出结果如下。
在这里插入图片描述

3.6 indicators文件完善

该文件中主要是由Dropdown组件搭配着Indicators图像构成,搭建主体框架,使之满足正常功能。先把第三部分的布局完善,该部分可以分为1行3列,在三个函数中均输入如下内容,查看是否水平排列。
在这里插入图片描述

保存文件修改后,刷新网页,此时界面上输出结果如下。
在这里插入图片描述
此时本应三列的内容,变成三行输出了, 因此需要在index.py文件中修改一下配置,在第三部分添加一个行设置,如下
在这里插入图片描述

此时保存文件后再刷新网址,发现布局样式满足一行三列的排布。
在这里插入图片描述
布局解决后,开始处理indicators.py文件中的功能完善,基础先把文字和Dropdown组件信息设置出来,由于indicators图像是回调产生,因此只设置一个容器增加id即可,后续设置回调函数即可。

首先获取Dropdown组件的标签信息,此处选择的是国家或者地区,因此可以在covid_data中获取Country/Region字段的唯一值。特别要注意标签信息不能包含None值,不然程序会报错,所以在构建Dropdown组件时,特别留意列表中不包含None值。
在这里插入图片描述
然后把这部分代码复制粘贴到api.py文件中,再导入到indicators.py文件中。完善Dropdown组件和文字信息,代码如下

import dash_bootstrap_components as dbc
from dash import html,Input,Output,dcc
from api import country_ls,updata_time

def IndicatorInfo():
    return dbc.Col(
        [
            html.H6('Select Country:'),
            dcc.Dropdown(
                country_ls,
                'China',
                id = 'dpd'
            ),
            html.H6(f'New cases: {updata_time}'),
            dcc.Graph(id='fig1')

        ],width=3,style={'textAlign':'center'}
    )

保存文件后,刷新网页,此时页面显示结果如下
在这里插入图片描述

正常显示后,可以设置回调函数,输出Indicators图形,比如先以中国的确诊数据为例,绘制出对比数据更新日期当天的indicator图形(即今天相对于昨天的涨跌指标图),借鉴官方的示例代码。
在这里插入图片描述

有了图像示例代码后,就需要构建数据接口,在api.ipynb文件中获取每个国家/地区每天的疫情信息,代码及输出结果如下。
在这里插入图片描述

以中国为例,数据更新之日的新增确诊数据和较昨日新增的确诊数据获取方式如下。

today_new_add = covid_data_2[covid_data_2['Country/Region'] == 'China'].iloc[-1]['confirmed'] - covid_data_2[covid_data_2['Country/Region'] == 'China'].iloc[-2]['confirmed'] 
yesterday_new_add = covid_data_2[covid_data_2['Country/Region'] == 'China'].iloc[-2]['confirmed'] - covid_data_2[covid_data_2['Country/Region'] == 'China'].iloc[-3]['confirmed'] 
print('今日新增:{},昨日新增{},indicator指标{}'.format(today_new_add,yesterday_new_add,today_new_add-yesterday_new_add))

输出结果如下。
在这里插入图片描述

进一步将获取的过程封装为函数indicator(),传入国家country和covid数据类型type,最后函数返回就是要绘制的figure对象。先把获取每个国家/地区每天的信息covid_data_2变量复制粘贴到api.py文件汇总,然后导入到indicators.py文件里。
在这里插入图片描述

此时indicators.py文件中的代码如下。

import dash_bootstrap_components as dbc
from dash import html,Input,Output,dcc
from api import country_ls,updata_time,covid_data_2
import plotly.graph_objects as go
from app import app

def IndicatorInfo():
    return dbc.Col(
        [
            html.H6('Select Country:'),
            dcc.Dropdown(
                country_ls,
                'China',
                id = 'dpd'
            ),
            html.H6(f'New cases: {updata_time}'),
            dcc.Graph(id='confirmed'),
            dcc.Graph(id='death'),
            dcc.Graph(id='recovered'),
            dcc.Graph(id='active'),

        ],width=3,style={'textAlign':'center'}
    )

def indicator(country,type):
    today_new_add = covid_data_2[covid_data_2['Country/Region'] == country].iloc[-1][type] - covid_data_2[covid_data_2['Country/Region'] == country].iloc[-2][type] 
    yesterday_new_add = covid_data_2[covid_data_2['Country/Region'] == country].iloc[-2][type] - covid_data_2[covid_data_2['Country/Region'] == country].iloc[-3][type] 

    return go.Figure(go.Indicator(
                title = {'text': f'New {type}'},
                mode = "number+delta",
                value = today_new_add,
                delta = {'position': "bottom", 'reference': yesterday_new_add},
                domain = {'x': [0, 1], 'y': [0, 1]}))

@app.callback(
    [Output('confirmed','figure'),Output('death','figure'),Output('recovered','figure'),Output('active','figure')],
    [Input('dpd','value')]
)
def update(value):
    return (
        indicator(value,'confirmed'),
        indicator(value,'death'),
        indicator(value,'recovered'),
        indicator(value,'active'),
    ) 

保存文件后刷新网址,界面运行结果如下。(此时三个indicator图像均可以正常加载,点击下拉菜单可以实现数据更新,但是样式和大小需要进一步调整)
在这里插入图片描述

对于两个数值的大小和位置,可以通过number和delta两个参数进行控制。
在这里插入图片描述

修改后保存,刷新网址,此时界面显示出数值的大小适合,但是标题太小了,因此需要再修改一下标题样式。
在这里插入图片描述
标题颜色也需要进行修改,因此需要在indicator()函数中添加一个color参数。此次修改的地方如下:

  • 对于indictors图形容器和后面回调的Output的内容存在了重复,可以考虑列表推导式简化
  • 然后对于颜色参数可以添加到字体和数值中
  • 图像的高度也需要调整
import dash_bootstrap_components as dbc
from dash import html,Input,Output,dcc
from api import country_ls,updata_time,covid_data_2
import plotly.graph_objects as go
from app import app

ls = ['confirmed','death','recovered','active']

def IndicatorInfo():
    return dbc.Col(
        [
            html.H6('Select Country:',style={'paddingTop':20}),
            dcc.Dropdown(
                country_ls,
                'China',
                id = 'dpd'
            ),
            html.H6(f'New cases: {updata_time}',style={'paddingTop':20}),
            html.Div(
                [
                    dcc.Graph(id=type,config={'displayModeBar': False}) for type in ls
                ],
            )
        ],width=3,style={'textAlign':'center','paddingLeft':45,'paddingRight':45}
    )

def indicator(country,type,color):
    today_new_add = covid_data_2[covid_data_2['Country/Region'] == country].iloc[-1][type] - covid_data_2[covid_data_2['Country/Region'] == country].iloc[-2][type] 
    yesterday_new_add = covid_data_2[covid_data_2['Country/Region'] == country].iloc[-2][type] - covid_data_2[covid_data_2['Country/Region'] == country].iloc[-3][type] 

    fig = go.Figure(go.Indicator(
                title = {'text': f'New {type}','font': {'size': 15,'color':color}},
                mode = "number+delta",
                number = {'valueformat': ',',
                    'font': {'size': 15,'color':color}},
                value = today_new_add,
                delta = {'position': "right", 'reference': yesterday_new_add,'valueformat': ',',
                    'relative': False,
                    'font': {'size': 15}},
                domain = {'x': [0, 1], 'y': [0, 1]}))
                    
    fig.update_layout(
        height = 75  #这个地方卡了很久,如果添加weight属性就不会居中,所以直接去掉就可
    )
    return fig

@app.callback(
    [Output(type,'figure') for type in ls],
    [Input('dpd','value')]
)
def update(value):
    return (
        indicator(value,'confirmed','orange'),
        indicator(value,'death','red'),
        indicator(value,'recovered','green'),
        indicator(value,'active','black')
        )

保存文件后,刷新网址,界面显示内容如下。
在这里插入图片描述

3.7 piechart.py文件完善

此文件功能就是对Dropdown组件选中的标签,进行疫情四项指标的占比输出。主要的问题在于获取各项指标数据,由于各指标名称和字段名称是相同的,所以还是使用列表推导式的方式进行简化代码。构建pie图核心参数一个是values,还有一个names,这两参数都与四个指标相关,最终的代码如下。

import dash_bootstrap_components as dbc
from dash import html,Input,Output,dcc
from app import app
from api import covid_data_2
import plotly.express as px

ls = ['confirmed','death','recovered','active']
def PieInfo():
    return dbc.Col(id = 'pie',width=4,
    style={'textAlign':'center','paddingTop':20})

@app.callback(
    Output('pie','children'),
    [Input('dpd','value')]
)
def update(value):
    lastest_record = covid_data_2[covid_data_2['Country/Region'] == value].iloc[-1]
    fig = px.pie(values=[lastest_record[i] for i in ls],names=ls)
    fig.update_layout(
        legend={
            'orientation':'h',
            'xanchor':'center',
            'yanchor':'bottom',
            'x':0.5,
            'y':-0.15
            },
        margin=dict(
            t=25,
            b=125
        )
    )
    return [
        html.H6(f'Total Cases: {value}'),
        dcc.Graph(figure=fig,config={'displayModeBar': False})
    ]

保存修改后,刷新网址,此时界面中输出的内容如下。(费时间的地方在于pie图的位置和legend的位置放置,需要慢慢调整参数,达到自己满意的一个样式)
在这里插入图片描述

3.8 barlinechart.py文件完善

该文件的功能是按照Dropdown组件选取的标签,绘制对应国家/地区的最近30天的疫情确诊数据,以及近七天的平滑数据曲线。首先在api.ipynb文件中获取最近30天的疫情确诊数据,以中国为例。

先提取中国确诊的疫情数据,并借助shift(1)方法,求解每日新增的确诊数量。
在这里插入图片描述
然后借rolling(7)方法获取数据7日曲线数据。
在这里插入图片描述
最后就是对最后30条数据进行切片即可获取对应的数据。把此过程封装为函数get_30_days_data(),然后保存在api.py文件中,方便调用。
在这里插入图片描述
数据都准备完毕后,开始完善barlinechart.py文件。其中go.Figure()中可以绘制多个图形,图形之间用列表存放;还有重点关于交互图中的hover_text的设置,此处可以根据自己需要任意设定显示信息,比如这里显示的有日期、数量和国家,扩宽了数据的可视化的角度;此外还有y轴相关信息的设置,同理x轴的设置也是同理,该文件中全部的代码如下。

import dash_bootstrap_components as dbc
from dash import html,Input,Output,dcc
from app import app
from api import get_30_days_data
import plotly.graph_objects as go

def BarLineInfo():
    return dbc.Col(id='barline',style={'textAlign':'center','paddingTop':20})

@app.callback(
    Output('barline','children'),
    [Input('dpd','value')]
)
def update(value):

    covid_data_30 = get_30_days_data(value)

    fig = go.Figure([
                go.Bar(
                    x = covid_data_30['date'].tail(30),
                    y = covid_data_30['confirmed new add'].tail(30),
                    name='Daily Confirmed Cases',
                    marker = {'color':'orange'},
                    hoverinfo='text',
                    hovertext=
                    '<b>Date</b>: ' + covid_data_30['date'].tail(30).astype(str)+'<br>' +
                    '<b>Daily Confirmed Cases</b>:' + [f'{i:.0f}' for i in covid_data_30['confirmed new add'].tail(30)]+'<br>' +
                    '<b>Country</b>: ' + covid_data_30['Country/Region'].tail(30).astype(str) + '<br>'
                ),
                go.Scatter(
                    x=covid_data_30['date'].tail(30),
                    y=covid_data_30['Rolling Ave.'].tail(30),
                    mode='lines',
                    name='Rolling Average of the last 7 days - Daily Confirmed Cases',
                    marker={'color': 'pink'},
                    hoverinfo='text',
                    hovertext=
                    '<b>Date</b>: ' + covid_data_30['date'].tail(30).astype(str) + '<br>' +
                    '<b>Rolling Ave.</b>:' + [f'{i:.0f}' for i in covid_data_30['Rolling Ave.'].tail(30)] + '<br>'
                )
            ]
    )
    fig.update_layout(
        legend={
            'orientation':'h',
            'xanchor':'center',
            'yanchor':'bottom',
            'x':0.5,
            'y':-0.25
            },
        margin=dict(
            t=25,
            b=150,
            l=0,
            r=70
        ),
        yaxis={
                'title': '<b>Daily Confirmed Cases</b>',
                'color': 'black',
                'showline': True,
                'showgrid': True,
                'showticklabels': True,
                'linecolor': 'white',
                'linewidth': 1,
                'ticks': 'outside',
                'tickfont': {
                    'family': 'Aerial',
                    'color': 'white',
                    'size': 12
                }
            }
    )

    return [
        html.H6(f'Last 30 Days Confirmed Cases: {value}'),
        dcc.Graph(
            figure=fig,config={'displayModeBar': False}
        )
    ]

保存文件后刷新网页,界面中显示的内容如下。
在这里插入图片描述

3.9 mapchart.py文件完善

该文件的功能实现各国家地区确诊数据随着时间变化的地图可视化。关于地图可视化,可以参考官方示例代码。由于地理数据是属于面数据(区别于具体的点数据),因此可以采用px.choropleth()方法绘制。
在这里插入图片描述

现在关键点落到地理信息的匹配上,获取的数据中虽然有国家/区域信息,但是和px.choropleth()方法绘制地图时需要的地理数据不一致,因此需要将目前手上的数据通过某一特定标识进行转化。

px.choropleth()方法中的location参数指定的iso_alpha就可以作为参考,国家/地区的iso编码都是唯一的,因此可以用过地理位置信息。而所有国家的iso编码信息,项目中提供iso编码.txt文件,如下。
在这里插入图片描述
从中提取iso编码(对应第一列)、国家/地区名称(英文是倒数第二列,中文是倒数第一列),代码如下。
在这里插入图片描述

经过仔细观察后发现,获取的数据库中的疫情数据对应的国家/地区,与iso编码中的国家/地区并不完全匹配。
在这里插入图片描述
可以创建一个替换字典,将国家/地区名称化为一致,然后借助replace()方法进行数据替换。
在这里插入图片描述
核实数据是否替换成功。以韩国,朝鲜为例,数据清洗完毕。
在这里插入图片描述
接着就是把iso数据和疫情数据进行合并,合并的依据,一个是以Country/Region字段,一个是以country字段,合并的代码和输出结果如下
在这里插入图片描述
至此把绘制地图可视化的数据准备完毕,接着就是把以上处理数据的代码转移到api.py文件中,并把最终的geo_data变量导入到mapchart.py文件汇总。 在实际测试的过程中发现针对演示的时间字段也需要进行处理,只提取数据的年月,并把数据转化为字符串格式(原来时间的格式是datetime数据格式)
在这里插入图片描述
然后把这两行代码更新到api.py中,然后完善mapchart.py文件,全部代码如下

from dash import html,Input,Output,dcc
from app import app
from api import geo_data
import plotly.express as px

def MapInfo():

    #考虑到测试计算机的性能,可以指定数据采样,比如只取十分之一的数据进行演示
    geo_data_resample = geo_data.sample(frac = 0.1)

    fig = px.choropleth(geo_data_resample.sort_values('date_play'),               
                                locations="iso",               
                                color="confirmed",
                                hover_name="Country/Region",  
                                animation_frame="date_play",    
                                color_continuous_scale='amp',  
                                height=600)

    fig.update_layout(
        margin=dict(
            l=0,
            r=0,
            b=0,
            t=25,
            pad=0
        )
    )
    return dcc.Graph(
            figure= fig
    )

保存文件后,刷新网址,页面输出结果如下。(受限于测试计算机的性能,使用所有的数据量会导致演示地图稍微卡顿,项目中设置了一个采样方法,这里使用了十分之一的数据量。此外还发现在地图的动态演示中,发现格陵兰岛的数据始终空白)
在这里插入图片描述
可以考虑补充这部分数据,假定都是以0进行填充。(这里是实现某一地区数据缺失时的处理方式,如果有真实数据,直接用真实数据按照此方法填补即可),在api.py文件中添加格陵兰岛的数据,并把数据合并到geo_data变量中,如下
在这里插入图片描述
此时保存文件后,刷新网页,动态演示过程中,格陵兰岛的颜色不再是灰白色了,数据值为0。
在这里插入图片描述

4 样式修改

4.1 整体样式的修改

在assets文件夹下新建一个setting.css文件,设置整体风格。最后一行的overflow-y设置是参照 实战7中3.4 table.py文件完善部分 防止全部全局撑开后网页抖动的设置,具体介绍可以参考实战7。 背景设置为一种蓝色调,字体颜色设置为白色。
在这里插入图片描述

setting.py文件中的全部代码如下。(其中只有两个属性是项目9中没有的属性,div_container属性控制四部分的整体布局,card_container属性用于控制组件的样式)

html,body{ 
    background:#0d0247;
    color: white;
    overflow-y:scroll;} 

.div_container{
    text-align: center;
    margin:  20px;
    padding: 20px;
    margin-top: 5px;
}

.card_container{
    border-radius: 5px;
    background-color: #182757;
    margin: 16px;
    padding: 20px;
    position: relative;
    box-shadow: 2px 2px 2px #1e294e;
}   

 /* width */
 ::-webkit-scrollbar {
    width: 10px !important;
    display: block !important;
  }


/* Track */
::-webkit-scrollbar-track {
    background: #232c37 !important;
    border-radius: 10px !important;
    display: block !important;
    }
    
/* Handle */
::-webkit-scrollbar-thumb {
background: #363535 !important;
}

/* Handle on hover */
::-webkit-scrollbar-thumb:hover {
background: white !important;
}

#dpd * {
    background-color: #182757 !important;
    color: #7f9ab4 !important;
    font-size: 10px !important;
}

.Select-control{
    border: 3px solid #323844 !important;
}

.Select-menu-outer{
    overflow: hidden !important;
    border-color: #36425e !important;
}

4.2 index.py中样式的修改

把div_container属性应用于整个Div,也就是核心四部分的对齐和布局上。
在这里插入图片描述

4.3 header.py中样式修改

只需要修改一下更新时间的颜色。剩下的一些样式根据实际调试过程中的变化进行修改,比如把00:01(UTC)单独另起一行,然后调整距离右侧的边距,使之与上面的日期文字呈现中间对齐的样子。
在这里插入图片描述

4.4 indicators.py中样式修改

在Col容器中应用卡片属性card_container,并根据实际显示调整width的值,原来是3,现在调整为2.5。
在这里插入图片描述

关于绘制的图形中白色背景去除的问题,只需要添加两行代码即可(其余图形中的背景去除也是相同方式)。#182757就是卡片属性中的背景颜色。
在这里插入图片描述

4.5 piechart.py中样式修改

和4.4部分修改的内容一致。
在这里插入图片描述

4.6 barlinechart.py中样式修改

和4.4和4.5中一样
在这里插入图片描述

4.7 mapchart.py中样式修改

和前面的修改一致。
在这里插入图片描述
但是不知怎么最后一个地图的部分总是显示左右无法和前面部分的组件对齐,因此在index.py中单独设置一下左右边距,实测左侧少了15px,右侧多了15px,如下
在这里插入图片描述

至此,整个项目就梳理完毕,撒花✿✿ヽ(°▽°)ノ✿

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/151015.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringMVC基本使用

SpringMVC基本使用1、回顾MVC1.1、什么是MVC1.2、Model1时代1.3、Model2时代1.4、回顾Servlet2、什么是SpringMVC2.1、概述2.2、中心控制器2.3、SpringMVC执行原理3、HelloSpring3.1、配置版3.2、注解版3.3、小结4、Controller 及 RestFul4.1、控制器Controller4.2、实现Contro…

【笔记:第一课】学习开发一个RISC-V上的操作系统 - 汪辰 - 2021春

文章目录前言来源正文小结前言 创作开始时间&#xff1a;2023年1月9日20:02:19 如题&#xff0c;学习一下RISC-V。 来源 https://www.bilibili.com/video/BV1Q5411w7z5/ 正文 打好基础&#xff01;好好学习 本课程目的&#xff1a; 了解 RISC-V 的相关知识学会查看RISC-…

week10

T1 Einstein学画画 题目描述 Einstein 学起了画画。 此人比较懒~~&#xff0c;他希望用最少的笔画画出一张画…… 给定一个无向图&#xff0c;包含 nnn 个顶点&#xff08;编号 1∼n1 \sim n1∼n&#xff09;&#xff0c;mmm 条边&#xff0c;求最少用多少笔可以画出图中所…

解决RuntimeError: CUDA error: out of memory

注意&#xff1a;报错内容只有这一行&#xff0c;RuntimeError: CUDA error: out of memory&#xff0c;没有后面的内存分析。 因为报错的时候忘记截图了&#xff0c;修改好了才来记录的。这里引用别的博主的图片。图片来源 1&#xff1a;刚开始我怀疑是batchsize设的太大了&a…

vue01-基础

一、vue简介 1.1 描述 一套用于构建用户界面的渐进式JavaScript框架 构建用户界面&#xff1a;把数据处理成界面 渐进式&#xff1a;可以从简单应用引入的轻量小巧核心库&#xff0c;扩展至各式vue插件 1.2 特点 1.组件化模式&#xff0c;提高代码复用率且便于维护&#…

【自学Python】Python整型(int)

Python整型(int) Python整型(int)教程 Python 整型专门用来表示整数。 Python 整型支持四种形式&#xff0c;即十进制形式、二进制形式、八进制形式和十六进制形式。 Python整型(int) 十进制形式 Python 最常见的整数就是十进制形式的整数。在使用十进制表示整数值时&…

虚拟化技术学习笔记7

1、KVM虚拟机CPU热添加&#xff1a; # 能够了解虚拟机添加CPU的作用及预准备 # 能够通过virt-manager为虚拟机添加CPU # 能够通过virsh命令为虚拟机添加CPU CPU热添加&#xff0c;不需要重新启动虚拟机。这个是CPU的热添加。 virsh list virsh dominfo centos7-1lscpuvish s…

软件测试中的网络问题

1.背景 在测试过程中&#xff0c;出现的问题&#xff0c;除了代码问题&#xff0c;还有很多的网络问题&#xff0c;所以需要了解网络知识&#xff0c;这样能发现网络问题&#xff0c;尽快解决就能提高效率。 2.计算机网络体系结构 OSI七层模型&#xff1a;物理层&#xff0c…

【链表】leetcode24. 两两交换链表中的节点(C/C++/Java/Js)

leetcode24. 两两交换链表中的节点1 题目2 思路3 代码3.1 C版本&#xff08;递归迭代&#xff09;3.2 C版本&#xff08;递归迭代&#xff09;3.3 Java版本&#xff08;递归迭代&#xff09;3.4 JavaScript版本4 总结1 题目 题源链接 给你一个链表&#xff0c;两两交换其中相…

Deep Learning for Image Super-resolution:A Survey

Abstract图像超分辨率技术是计算机视觉中提高图像和视频分辨率的一类重要的图像处理技术。近年来&#xff0c;深度学习技术在图像超分辨率方面取得了显著进展。本文旨在对基于深度学习的图像超分辨率研究进展进行综述。一般而言&#xff0c;我们可以将现有的SR技术研究大致分为…

C++设计模式:原型模式(详解+实现案例)

文章目录原型模式使用场景实现步骤案例一案例二优缺点原型模式 原型模式&#xff1a; 用原型实例指定创建对象的种类&#xff0c;并通过拷贝这些原型创建新的对象&#xff0c;简单理解就是“克隆指定对象” 使用场景 某些结构复杂的对象的创建工作中由于需求的变化&#xff…

QT之OpenGL光照

QT之OpenGL光照1. 冯氏光照模型概述1.1 环境光照1.2 漫反射光照1.2.1 法向量1.3 镜面光照1.4 冯氏光照公式1.5 着色器demo2. 材质2.1 demo3. 光照贴图3.1 demo4. 投光物4.1 平行光4.1.1 平行光Demo4.2 点光源4.2.1 衰减4.2.1 点光源Demo4.3 聚光4.3.1 聚光Demo4.3.2 平滑/软化边…

Ajax工作原理

Ajax工作原理 HTTP协议原理与ajax工作原理 1.什么HTTP协议 协议指的是规定浏览器跟服务器交互的数据格式 2.浏览器请求 必须是&#xff1a;请求报文 3.服务器响应 必须是&#xff1a;响应报文 4.请求报文与响应报文的数据格式如下 1.请求报文 (1)请求行 &#xff1a; 包含请…

TP4054国产替代DP4054 500mA 线性锂电充电芯片

DP4054 是一款完整的采用恒定电流/恒定电压单节锂离子电池充电管理芯片。其SOT小封装和较少的外部元件数目使其成为便携式应用的理想器件&#xff0c;DP4054 可以适合USB 电源和适配器电源工作。由于采用了内部PMOSFET 架构&#xff0c;加上防倒充电路&#xff0c;所以不需要外…

Msf后渗透测试阶段

● 已经获得目标系统控制权后扩大战果 ○ 提权 ○ 信息收集 ○ 渗透内网 ○ 永久后门 ● 基于已有session扩大战果 msfvenom -a x86 --platform windows -p windows/meterpreter/reverse_tcp LHOST1.1.1.1 LPORT4444 -b "\x00"-e x86/shikata_ga_nai -f exe -o 1.ex…

重发布-路由策略实验2(1.8)

目标&#xff1a; 1、首先对每个路由器进行接口ip的配置 r1&#xff1a; [r1]interface GigabitEthernet 0/0/0 [r1-GigabitEthernet0/0/0]ip add 12.1.1.1 24 [r1-GigabitEthernet0/0/0]int gi 0/0/1 [r1-GigabitEthernet0/0/1]ip add 13.1.1.1 24 [r1-GigabitEthernet0/0/1…

LinkedList与单向链表(二)(双向链表)

1.ListedList的模拟实现package Demo1;/*** Describe:双向链表的简单模拟实现* User:lenovo* Date:2023-01-08* Time:11:20*/ class Node {int val;Node prev;Node next;public Node(int val) {this.val val;}public Node() {} } public class MyLinkedList {Node first;Node …

Linux--权限

一、目录权限 文件的权限描述符&#xff0c;由10个字母组成 如下图所示&#xff0c;可以按照1-3-3-3的结构划分&#xff0c;用rwx表示拥有权限&#xff0c;r代表read&#xff08;可读&#xff09;&#xff0c;w代表write&#xff08;可写&#xff09;&#xff0c;x代表execut…

Python高阶技巧(十二)

python学习之旅(十二) &#x1f44d;查看更多可以关注查看首页或点击下方专栏目录 一.闭包 可以保存函数内变量&#xff0c;不会随着函数调用完而销毁 (1) 基本定义 在函数嵌套的前提下&#xff0c;内部函数使用了外部函数的变量&#xff0c;并且外部函数返回了内部函数&#x…

万字长文,带你从0到1的了解商业智能BI

借助互联网技术的发展&#xff0c;每天我们都会接触到大量的信息&#xff0c;信息的增长速度可以说是海啸级的。在这样一个信息爆炸的时代&#xff0c;掌握怎样利用数据就相当于掌握了一项生存技能&#xff0c;很多可以发掘并充分利用数据的企业会发现自己远远领先于竞争对手。…