【Python+Flask+Echarts】可视化练习题 ---- 疫情数据热力图



一、热力图介绍

  • 热力图,有时也称之为交叉填充表。该图形最典型的用法就是实现列联表的可视化,即通过图形的方式展现两个离散变量之间的组合关系
  • 类型:关系型数据的可视化
  • 特点:热力图体现了两个离散变量之间的组合关系
  • Echarts官方实例:
    在这里插入图片描述
    返回顶部

二、案例

① 分析提取需求

  • 数据集
    在这里插入图片描述
    数据主要字段:日期、省份、城市、新增确诊、新增出院、新增死亡、消息来源、来源1,来源2,来源3。
    在这里插入图片描述
  • 1.通过对数据集的观察,城市字段缺失较难恢复,本想通过来源提取城市信息,但是在来源字段中内容分布不规则,无法有效提取。所以只能对这部分空值记录删除
  • 2.通过热力图的定义,以及官方实例得出最关键的是:热力图热力图体现了两个离散变量之间的组合关系。也就是包含有两个变量,以及最后的组合结果,可以反映出哪些信息内容。在这里,我们不妨设想:展现出某一省份与其城市之间组合可以得到什么。比如:某一省份的不同城市新增确诊人数/新增出院人数…
  • 3.问题又来了,只用某一省份与其城市,绘制出来热力图就是一张横条,未免显得太尴尬。可以继续扩充。同时加上日期,但是日期按照天数太多(若只统计某一个月还可以),这里就暂定月份。
  • 4.综上所述,需求基本如下:绘制某省各市在已知的每个月新增确诊数热力图
  • 5.基本模板
    在这里插入图片描述

返回顶部


② 代码实现

▶读取数据

import pandas as pd
from flask import Flask,render_template

# 日期、省份、城市、新增确诊、新增出院、新增死亡、消息来源、来源1,来源2,来源3
columns = ['日期','省份','城市','新增确诊','新增出院','新增死亡','消息来源','来源1','来源2','来源3']
data = pd.read_csv("G:\Projects\pycharmeProject-C\Flask\dataset\yiqing.csv",names=columns,index_col=False)

在这里插入图片描述

数据读取的时候通过names参数添加列名index_col参数可以强制读取时不使用第一列作为索引,否则会出现下图二错位的现象。

在这里插入图片描述
在这里插入图片描述

返回顶部


▶简单清洗

# 简单清洗数据
# 去重
print(data.shape)
print(data.loc[data.duplicated()])
print(data.drop_duplicates().shape)
# 去空 --- 提取城市不为空的记录
print(data.isnull().sum())
data = data.loc[~data['城市'].isnull()]
# 查看数据类型
print(data.dtypes)

在这里插入图片描述
返回顶部


▶提取需求信息

# 提取武汉信息
hb_data = data.loc[data['省份']=='湖北',['城市','日期','新增确诊']]

在这里插入图片描述

# 提取月份信息
hb_data['日期'] = [str(x)[0:2] for x in hb_data['日期']]

在这里插入图片描述

# 分组聚合统计
result = hb_data.groupby(['城市','日期']).agg(count=('新增确诊','count')).reset_index()
print(result.shape[0]) # 58
citys = result['城市'].unique().tolist() # x轴标签
dates = result['日期'].unique().tolist() # y轴标签 
count = result['count'].values.tolist()

在这里插入图片描述
在这里插入图片描述

返回顶部


▶Flask部分

# 创建Flask对象
app = Flask(__name__)
# 视图
@app.route("/")
def index():
    city_list = city
    date_list = date
    # 关联html页面,传输数据
    return render_template("yiqing_heatmap.html",city = city_list,date = date_list)
if __name__ == '__main__':
    # 运行
    app.run(debug=True)

▶可视化部分

