【Python Onramp】5.利用Pyecharts进行可视化:综合应用

系列文章目录

【Python Onramp】 0. 卷首语

上一篇:【Python Onramp】4. Python文本分析(2)分章节统计、人物共现和pyecharts可视化
下一篇:【Python Onramp】6.一篇文章了解web开发要点:用Python开发简易的网页端成绩查询系统

项目描述

实现样例见:https://honour-van.github.io/cs50/index.html#2nd

这一节相对简单,因为pyecharts给我们提供了异常便利的对外接口。
同时这个项目具有一定的开放性,可以自行选定主题完成几个可视化任务即可。如下是我在课程中完成的几个任务:

任务1: 词频展示,利用词云和柱形图等

在这里插入图片描述可以实现在tab结构中,相当于是另一个组合图表的练习,如下:在这里插入图片描述

任务2:地理连线图

在这里插入图片描述

任务3:世界地图

在这里插入图片描述

任务4:组合图表

在这里插入图片描述

语法总览

语法点1:PyEcharts与面向对象

可以查看官方给出的示例:https://pyecharts.org/#/zh-cn/demo_data

Apache ECharts是一个基于 JavaScript 的开源可视化图表库。版式精美简约,具有可交互性。
Pyecharts是Echarts的一个python第三方库。相比Python的其他可视化工具,PyEcharts在数据展示方面具有非常显著的优越性。

小规模的Pyecharts的通用开发模式,就是将需要使用的数据替换到样例中。
但这要求对于Pyecharts对象有一定的了解,下面我们来做一个简单的说明。

语法点2:PyEcharts对象的一般结构与链式调用

PyEcharts的简约之美,很大程度上都来源于其将数据通道和属性通道分开。

数据的操控建立在Python的基础上,有大量高效、完善的数据结构可以使用,包括但不限于内置的list、dict以及pandas.dataframe等等。

而属性通道沿用了Echarts中的options结构,进行了良好的封装。

生成一个图表,可以使用链式调用的结构,而完全不必生成任何中间变量。比如我们要生成一个柱形图,可以使用这样的代码:

(
    Bar(
        init_opts=opts.InitOpts(
            theme=ThemeType.DARK
        )
    ) 
    .add_xaxis(xdata)
    .add_yaxis("词频", ydata, color='darkblue')
    .set_global_opts(
        title_opts=opts.TitleOpts(
            title="词频统计",
            subtitle="以红楼梦为例"),
        toolbox_opts=opts.ToolboxOpts(),
        xaxis_opts=opts.AxisOpts(
            # is_show=False,
            axislabel_opts={"rotate":45}
        ),
    )
    .render()
)

其中

    Bar(
        init_opts=opts.InitOpts(
            theme=ThemeType.DARK
        )
    ) 

的部分相当于构造函数,属性设定沿用了JavaScript风格echarts的options方法。

    .add_xaxis(xdata)
    .add_yaxis("词频", ydata, color='darkblue')

是数据通道,分别将横坐标和纵坐标输入对象。

.set_global_opts(
    title_opts=opts.TitleOpts(
        title="词频统计",
        subtitle="以红楼梦为例"),
    toolbox_opts=opts.ToolboxOpts(),
    xaxis_opts=opts.AxisOpts(
        # is_show=False,
        axislabel_opts={"rotate":45}
    ),
)

是全局属性设定,具体的操作都可以在pyecharts的官网中查到。尽管我个人也往往倾向于找一找教程。

render函数是渲染为html文件的函数,可以为目标文件设定文件名,默认为’render.html’,另外在jupyter中可以用render_notebook函数将其直接生成在notebook中,也很好。

上面的版本中添加了toolbox选项,如果做的折线图数据过多的话,也可以尝试使用datazoom,如同我们在上一节的分节分析中实现的一样。

具体实现

任务1:词频分析

利用三种图形进行词频可视化

基于之前在文本分析中得到的结果,我们这里利用柱形图、词云图和象形柱状图进行数据可视化。

柱形图的绘制相对简单,要点是要选取合适的绘制范围,基于pyecharts,之后的操作都非常简单