<!DOCTYPE html>
<html lang="en" style="height:100%">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body style="height:100%">
<div id="container" style="height:100%"></div>
<script type="text/javascript" src="../static/echarts.min.js"></script>
<script type="text/javascript">
    var dom = document.getElementById("container");
    var myecharts = echarts.init(dom);
    // 获取后端传输数据
    var city = [{% for item in city %}'{{ item }}',{% endfor %}];
    var date = [{% for item in date %}'{{ item }}',{% endfor %}];
    var data = [
        [0,0,8],[0,1,36],[0,2,21],[0,3,1],
        [1,0,8],[1,1,38],[1,2,23],[1,3,0],
        [2,0,6],[2,1,38],[2,2,18],[2,3,0],
        [3,0,0],[3,1,0],[3,2,0],[3,3,1],
        [4,0,7],[4,1,41],[4,2,18],[4,3,0],
        [5,0,8],[5,1,46],[5,2,26],[5,3,0],
        [6,0,4],[6,1,41],[6,2,25],[6,3,0],
        [7,0,7],[7,1,38],[7,2,17],[7,3,0],
        [8,0,24],[8,1,62],[8,2,38],[8,3,18],
        [9,0,5],[9,1,34],[9,2,19],[9,3,0],
        [10,0,4],[10,1,8],[10,2,5],[10,3,0],
        [11,0,10],[11,1,45],[11,2,24],[11,3,0],
        [12,0,10],[12,1,39],[12,2,25],[12,3,1],
        [13,0,6],[13,1,41],[13,2,21],[13,3,1],
        [14,0,6],[14,1,43],[14,2,30],[14,3,0],
        [15,0,7],[15,1,43],[15,2,24],[15,3,0],
        [16,0,10],[16,1,46],[16,2,20],[16,3,0],
        [17,0,6],[17,1,54],[17,2,26],[17,3,0],
    ]
    // 对data进行形态转变
    // 处理 0 值样式
    data = data.map(function (item) {
              return [item[0], item[1], item[2] || '-'];
           });
    var option = null;
    option = {
        tooltip:{
           position:'top'
        },
        grid:{
           height:'50%',
           top:'10%'
        },
        xAxis:{
            type:'category',
            data:city,
            // 划分区域是否显示,类似于绘制的网格
            splitArea:{
              show:true
            },
            boundaryGap:true,
            axisTick:{
               alignWithLabel:true
            },
            axisLabel: {
                interval:0,
                rotate:0
            }
        },
        yAxis:{
            type:'category',
            data:date,
            splitArea:{
               show:true
            }
        },
        visualMap:{
            min:0,
            max:70,
            calculable:true,
            orient:'horizontal',  //热力分布标签布局
            left:'center',
            bottom:'15%'
        },
        series:[
           {
              name:'新增确诊',
              type:'heatmap',
              data:data,
              label:{
                 show:true    // 每个item上的标签,对应的聚合值
              },
              emphasis:{      // 高亮显示,设置阴影
                 itemStyle:{
                     shadowBlur: 10,
                     shadowColor: 'rgba(0, 0, 0, 0.5)'
                 }
              }
           }
        ]
    };
    if(option && typeof option=='object'){
        myecharts.setOption(option);
    }
</script>
</body>
</html>

返回顶部


③ 效果展示

在这里插入图片描述
返回顶部


④ 代码优化

 var data = [
        [0,0,8],[0,1,36],[0,2,21],[0,3,1],
        [1,0,8],[1,1,38],[1,2,23],[1,3,0],
        [2,0,6],[2,1,38],[2,2,18],[2,3,0],
        [3,0,0],[3,1,0],[3,2,0],[3,3,1],
        [4,0,7],[4,1,41],[4,2,18],[4,3,0],
        [5,0,8],[5,1,46],[5,2,26],[5,3,0],
        [6,0,4],[6,1,41],[6,2,25],[6,3,0],
        [7,0,7],[7,1,38],[7,2,17],[7,3,0],
        [8,0,24],[8,1,62],[8,2,38],[8,3,18],
        [9,0,5],[9,1,34],[9,2,19],[9,3,0],
        [10,0,4],[10,1,8],[10,2,5],[10,3,0],
        [11,0,10],[11,1,45],[11,2,24],[11,3,0],
        [12,0,10],[12,1,39],[12,2,25],[12,3,1],
        [13,0,6],[13,1,41],[13,2,21],[13,3,1],
        [14,0,6],[14,1,43],[14,2,30],[14,3,0],
        [15,0,7],[15,1,43],[15,2,24],[15,3,0],
        [16,0,10],[16,1,46],[16,2,20],[16,3,0],
        [17,0,6],[17,1,54],[17,2,26],[17,3,0],
    ]

在上面的案例中,最后的data数据是本人纯手打,采用的是读取静态数据的方式,并且热力图数据以坐标的形式定位展现,实际上并没有通过前后端真实实现动态获取数据。

所以,进行了代码优化,能够实现在后端将数据处理成对应的形式。


▶ 如何优化?

在这里插入图片描述
通过上图的对比可以看出,通过静态数据(手动录入)时,对于原始数据进行了填补。在聚合统计后会发现每个城市并不是每个月都有统计结果的。以仙桃市、十堰市、咸宁市为例,仙桃市统计结果为4个月的,而两个市,存在4月数据缺失(默认为0),所以最后的时候本人进行了手动填充。

所以,优化的方向就明确了:在统计聚合之后,对结果进行填充优化

  • 这里有两种方案:

    方案一:对月进行分组,提取每月的城市信息,与全部城市信息比对,若有缺失,对该月聚合结果进行城市信息填充

    方案二:对城市进行分组,提取城市的不同月份信息,查看月份数量,若有缺失,对该城市聚合结果进行月份信息填充

由于这里月份信息数量少,最后合并较为简便,所以采用方案一

返回顶部


▶ 具体优化

# 优化
def add_all(citynames,month):
    # 按照日期分组
    date_grouped = result.loc[result['日期']== month].values.tolist()
    # 遍历提取每个月的城市信息
    date_city = []
    for item in date_grouped:
        date_city.append(item[0])
    # 对比全部城市信息,补全当前月份每个城市的信息
    # 排序返回
    for i in  citynames:
        if i not in date_city:
            date_grouped.append([i,month,0])
    return sorted(date_grouped,key=lambda x:x[0])
data_1 = add_all(citys,'1月')
data_2 = add_all(citys,'2月')
data_3 = add_all(citys,'3月')
data_4 = add_all(citys,'4月')

在这里插入图片描述
可以看到最后的填补效果,主要是4月的信息缺失比较多~
在这里插入图片描述
整个数据集填充完成之后,就是要将其转换成坐标的形式传输到前端.

['仙桃市', '1月', 8] ----> [0,0,8]
['十堰市', '1月', 8] ----> [1,0,8]
# 坐标转化
def trans_data(data,y):
    for i in range(len(data)):  // i代表x轴坐标值
        data[i][0] = i
        data[i][1] = y
    return data
ts_data_1 = trans_data(data_1,0)
ts_data_2 = trans_data(data_2,1)
ts_data_3 = trans_data(data_3,2)
ts_data_4 = trans_data(data_4,3)
# 合并结果集
data_1.extend(data_2)
data_3.extend(data_4)
data_1.extend(data_3)
list = data_1

在这里插入图片描述

app = Flask(__name__)
@app.route("/")
def index():
    city_list = citys
    date_list = dates
    # 添加list数据
    list_all = list
    return render_template("yiqing_heatmap.html",city = city_list,date = date_list,list = list_all)
if __name__ == '__main__':
    app.run(debug=True)
<!DOCTYPE html>
<html lang="en" style="height:100%">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body style="height:100%">
<div id="container" style="height:100%"></div>
<script type="text/javascript" src="../static/echarts.min.js"></script>
<script type="text/javascript">
    var dom = document.getElementById("container");
    var myecharts = echarts.init(dom);
    // 获取后端数据
    var city = [{% for item in city %}'{{ item }}',{% endfor %}];
    var date = [{% for item in date %}'{{ item }}',{% endfor %}];
    var list = [{% for item in list %}{{ item }},{% endfor %}];
    // 转变list,主要处理 0 值
    data = list.map(function (item) {
              return [item[0], item[1], item[2] || '-'];
           });
    var option = null;
    option = {
        tooltip:{
           position:'top'
        },
        grid:{
           height:'50%',
           top:'10%'
        },
        xAxis:{
            type:'category',
            data:city,
            splitArea:{
              show:true
            },
            boundaryGap:true,
            axisTick:{
               alignWithLabel:true
            },
            axisLabel: {
                interval:0,
                rotate:0
            }
        },
        yAxis:{
            type:'category',
            data:date,
            splitArea:{
               show:true
            }
        },
        visualMap:{
            min:0,
            max:70,
            calculable:true,
            orient:'horizontal',
            left:'center',
            bottom:'15%'
        },
        series:[
           {
              name:'新增确诊',
              type:'heatmap',
              data:data,
              label:{
                 show:true
              },
              emphasis:{
                 itemStyle:{
                     shadowBlur: 10,
                     shadowColor: 'rgba(0, 0, 0, 0.5)'
                 }
              }
           }
        ]
    };
    if(option && typeof option=='object'){
        myecharts.setOption(option);
    }
</script>
</body>
</html>

在这里插入图片描述

返回顶部


  • 1
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

骑着蜗牛ひ追导弹'

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值