首先绘制的图片过小,
在这里插入图片描述
所以我们扩大了词频范围,但随后发现范围过大,致使整个图片铺满,出现了显著的长尾海洋+有效数据孤岛。

在这里插入图片描述
最终我们选用了前100个数据进行词云绘图。

另外补充了象形柱状图,同样用于表示量的关系。

"""
@file: wordfreq.py
@author: Honour Van PKU EE
@description: 
    可视化作业1:利用三种图形进行词频可视化
    基于之前在文本分析中得到的结果,
    我们这里利用柱形图、词云图和象形柱状图进行数据可视化
"""

from pyecharts.charts import WordCloud, Bar, PictorialBar, Tab
from pyecharts.globals import SymbolType, ThemeType
import pyecharts.options as opts
import os

# -------从文件中读出人物词频------------------
src_filename = './assets/红楼梦词频.csv'
# 格式:人物,出现次数

with open(src_filename, 'r', encoding='utf-8') as src_file:
    line_list = src_file.readlines()  # 返回列表,文件中的一行是一个元素

# print(line_list)  # 检查读入数据的情况

# 将读入的每行数据拆分成元组
wordfreq_list = []  # 用于保存元组(人物姓名,出现次数)
for line in line_list:
    line = line.strip()  # 删除'\n'
    line_split = line.split(',')  # 以逗号作为标志,把字符串切分成词,存在列表中
    wordfreq_list.append((line_split[0], line_split[1]))

# print(wordfreq_list)  # 检查读入数据的情况

del wordfreq_list[0]  # 删除csv文件中的标题行
# -------从文件中读出人物词频完成------------------

# -------绘制词频的词云图----------------------
wordcloud = (
    WordCloud(
        init_opts=opts.InitOpts(
            theme=ThemeType.INFOGRAPHIC,
            page_title='红楼梦词云'
        )
    )
    .add(series_name="词频分析",
         data_pair=wordfreq_list[:200],
         word_size_range=[6, 66],
         shape='roundRect',

         )
    .set_global_opts(
        title_opts=opts.TitleOpts(
            title="词频分析",
            subtitle="以红楼梦为例",
            title_textstyle_opts=opts.TextStyleOpts(font_size=23)
        ),
        tooltip_opts=opts.TooltipOpts(is_show=True),
        toolbox_opts=opts.ToolboxOpts()
    )
)

# --------绘制词云图完成----------------------

# -------绘制词频的柱形图----------------------
xdata = [x[0] for x in wordfreq_list[:16]]
ydata = [y[1] for y in wordfreq_list[:16]]
bar = (
    Bar(
        init_opts=opts.InitOpts(
            theme=ThemeType.DARK
        )
    )
    .add_xaxis(xdata)
    .add_yaxis("词频", ydata, color='darkblue')
    .set_global_opts(
        title_opts=opts.TitleOpts(
            title="词频统计",
            subtitle="以红楼梦为例"),
        toolbox_opts=opts.ToolboxOpts(),
        xaxis_opts=opts.AxisOpts(
            # is_show=False,
            axislabel_opts={"rotate":45}
        ),
    )
)

# --------绘制柱形图完成----------------------

# --------绘制象形柱状图----------------------
pictorialbar = (
    PictorialBar(
        init_opts=opts.InitOpts(
            theme=ThemeType.WONDERLAND,
            page_title='红楼梦词频象形柱状图'
        )
    )
    .add_xaxis(xdata)
    .add_yaxis(
        "",
        ydata,
        label_opts=opts.LabelOpts(is_show=False),
        symbol_size=18,
        symbol_repeat="fixed",
        symbol_offset=[0, 0],
        is_symbol_clip=True,
        symbol=SymbolType.ROUND_RECT,
        color='darkblue'
    )
    .reversal_axis()
    .set_global_opts(
        title_opts=opts.TitleOpts(
            title="词频象形柱图",
            subtitle="以红楼梦为例"
        ),
        yaxis_opts=opts.AxisOpts(
            axistick_opts=opts.AxisTickOpts(is_show=False),
            axisline_opts=opts.AxisLineOpts(
                linestyle_opts=opts.LineStyleOpts(opacity=0)
            ),
        ),
        toolbox_opts=opts.ToolboxOpts(),
    )
)
# --------象形柱形图完成----------------------

# --------将几个图片绘制到同一个tab中---------
(
    Tab(page_title='红楼梦词频分析')
    .add(bar, "词频柱形图")
    .add(wordcloud, "词云图")
    .add(pictorialbar, "象形柱状图")
    .render('./out/wordfreq.html')
)
# --------tab绘制完成-----------------------

# 尝试直接利用chrome打开html文件
try:
    os.system('start chrome ./out/wordfreq.html')
except:
    print("Chrome Error")

利用pycharts.charts.Tab组织三个图样。如下:
在这里插入图片描述
html交互图表可见链接https://honour-van.github.io/cs50/index.html#hw21

任务2:地理连线图

利用pyecharts实现地图以及连线。由于课程需要,还需要给其编制一个故事背景。

这个地图使用地理散点和连线两种基本图样描述了寒暑假旅行的大体路线。
这里的旅行示意图是按照省内GDP排名随机安插的整数值,并没有实在的意义

  
"""
@file: geolink.py
@author: Honour Van PKU EE
@description: 
    可视化作业2:地理坐标系图练习
    利用pyecharts实现地图以及连线。
    这个地图使用地理散点和连线两种基本图样描述了寒暑假旅行示意图
故事背景:
    一个漫长的寒假,内卷自学新知识累的半死的小h想在省内走一走,
    从西安出发,ta本来想沿着全省来一次环省旅行,但是发现越往北走,气候越不好,所以ta打算尽可能往南走
    骑车或者轮滑都令人困倦。由于他的精力是有限的,他必须要用最低的成本走完越多的路程。
    每一条路径的消耗都已经以list的形式给出,没有在图中给出。在一定的城市可以获得一定的补充。
    由于你刚刚学会python,尝试编写一个程序帮他找到最佳的路径遍历陕南。注意:Floyd算法可能会超时。
    (手动狗头:这个程序和算法题毫无关联
"""

import os
from pyecharts import options as opts
from pyecharts.charts import Geo
from pyecharts.faker import Faker
from pyecharts.globals import ChartType, SymbolType, ThemeType

# 地理图的链式调用
(
    Geo(
        init_opts=opts.InitOpts(
            theme=ThemeType.INFOGRAPHIC,
            bg_color='white',
            page_title='Honour的省内旅行路线'
        )
    ) # 确定主题、标签页名称等
    .add_schema(
        maptype="陕西",
        itemstyle_opts=opts.ItemStyleOpts(
            color="lightblue", border_color="black"),
    ) # 确定地图基本属性
    .add(
        "",
        [("西安", 100), ("汉中", 40), ("榆林", 30), ("延安", 50),
         ("安康", 20), ("宝鸡", 80), ("铜川", 90), ("咸阳", 70),
         ("渭南", 60), ("商洛", 10)],
        # 城市标记参数是由元组项组成的列表
        type_=ChartType.EFFECT_SCATTER,
        color="white",
    ) # 在省内各城市加点,按照产值排序
    .add(
        "暑假",
        [("汉中", "安康"), ("安康", "商洛"), ("商洛", "西安"), ("西安", "宝鸡"), ("宝鸡", "汉中")],
        # 连线方向参数是由元组项组成的列表
        type_=ChartType.LINES,
        effect_opts=opts.EffectOpts(
            symbol=SymbolType.ARROW, symbol_size=6, color="white"
        ),
        linestyle_opts=opts.LineStyleOpts(curve=0.2),
        # curve为正时,曲线是凸的;为负时,曲线是凹的
    )
    .add(
        "寒假(骑行)",
        [("西安", "渭南"), ("渭南", "铜川"), ("铜川", "延安"), ("延安", "榆林")],
        # 连线方向参数是由元组项组成的列表
        type_=ChartType.LINES,
        effect_opts=opts.EffectOpts(
            symbol=SymbolType.ARROW, symbol_size=6, color="white"
        ),
        linestyle_opts=opts.LineStyleOpts(curve=-0.2),
        # curve为正时,曲线是凸的;为负时,曲线是凹的
    )
    .add(
        "乘火车",
        [("榆林", "西安")],
        # 连线方向参数是由元组项组成的列表
        type_=ChartType.LINES,
        effect_opts=opts.EffectOpts(
            symbol=SymbolType.TRIANGLE, symbol_size=6, color="white"
        ),
        linestyle_opts=opts.LineStyleOpts(curve=-0.1),
        # curve为正时,曲线是凸的;为负时,曲线是凹的
    )
    .set_series_opts(label_opts=opts.LabelOpts(is_show=False))
    .set_global_opts(
        visualmap_opts=opts.VisualMapOpts(range_color=Faker.visual_color),
        title_opts=opts.TitleOpts(
            title="省内旅行计划",
            subtitle="骑行或轮滑"),
        toolbox_opts=opts.ToolboxOpts(),
    )
    .render("./out/geo_shaanxi.html")
)

# 尝试直接利用chrome打开html文件
try:
    os.system("start chrome .\out\geo_shaanxi.html")
except:
    print("Chrome Error")

实现方面没有特别困难的点,在示例代码的基础上,我们增加了特效和主题的更换,使渲染效果大大优化。如下

html交互图表可见链接https://honour-van.github.io/cs50/index.html#hw22

任务3:中国地图、世界地图

利用这个地图,我们将2019年的世界GDP前十作一展示。

通过GDP Top10和GDPPC(pre capita, 人均国内生产总值)的对比,我们可以看出,世界上的GDP大国和GDPPC强国几乎正交。人均GDP高的国家往往是早期的发达国家。

与之相比,GDP大国往往却出现在当今的发展中国家,以中国为代表的一系列国家,以庞大的人口体量 带来了经济发展空间。

下一个任务我们将基于时间尺度进行。

我们将GDP数据存入json文件中,随后读取出来,并利用pyecharts相关组件进行地图绘制。

"""
@file: map.py
@author: Honour Van PKU EE
@description: 
    可视化作业3:地图可视化练习
    我们将GDP数据存入json文件中,随后读取出来,并利用pyecharts相关组件进行地图绘制。
故事背景:
    这个地图主题略微正经,我们将2019年的世界GDP前十作一展示。
    通过GDP Top10和GDPPC(pre capita, 人均国内生产总值)的对比,
    我们可以看出,世界上的GDP大国和GDPPC强国几乎正交。人均GDP高的国家往往是早期的发达国家。
    与之相比,GDP大国往往却出现在当今的发展中国家,以中国为代表的一系列国家,以庞大的人口体量
    带来了经济发展空间。下一个任务我们将基于时间尺度进行。
"""

import os
from pyecharts.faker import Faker
from pyecharts import options as opts
from pyecharts.charts import Map, Page
from pyecharts.globals import ThemeType
import json

from pyecharts.options.charts_options import PageLayoutOpts

# 从json文件导入GDP和GDPPC数据
with open('./assets/GDP.json', 'r', encoding='utf-8') as f:
    gdp_dict = json.load(f)
    gdp_list = [[country[0], country[1]] for country in gdp_dict.items()]
with open('./assets/GDP_ave.json', 'r', encoding='utf-8') as f:
    gdppc_dict = json.load(f)
    gdppc_list = [[country[0], country[1]] for country in gdppc_dict.items()]

# 绘制GDP top10 地图
m1 = (
    Map()
    .add("GDP/$1T",
         gdp_list,
         maptype="world",
         is_map_symbol_show=False,  # 不描点
         )
    .set_series_opts(label_opts=opts.LabelOpts(is_show=False))
    .set_global_opts(
        title_opts=opts.TitleOpts(title="GDP Top10"),
        visualmap_opts=opts.VisualMapOpts(max_=22, is_piecewise=True),
    )
)

# 绘制GDP per capita top10
m2 = (
    Map()
    .add("GDPPC/$10,000",
         gdppc_list,
         maptype="world",
         is_map_symbol_show=False,  # 不描点
         )
    .set_series_opts(label_opts=opts.LabelOpts(is_show=False))
    .set_global_opts(
        title_opts=opts.TitleOpts(title="GDPPC Top10"),
        visualmap_opts=opts.VisualMapOpts(min_=5, max_=12),  # 调整动态范围
    )
)

# 将两个图样放入一个page当中
(
    Page(
        page_title="GDP top10",
    )
    .add(m1, m2)
    .render('./out/map_world_gdp.html')
)

# 尝试直接利用chrome打开html文件
try:
    os.system("start chrome ./out/map_world_gdp.html")
except:
    print("Chrome Error")

部分统计数据将中国澳门分开计算平均,这样的结果是,中国澳门也位列前十;但我们始终认为,港澳台是中国不可分割的一部分,所以统计数据中无澳门,采用至11名。

效果如下:
在这里插入图片描述

任务4:组合图表

故事背景:

我们选取了两个主题进行分析,试图从一个侧面反应疫情对世界经济的影响。

一个是疫情的感染率,来表征一个国家受到疫情的冲击程度。

另一方面我们选用了GDP的减量进行展示。

两相对比,我们发现,除美国外,其他国家大都有如下的特征:

一类国家采用对疫情的强硬措施,以中国为代表,采用了相对强的休克性疗法,这样的结果是,有效地控制疫情,但是GDP减量较大
一类国家采用保持经济的措施,以欧洲部分国家为代表,一定程度上保持了经济稳定运行,但是不强力阻滞疫情发展,导致了较为严重的疫情
从这个比较当中我们不难看出中国为了保障人民的生命健康权益,所做出的重大牺牲。

代码实现

这个在任务3的基础上加入了时间的考虑,但由于我们选取的事件是疫情相关, 所以时间线并不长,所以我们没有使用timeline结构。

共绘制如下四幅图,利用overlap和page绘制在同一个网页中。

  1. GDP前十的两年GDP的并列柱形图
  2. GDP前十国家的感染率
  3. 世界疫情统计图
  4. 世界GDP减量热图
  
"""
@file: multi.py
@author: Honour Van PKU EE
@description: 
    可视化作业4:组合图表练习
    我们将GDP数据存入json文件中,随后读取出来,并利用pyecharts相关组件进行地图绘制。
故事背景:
    这个在任务3的基础上加入了时间的考虑,但由于我们选取的事件是疫情相关,
    所以时间线并不长,所以我们没有使用timeline结构。
    我们选取了两个主题进行分析,一个是疫情的感染率,来表征一个国家受到疫情的冲击程度。
    另一方面我们选用了GDP的减量进行展示。两相对比,我们发现,除美国外,其他国家大都有如下的特征:
    1. 一类国家采用对疫情的强硬措施,以中国为代表,采用了相对强的休克性疗法,这样的结果是,有效地控制疫情,但是GDP减量较大
    2. 一类国家采用保持经济的措施,以欧洲部分国家家为代表,一定程度上保持了经济稳定运行,但是不强力阻滞疫情发展,导致了较为严重的疫情
    从这个比较当中我们不难看出中国为了保障人民的生命健康权益,所做出的重大牺牲。
"""

from os import name, system
from pyecharts import options as opts
from pyecharts.charts.chart import Chart
from pyecharts.globals import ChartType, SymbolType
from pyecharts.charts import Bar, EffectScatter, Page, Geo, Tab
import json

# --------------------导入GDP和疫情数据----------------
with open("./assets/GDP.json", 'r', encoding='utf-8') as f:
    gdp_dict = json.load(f)
    x_data = list(gdp_dict.keys())
    y1_data = list(gdp_dict.values())
with open("./assets/GDP_2020.json", 'r', encoding='utf-8') as f:
    gdp_dict = json.load(f)
    y2_data = list(gdp_dict.values())
# -------------------------------------------------------



# ==================制作柱形和涟漪散点图的overlap图式============
# -------------------生成并列柱形图--------------------------
bar = (
    Bar()
    .add_xaxis(
        x_data,
    )
    .add_yaxis(
        "GDP Top10 in 2019",
        y1_data,
        itemstyle_opts=opts.ItemStyleOpts(color='#5793ff'), #注意这里要用ItemStyleOpts
        yaxis_index=0,
    )
    .add_yaxis(
        "GDP Top10 in 2020",
        y2_data,
        yaxis_index=0,
        itemstyle_opts=opts.ItemStyleOpts(color='#d14a61'),
    )
    .extend_axis(
        yaxis=opts.AxisOpts(
            type_="value",
            name="COVID-19 rate",
            min_=0,
            max_=10,
            position="left",
            axisline_opts=opts.AxisLineOpts(
                linestyle_opts=opts.LineStyleOpts(color="#3ba25f")
            ),
            axislabel_opts=opts.LabelOpts(formatter="{value}%"),
            splitline_opts=opts.SplitLineOpts(
                is_show=True, linestyle_opts=opts.LineStyleOpts(opacity=1)
            ),
        )
    )
    .set_global_opts(
        yaxis_opts=opts.AxisOpts(
            name="GDP",
            min_=0,
            max_=22,
            position="right",
            offset=0,  # Y 轴相对于默认位置的偏移,在相同的 position 上有多个 Y 轴的时候有用
            axisline_opts=opts.AxisLineOpts(
                linestyle_opts=opts.LineStyleOpts(color="#675bba")
            ),
            axislabel_opts={
                "formatter": "${value} trillion",
                "rotate": -10,
            }
        ),
        xaxis_opts=opts.AxisOpts(
            # name="country",
            axislabel_opts={"rotate": 30}
        ),
        title_opts=opts.TitleOpts(title="GDP Top10"),
        tooltip_opts=opts.TooltipOpts(
            trigger="axis", axis_pointer_type="cross"),  # 提示框配置项
    )
)

# -----------------生成涟漪散点图---------------------------------------
with open("./assets/pandemic.json", 'r', encoding='utf-8') as f:
    cvd_dict = json.load(f)
    y3_data = [("%.1f"%(100*x['confirmed']/x['population'])) for x in cvd_dict.values()]
c = (
    EffectScatter()
    .add_xaxis(
        x_data,
    )
    .add_yaxis(
        "COVID-19 rate",
        y3_data,
        yaxis_index=1,
        itemstyle_opts=opts.ItemStyleOpts(color='#3ba25f'),
        symbol=SymbolType.DIAMOND # 更换特效类型
    )
)

bar.overlap(c) # 进行图片堆叠

# ===========================GDP和疫情状况图表完成====================

# =========================构建直观的地图展示========================
y4_data = [list((x[0], ("%1.f"%((float(x[1])-float(x[2]))*45+5)))) for x in zip(x_data, y1_data, y2_data)]
geo = (
    Geo()
    .add_schema(maptype='world')
    .add_coordinate_json(json_file='./assets/world_country.json')
    .add(
        "GDP decrement",
        y4_data,
        type_=ChartType.EFFECT_SCATTER,
        is_selected=False,
        symbol=SymbolType.ROUND_RECT
    )
    .add(
        "Pandemic",
        [list((x[0], ("%.1f"%(100*10*x[1]['confirmed']/x[1]['population'])))) for x in cvd_dict.items()], 
        type_=ChartType.EFFECT_SCATTER,
        symbol=SymbolType.DIAMOND
    )
    .set_series_opts(label_opts=opts.LabelOpts(is_show=False))
    .set_global_opts(
        visualmap_opts=opts.VisualMapOpts(),
        title_opts=opts.TitleOpts(title="GDP decrement in these countries"),
    )
)
# =========================地图展示完成========================

# =======================图片组合=============================
page = (
    Page(
        page_title = "COVID-19 effecting GDP Top10",
        layout=Page.SimplePageLayout
    )
    .add(
        bar,
        geo,
    )
    .render("./out/pandemic-gdp.html")
)

# 尝试直接利用chrome打开html文件
try:
    system("start chrome ./out/pandemic-gdp.html")
except:
    print("Chrome Error")

效果如下:
在这里插入图片描述

红楼梦分节

上一节以及https://honour-van.github.io/cs50/plus1.html

总结

一个思想:数据通道和属性通道分离,进行面向对象封装。这种低耦合的结构很利于我们在只有代码示例的情况下进行再开发
语法要点:Pyecharts使用链式调用,主要选项都在Options以类键值对的方式配置。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值