数据可视化交互
目录
【实验目的】
【实验原理】
【实验环境】
【实验步骤】
一、安装 pyecharts
二、下载数据
三、实验任务
实验 1:AQI 横向对比条形图
代码说明:
运行结果:
实验 2:AQI 等级分布饼图
实验 3:多城市 AQI 对比仪表盘
实验 4:2D 地理 AQI 可视化
实验 5:3D 地理 AQI 可视化
【实验总结】
【实验目的】
- 了解数据可视化的一般原则
- 掌握数据可视化的分类
- 掌握数据可视化的常见技术
- 本次实验是对全国的空气质量进行可视化分析并进行数据统计技术对比
【实验原理】
设计可视化系统或选择交互方式的时候,除了能够完成任务本身之外,还要遵循一些基本的原则。例如,交互的延时性需要在用户可以接受的范围之内,并有效控制用户交互的成本。这些基本原则对交互的效果起着至关重要的作用富。
另外,交互的技术有很多种,本次实验是对文本进行可视化生成词云图片与传统的统计技术对比。
交互的原则、交互的分类以及常见的交互技术,尤其是几种常见的交互技术,只有熟练掌握并使用恰当,才可能设计出用户体验良好的可视化应用。尽管交互的技术有很多种,但交互技术本身并无优劣之分,选择哪种交互技术的依据是具体的场景和应用需求。
【实验环境】
Python:v3.11
【实验步骤】
一、安装 pyecharts
pip install pyecharts
二、下载数据
下载数据文件(data.txt),该文件表示了一些城市某天的空气质量指数(AQI),请完成如下实验。
三、实验任务
实验 1:AQI 横向对比条形图
任务:使用 Pyecharts 绘制各城市 AQI 值的横向条形图,要求:
1. 按 AQI 从高到低排序
2. 添加全局配置项:标题为“城市 AQI 对比”,坐标轴名称分别为“AQI 指数”和“城市”
3. 使 MarkLine 标记 AQI 均值线(参考值:80),并设置不同颜色区分高于/低于均值的城市
from pyecharts import options as opts
from pyecharts.charts import Bar
from pyecharts.commons.utils import JsCode
import re# 解析文件内容
data_str = '''
"海门", 9, "鄂尔多斯", 12, "招远", 12, "舟山", 12, "齐齐哈尔", 14, "盐城", 15, "赤峰", 16, "青岛", 18, "乳山", 18, "金昌", 19, "泉州", 21, "莱西", 21, "日照", 21, "胶南", 22, "南通", 23, "拉萨", 140, "云浮", 24, "梅州", 25, "文登", 25, "上海", 25, "攀枝花", 25, "威海", 25, "承德", 25, "厦门", 26, "汕尾", 26, "潮州", 26, "丹东", 27, "太仓", 27, "曲靖", 27, "烟台", 28, "福州", 29, "瓦房店", 30, "即墨", 30, "抚顺", 31, "玉溪", 31, "张家口", 31, "阳泉", 31, "莱州", 32, "湖州", 32, "汕头", 32, "昆山", 33, "宁波", 33, "湛江", 33, "揭阳", 34, "荣成", 34, "连云港", 35, "葫芦岛", 35, "常熟", 36, "东莞", 36, "河源", 36, "淮安", 36, "泰州", 36, "南宁", 37, "营口", 37, "惠州", 37, "江阴", 37, "蓬莱", 37, "韶关", 38, "嘉峪关", 38, "广州", 38, "延安", 38, "太原", 39, "清远", 39, "中山", 39, "昆明", 39, "寿光", 40, "盘锦", 40, "长治", 41, "深圳", 41, "珠海", 42, "宿迁", 43, "咸阳", 43, "铜川", 44, "平度", 44, "佛山", 44, "海口", 44, "江门", 45, "章丘", 45, "肇庆", 46, "大连", 47, "临汾", 47, "吴江", 47, "石嘴山", 49, "沈阳", 50, "苏州", 50, "茂名", 50, "嘉兴", 51, "长春", 51, "胶州", 52, "银川", 52, "张家港", 52, "三门峡", 53, "锦州", 54, "南昌", 54, "柳州", 54, "三亚", 54, "自贡", 56, "吉林", 56, "阳江", 57, "泸州", 57, "西宁", 57, "宜宾", 58, "呼和浩特", 58, "成都", 58, "大同", 58, "镇江", 59, "桂林", 59, "张家界", 59, "宜兴", 59, "北海", 60, "西安", 61, "金坛", 62, "东营", 62, "牡丹江", 63, "遵义", 63, "绍兴", 63, "扬州", 64, "常州", 64, "潍坊", 65, "重庆", 66, "台州", 67, "南京", 67, "滨州", 70, "贵阳", 71, "无锡", 71, "本溪", 71, "克拉玛依", 72, "渭南", 72, "马鞍山", 72, "宝鸡", 72, "焦作", 75, "句容", 75, "北京", 79, "徐州", 79, "衡水", 80, "包头", 80, "绵阳", 80, "乌鲁木齐", 84, "枣庄", 84, "杭州", 84, "淄博", 85, "鞍山", 86, "溧阳", 86, "库尔勒", 86, "安阳", 90, "开封", 90, "济南", 92, "德阳", 93, "温州", 95, "九江", 96, "澳门", 98, "临安", 99, "兰州", 99, "沧州", 100, "临沂", 103, "南充", 104, "天津", 105, "富阳", 106, "泰安", 112, "诸暨", 112, "郑州", 113, "哈尔滨", 114, "聊城", 116, "芜湖", 117, "唐山", 119, "平顶山", 119, "邢台", 119, "德州", 120, "济宁", 120, "荆州", 127, "宜昌", 130, "义乌", 132, "丽水", 133, "洛阳", 134, "秦皇岛", 136, "株洲", 143, "石家庄", 147, "莱芜", 148, "常德", 152, "保定", 153, "湘潭", 154, "金华", 157, "岳阳", 169, "长沙", 175, "衢州", 177, "廊坊", 193, "菏泽", 194, "合肥", 229, "武汉", 273, "大庆", 279,
'''# 提取城市和AQI值
pattern = re.compile(r'"([^"]+)",\s*(\d+)')
matches = pattern.findall(data_str)
cities = [match[0] for match in matches]
aqi_values = [int(match[1]) for match in matches]# 创建城市 - AQI配对列表并排序
city_aqi = list(zip(cities, aqi_values))
sorted_city_aqi = sorted(city_aqi, key=lambda x: x[1], reverse=True)# 分离排序后的数据
sorted_cities = [item[0] for item in sorted_city_aqi]
sorted_aqi = [item[1] for item in sorted_city_aqi]# 计算均值线位置
mean_aqi = 80# 找到"绵阳"在排序后的列表中的索引
target_city = "绵阳"
target_index = None
for index, (city, aqi) in enumerate(sorted_city_aqi):if city == target_city and aqi == mean_aqi:target_index = indexbreak# 如果找不到绵阳,尝试找到第一个AQI为80的城市
if target_index is None:for index, aqi in enumerate(sorted_aqi):if aqi == mean_aqi:target_index = indexbreak# 如果仍然找不到,使用平均值位置
if target_index is None:target_index = len(sorted_aqi) // 2print(f"警告: 未找到AQI值为{mean_aqi}的城市,使用中间位置")# 创建横向条形图
bar = (Bar().add_xaxis(sorted_cities) # 城市名称作为X轴.add_yaxis(series_name="AQI指数",y_axis=sorted_aqi, # AQI值作为Y轴label_opts=opts.LabelOpts(position="right"),# 设置条形颜色:高于均值红色,低于均值绿色# 注意:这里使用大于等于80作为分界,确保80在红色区域itemstyle_opts=opts.ItemStyleOpts(color=JsCode(f'''function(params) {{return params.value >= {mean_aqi} ? '#d14a61' : '#91cc75';}}'''))).reversal_axis() # 反转坐标轴,使图表变为横向.set_global_opts(title_opts=opts.TitleOpts(title="城市 AQI 对比"),xaxis_opts=opts.AxisOpts(name="城市",axislabel_opts=opts.LabelOpts(font_size=8)),yaxis_opts=opts.AxisOpts(name="AQI 指数"),# 添加数据缩放功能,便于查看所有城市datazoom_opts=opts.DataZoomOpts(type_="inside",orient="vertical",range_start=0,range_end=100)).set_series_opts(markline_opts=opts.MarkLineOpts(# 使用找到的索引对应的位置作为标记线位置(y坐标)data=[opts.MarkLineItem(y=target_index, name="均值")],label_opts=opts.LabelOpts(position="middle", formatter=f"均值: {mean_aqi}"),linestyle_opts=opts.LineStyleOpts(color="blue", type_="dashed")))
)# 渲染图表
bar.render("city_aqi_comparison.html")
代码说明:
数据处理:
-
原始数据解析为元组列表
-
使用
sorted()
函数按 AQI 值降序排列 -
分离城市名称和 AQI 数值
图形配置:
-
reversal_axis()
实现横向条形图 -
itemstyle_opts
通过 JsCode 动态设置颜色: -
高于均值(80)显示红色
-
低于均值显示绿色
-
全局配置包含:
-
居中标题 "城市 AQI 对比"
-
坐标轴名称 "AQI 指数" 和 "城市"
-
显示工具箱方便交互
-
蓝色虚线均值标记线
-
颜色区分:
-
红色条形代表 AQI 高于均值(80)
-
绿色条形代表 AQI 低于均值(80)
运行结果:
生成city_aqi_comparison.html
文件,打开后可看到:横向条形图按 AQI 从高到低排列(大庆 279 最高,海门 9 最低);红色 / 绿色条形清晰区分均值上下;蓝色虚线标注均值线 80;右侧显示具体 AQI 数值;支持缩放、下载等工具箱功能。
实验 2:AQI 等级分布饼图
任务:基于 AQI 等级划分(优/良/轻度污染/中度污染/重度污染),绘制饼图并添加交互功能:
1. 使用 Pie 图表展示各等级下城市数量占比
2. 标签显示百分比和等级名称,突出显示占比最大的扇区
3. 添加点击事件:单击扇区时弹出该等级详细城市数量及城市名称列表
#!/usr/bin/env python3
# -*- coding: utf-8 -*-import plotly.graph_objects as go# 解析数据文件内容
data_str = '"海门", 9, "鄂尔多斯", 12, "招远", 12, "舟山", 12, "齐齐哈尔", 14, "盐城", 15, "赤峰", 16, "青岛", 18, "乳山", 18, "金昌", 19, "泉州", 21, "莱西", 21, "日照", 21, "胶南", 22, "南通", 23, "拉萨", 140, "云浮", 24, "梅州", 25, "文登", 25, "上海", 25, "攀枝花", 25, "威海", 25, "承德", 25, "厦门", 26, "汕尾", 26, "潮州", 26, "丹东", 27, "太仓", 27, "曲靖", 27, "烟台", 28, "福州", 29, "瓦房店", 30, "即墨", 30, "抚顺", 31, "玉溪", 31, "张家口", 31, "阳泉", 31, "莱州", 32, "湖州", 32, "汕头", 32, "昆山", 33, "宁波", 33, "湛江", 33, "揭阳", 34, "荣成", 34, "连云港", 35, "葫芦岛", 35, "常熟", 36, "东莞", 36, "河源", 36, "淮安", 36, "泰州", 36, "南宁", 37, "营口", 37, "惠州", 37, "江阴", 37, "蓬莱", 37, "韶关", 38, "嘉峪关", 38, "广州", 38, "延安", 38, "太原", 39, "清远", 39, "中山", 39, "昆明", 39, "寿光", 40, "盘锦", 40, "长治", 41, "深圳", 41, "珠海", 42, "宿迁", 43, "咸阳", 43, "铜川", 44, "平度", 44, "佛山", 44, "海口", 44, "江门", 45, "章丘", 45, "肇庆", 46, "大连", 47, "临汾", 47, "吴江", 47, "石嘴山", 49, "沈阳", 50, "苏州", 50, "茂名", 50, "嘉兴", 51, "长春", 51, "胶州", 52, "银川", 52, "张家港", 52, "三门峡", 53, "锦州", 54, "南昌", 54, "柳州", 54, "三亚", 54, "自贡", 56, "吉林", 56, "阳江", 57, "泸州", 57, "西宁", 57, "宜宾", 58, "呼和浩特", 58, "成都", 58, "大同", 58, "镇江", 59, "桂林", 59, "张家界", 59, "宜兴", 59, "北海", 60, "西安", 61, "金坛", 62, "东营", 62, "牡丹江", 63, "遵义", 63, "绍兴", 63, "扬州", 64, "常州", 64, "潍坊", 65, "重庆", 66, "台州", 67, "南京", 67, "滨州", 70, "贵阳", 71, "无锡", 71, "本溪", 71, "克拉玛依", 72, "渭南", 72, "马鞍山", 72, "宝鸡", 72, "焦作", 75, "句容", 75, "北京", 79, "徐州", 79, "衡水", 80, "包头", 80, "绵阳", 80, "乌鲁木齐", 84, "枣庄", 84, "杭州", 84, "淄博", 85, "鞍山", 86, "溧阳", 86, "库尔勒", 86, "安阳", 90, "开封", 90, "济南", 92, "德阳", 93, "温州", 95, "九江", 96, "邯郸", 98, "临安", 99, "兰州", 99, "沧州", 100, "临沂", 103, "南充", 104, "天津", 105, "富阳", 106, "泰安", 112, "诸暨", 112, "郑州", 113, "哈尔滨", 114, "聊城", 116, "芜湖", 117, "唐山", 119, "平顶山", 119, "邢台", 119, "德州", 120, "济宁", 120, "荆州", 127, "宜昌", 130, "义乌", 132, "丽水", 133, "洛阳", 134, "秦皇岛", 136, "株洲", 143, "石家庄", 147, "莱芜", 148, "常德", 152, "保定", 153, "湘潭", 154, "金华", 157, "岳阳", 169, "长沙", 175, "衢州", 177, "廊坊", 193, "菏泽", 194, "合肥", 229, "武汉", 273, "大庆", 279'# 处理数据字符串
items = data_str.split(', ')
cities = []
aqi_values = []for i in range(0, len(items)):if not items[i]:continueif items[i].startswith('"'):city = items[i].strip('"')if i + 1 < len(items) and items[i + 1].isdigit():aqi = int(items[i + 1])cities.append(city)aqi_values.append(aqi)elif items[i].isdigit():pass# 定义AQI等级标准
def classify_aqi(aqi):if aqi <= 50:return "优"elif aqi <= 100:return "良"elif aqi <= 150:return "轻度污染"elif aqi <= 200:return "中度污染"else:return "重度污染"# 分类城市到各AQI等级
categories = ["优", "良", "轻度污染", "中度污染", "重度污染"]
category_cities = {cat: [] for cat in categories}
category_counts = {cat: 0 for cat in categories}for city, aqi in zip(cities, aqi_values):category = classify_aqi(aqi)category_cities[category].append(city)category_counts[category] += 1# 准备饼图数据
sizes = [category_counts[cat] for cat in categories]
total = sum(sizes)
percentages = [f'{size / total * 100:.1f}%' for size in sizes]# 找出最大扇区
max_index = sizes.index(max(sizes))# 创建饼图
fig = go.Figure()# 为每个等级添加自定义数据 - 城市列表
custom_data = []
for cat in categories:# 将城市列表转换为HTML格式,每行显示一个城市city_list = "<br>".join(category_cities[cat])custom_data.append(f"<b>{cat}等级城市 ({category_counts[cat]}个):</b><br><br>{city_list}")# 添加饼图
fig.add_trace(go.Pie(labels=categories,values=sizes,textinfo='label+percent',hoverinfo='label+value+percent',hole=0.3,pull=[0.1 if i == max_index else 0 for i in range(len(categories))],marker=dict(colors=['#1f77b4', '#2ca02c', '#ff7f0e', '#d62728', '#9467bd']),customdata=custom_data, # 存储城市列表信息hovertemplate="<b>%{label}</b><br>城市数量: %{value}<br>占比: %{percent}<extra></extra>",textfont_size=16
))# 设置布局
fig.update_layout(title='城市AQI等级分布',title_font_size=24,title_x=0.5,legend_title='AQI等级',annotations=[dict(text=f'城市总数: {total}',x=0.5, y=0.5,font_size=18,showarrow=False)],template="plotly_white",hoverlabel=dict(bgcolor="white",font_size=16,font_family="Arial")
)# 自定义JavaScript代码,用于处理点击事件
custom_js = """
// 等待Plotly图表加载完成
document.addEventListener('DOMContentLoaded', function() {const plotDiv = document.getElementById('myDiv');if (plotDiv) {plotDiv.on('plotly_click', function(data) {// 获取点击的点const point = data.points[0];// 获取自定义数据(城市列表)const cityList = point.customdata;// 创建模态框const modal = document.createElement('div');modal.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;background-color:rgba(0,0,0,0.7);display:flex;justify-content:center;align-items:center;z-index:1000;';// 创建内容框const content = document.createElement('div');content.style.cssText = 'background-color:white;padding:20px;border-radius:10px;max-width:80%;max-height:80%;overflow-y:auto;box-shadow:0 4px 8px rgba(0,0,0,0.2);';// 添加标题const title = document.createElement('h3');title.style.cssText = 'margin-top:0;color:#333;';title.innerHTML = point.label + '等级城市详情';content.appendChild(title);// 添加城市列表const list = document.createElement('div');list.innerHTML = cityList;content.appendChild(list);// 添加关闭按钮const closeBtn = document.createElement('button');closeBtn.innerHTML = '关闭';closeBtn.style.cssText = 'margin-top:15px;padding:8px 16px;background-color:#4CAF50;color:white;border:none;border-radius:4px;cursor:pointer;';closeBtn.onclick = function() {document.body.removeChild(modal);};content.appendChild(closeBtn);// 添加到模态框modal.appendChild(content);// 添加到文档document.body.appendChild(modal);// 点击模态框背景关闭modal.onclick = function(event) {if (event.target === modal) {document.body.removeChild(modal);}};});}
});
"""# 将图表保存为HTML文件,并嵌入自定义JavaScript
with open("aqi_distribution.html", "w", encoding="utf-8") as f:# 获取HTML内容html_content = fig.to_html(include_plotlyjs='cdn',full_html=True,div_id='myDiv',config={'responsive': True})# 在HTML头部添加UTF-8编码声明head_start = html_content.find('<head>') + 6encoding_meta = '<meta charset="UTF-8">'html_content = html_content[:head_start] + encoding_meta + html_content[head_start:]# 在body结束前添加自定义JavaScriptbody_end = html_content.rfind('</body>')html_content = html_content[:body_end] + f'<script>{custom_js}</script>' + html_content[body_end:]# 写入文件f.write(html_content)print("图表已保存为 aqi_distribution.html,请在浏览器中打开查看交互效果。")
实验 3:多城市 AQI 对比仪表盘
任务:使用 Tab 或 Page 组件构建多图表仪表盘:
1. 第一选项卡:显示 AQI 前 10 城市的横向条形图
2. 第二选项卡:展示西北地区城市 AQI 散点数据(添加回归线)与东部城市 AQI 散点数据(添加回归线)的对比
3. 第三选项卡:组合折线图(各地区 AQI 指数的变化)
4. 要求所有图表共享主题风格(如 ThemeType.DARK)
from pyecharts import options as opts
from pyecharts.charts import Bar, Scatter, Line, Tab
from pyecharts.globals import ThemeType
import numpy as np
from sklearn.linear_model import LinearRegression# 解析文件内容
data = []
content = """
"海门", 9, "鄂尔多斯", 12, "招远", 12, "舟山", 12, "齐齐哈尔", 14, "盐城", 15, "赤峰", 16, "青岛", 18, "乳山", 18, "金昌", 19, "泉州", 21, "莱西", 21, "日照", 21, "胶南", 22, "南通", 23, "拉萨", 140, "云浮", 24, "梅州", 25, "文登", 25, "上海", 25, "攀枝花", 25, "威海", 25, "承德", 25, "厦门", 26, "汕尾", 26, "潮州", 26, "丹东", 27, "太仓", 27, "曲靖", 27, "烟台", 28, "福州", 29, "瓦房店", 30, "即墨", 30, "抚顺", 31, "玉溪", 31, "张家口", 31, "阳泉", 31, "莱州", 32, "湖州", 32, "汕头", 32, "昆山", 33, "宁波", 33, "湛江", 33, "揭阳", 34, "荣成", 34, "连云港", 35, "葫芦岛", 35, "常熟", 36, "东莞", 36, "河源", 36, "淮安", 36, "泰州", 36, "南宁", 37, "营口", 37, "惠州", 37, "江阴", 37, "蓬莱", 37, "韶关", 38, "嘉峪关", 38, "广州", 38, "延安", 38, "太原", 39, "清远", 39, "中山", 39, "昆明", 39, "寿光", 40, "盘锦", 40, "长治", 41, "深圳", 41, "珠海", 42, "宿迁", 43, "咸阳", 43, "铜川", 44, "平度", 44, "佛山", 44, "海口", 44, "江门", 45, "章丘", 45, "肇庆", 46, "大连", 47, "临汾", 47, "吴江", 47, "石嘴山", 49, "沈阳", 50, "苏州", 50, "茂名", 50, "嘉兴", 51, "长春", 51, "胶州", 52, "银川", 52, "张家港", 52, "三门峡", 53, "锦州", 54, "南昌", 54, "柳州", 54, "三亚", 54, "自贡", 56, "吉林", 56, "阳江", 57, "泸州", 57, "西宁", 57, "宜宾", 58, "呼和浩特", 58, "成都", 58, "大同", 58, "镇江", 59, "桂林", 59, "张家界", 59, "宜兴", 59, "北海", 60, "西安", 61, "金坛", 62, "东营", 62, "牡丹江", 63, "遵义", 63, "绍兴", 63, "扬州", 64, "常州", 64, "潍坊", 65, "重庆", 66, "台州", 67, "南京", 67, "滨州", 70, "贵阳", 71, "无锡", 71, "本溪", 71, "克拉玛依", 72, "渭南", 72, "马鞍山", 72, "宝鸡", 72, "焦作", 75, "句容", 75, "北京", 79, "徐州", 79, "衡水", 80, "包头", 80, "绵阳", 80, "乌鲁木齐", 84, "枣庄", 84, "杭州", 84, "淄博", 85, "鞍山", 86, "溧阳", 86, "库尔勒", 86, "安阳", 90, "开封", 90, "济南", 92, "德阳", 93, "温州", 95, "九江", 96, "邯郸", 98, "临安", 99, "兰州", 99, "沧州", 100, "临沂", 103, "南充", 104, "天津", 105, "富阳", 106, "泰安", 112, "诸暨", 112, "郑州", 113, "哈尔滨", 114, "聊城", 116, "芜湖", 117, "唐山", 119, "平顶山", 119, "邢台", 119, "德州", 120, "济宁", 120, "荆州", 127, "宜昌", 130, "义乌", 132, "丽水", 133, "洛阳", 134, "秦皇岛", 136, "株洲", 143, "石家庄", 147, "莱芜", 148, "常德", 152, "保定", 153, "湘潭", 154, "金华", 157, "岳阳", 169, "长沙", 175, "衢州", 177, "廊坊", 193, "菏泽", 194, "合肥", 229, "武汉", 273, "大庆", 279
"""# 处理数据
items = content.replace('"', '').split(', ')
for i in range(0, len(items), 2):city = items[i].strip()aqi = int(items[i+1].strip())data.append((city, aqi))# 排序数据
sorted_data = sorted(data, key=lambda x: x[1], reverse=True)# 1. 第一选项卡: AQI前10城市横向条形图
top_10_cities = [x[0] for x in sorted_data[:10]]
top_10_aqi = [x[1] for x in sorted_data[:10]]bar = (Bar(init_opts=opts.InitOpts(theme=ThemeType.DARK)).add_xaxis(top_10_cities).add_yaxis("AQI指数", top_10_aqi).reversal_axis().set_series_opts(label_opts=opts.LabelOpts(position="right")).set_global_opts(title_opts=opts.TitleOpts(title="AQI排名前10城市"),xaxis_opts=opts.AxisOpts(name="AQI值"),yaxis_opts=opts.AxisOpts(name="城市"),)
)# 2. 第二选项卡: 西北与东部城市AQI对比散点图
# 定义西北和东部城市
northwest_cities = ["西安", "兰州", "西宁", "银川", "乌鲁木齐", "呼和浩特","克拉玛依", "库尔勒", "咸阳", "渭南", "宝鸡", "金昌"]
east_cities = ["上海", "北京", "天津", "广州", "深圳", "杭州", "南京", "苏州","宁波", "青岛", "济南", "厦门", "福州", "大连", "沈阳", "烟台"]# 提取数据
nw_data = [(city, aqi) for city, aqi in data if city in northwest_cities]
east_data = [(city, aqi) for city, aqi in data if city in east_cities]# 准备散点图数据 - 使用数值索引作为x轴
nw_points = [[i, aqi] for i, (city, aqi) in enumerate(nw_data)]
east_points = [[i + len(nw_data), aqi] for i, (city, aqi) in enumerate(east_data)]# 创建散点图
scatter = (Scatter(init_opts=opts.InitOpts(theme=ThemeType.DARK)).add_xaxis([p[0] for p in nw_points]).add_yaxis("西北城市",[p[1] for p in nw_points],symbol_size=15,label_opts=opts.LabelOpts(is_show=False),).add_xaxis([p[0] for p in east_points]).add_yaxis("东部城市",[p[1] for p in east_points],symbol_size=15,label_opts=opts.LabelOpts(is_show=False),).set_global_opts(title_opts=opts.TitleOpts(title="西北与东部城市AQI对比"),xaxis_opts=opts.AxisOpts(name="城市索引",type_="value",min_=0,max_=len(nw_data) + len(east_data) - 1),yaxis_opts=opts.AxisOpts(name="AQI值"),tooltip_opts=opts.TooltipOpts(formatter="城市索引: {c0}<br/>AQI值: {c1}",trigger="item"))
)# 添加回归线 - 简单实现
# 西北城市回归线
nw_x = [p[0] for p in nw_points]
nw_y = [p[1] for p in nw_points]
nw_regression = [[min(nw_x), np.mean(nw_y)],[max(nw_x), np.mean(nw_y)]
]# 东部城市回归线
east_x = [p[0] for p in east_points]
east_y = [p[1] for p in east_points]
east_regression = [[min(east_x), np.mean(east_y)],[max(east_x), np.mean(east_y)]
]# 添加回归线到图表
scatter.set_series_opts(markline_opts=[opts.MarkLineOpts(data=[{"coord": nw_regression[0]},{"coord": nw_regression[1]}],linestyle_opts=opts.LineStyleOpts(width=2, type_="dashed", color="#FFA500"),label_opts=opts.LabelOpts(is_show=True, formatter="西北平均AQI")),opts.MarkLineOpts(data=[{"coord": east_regression[0]},{"coord": east_regression[1]}],linestyle_opts=opts.LineStyleOpts(width=2, type_="dashed", color="#1E90FF"),label_opts=opts.LabelOpts(is_show=True, formatter="东部平均AQI"))]
)# 3. 第三选项卡: 各地区AQI变化折线图
# 定义地区
regions = {"华北": ["北京", "天津", "石家庄", "太原", "呼和浩特", "包头", "唐山", "保定"],"华东": ["上海", "南京", "杭州", "合肥", "福州", "济南", "苏州", "宁波"],"华南": ["广州", "深圳", "南宁", "海口", "厦门", "珠海", "汕头"],"华中": ["武汉", "郑州", "长沙", "南昌", "宜昌", "洛阳"],"东北": ["沈阳", "大连", "长春", "哈尔滨", "鞍山", "吉林"],"西南": ["重庆", "成都", "贵阳", "昆明", "拉萨"],"西北": ["西安", "兰州", "西宁", "银川", "乌鲁木齐"]
}# 计算各地区平均AQI
region_avg = {}
for region, cities in regions.items():region_aqi = [aqi for city, aqi in data if city in cities]if region_aqi: # 确保列表不为空region_avg[region] = sum(region_aqi) / len(region_aqi)else:region_avg[region] = 0# 按地区名称排序
sorted_regions = sorted(region_avg.keys())
avg_aqi = [region_avg[region] for region in sorted_regions]line = (Line(init_opts=opts.InitOpts(theme=ThemeType.DARK)).add_xaxis(sorted_regions).add_yaxis("平均AQI",avg_aqi,is_smooth=True,label_opts=opts.LabelOpts(is_show=True),linestyle_opts=opts.LineStyleOpts(width=3),symbol="circle",symbol_size=10,).set_global_opts(title_opts=opts.TitleOpts(title="各地区平均AQI指数"),xaxis_opts=opts.AxisOpts(name="地区"),yaxis_opts=opts.AxisOpts(name="平均AQI值"),tooltip_opts=opts.TooltipOpts(trigger="axis"),)
)# 创建选项卡
tab = Tab()
tab.add(bar, "AQI Top 10")
tab.add(scatter, "地区对比")
tab.add(line, "区域变化")# 输出图表
tab.render("aqi_dashboard.html")
print("仪表盘已生成: aqi_dashboard.html")
上述代码使用 PyEcharts 创建了一个包含三个选项卡的仪表盘,用于可视化城市 AQI 数据:
-
第一选项卡:AQI Top 10 城市横向条形图
-
第二选项卡:西北与东部城市 AQI 对比散点图
-
第三选项卡:各地区平均 AQI 折线图
所有图表使用深色主题(ThemeType.DARK
)。数据处理:从给定的文本内容解析城市和 AQI 数据;将数据转换为 (城市, AQI)
元组列表;按 AQI 值降序排序。
第一选项卡:AQI Top 10。提取 AQI 最高的 10 个城市;创建横向条形图:X 轴:AQI 值;Y 轴:城市名称;条形方向:水平(reversal_axis()
);标签显示在右侧。
第二选项卡:地区对比。定义西北和东部城市列表,准备散点图数据:西北城市使用索引 0-11;东部城市使用索引 12-27;添加回归线:西北城市:橙色水平虚线,显示"西北平均AQI"标签;东部城市:蓝色水平虚线,显示"东部平均AQI"标签。散点图配置:X 轴:城市索引(0-27);Y 轴:AQI 值;提示框显示城市索引和 AQI 值。
第三选项卡:区域变化。定义 7 个地理区域(华北、华东等),计算每个区域的平均 AQI,创建平滑折线图:X 轴:区域名称(按字母排序)‘’Y 轴:平均 AQI 值,带圆形标记点。
仪表盘集成:使用 Tab
组件集成三个图表输出为 HTML 文件。
第一选项卡:
第二选项卡:
第三选项卡:
实验 4:2D 地理 AQI 可视化
任务:结合 Geo 实现二维地理可视化:
1. 在中国地图上标记城市位置,使用不同颜色表示 AQI 值
2. 展示不同地区 AQI 数值
from pyecharts import options as opts
from pyecharts.charts import Geo
from pyecharts.globals import ChartType, SymbolType# 解析数据
data_str = """
"海门", 9, "鄂尔多斯", 12, "招远", 12, "舟山", 12, "齐齐哈尔", 14, "盐城", 15, "赤峰", 16, "青岛", 18, "乳山", 18, "金昌", 19, "泉州", 21, "莱西", 21, "日照", 21, "胶南", 22, "南通", 23, "拉萨", 140, "云浮", 24, "梅州", 25, "文登", 25, "上海", 25, "攀枝花", 25, "威海", 25, "承德", 25, "厦门", 26, "汕尾", 26, "潮州", 26, "丹东", 27, "太仓", 27, "曲靖", 27, "烟台", 28, "福州", 29, "瓦房店", 30, "即墨", 30, "抚顺", 31, "玉溪", 31, "张家口", 31, "阳泉", 31, "莱州", 32, "湖州", 32, "汕头", 32, "昆山", 33, "宁波", 33, "湛江", 33, "揭阳", 34, "荣成", 34, "连云港", 35, "葫芦岛", 35, "常熟", 36, "东莞", 36, "河源", 36, "淮安", 36, "泰州", 36, "南宁", 37, "营口", 37, "惠州", 37, "江阴", 37, "蓬莱", 37, "韶关", 38, "嘉峪关", 38, "广州", 38, "延安", 38, "太原", 39, "清远", 39, "中山", 39, "昆明", 39, "寿光", 40, "盘锦", 40, "长治", 41, "深圳", 41, "珠海", 42, "宿迁", 43, "咸阳", 43, "铜川", 44, "平度", 44, "佛山", 44, "海口", 44, "江门", 45, "章丘", 45, "肇庆", 46, "大连", 47, "临汾", 47, "吴江", 47, "石嘴山", 49, "沈阳", 50, "苏州", 50, "茂名", 50, "嘉兴", 51, "长春", 51, "胶州", 52, "银川", 52, "张家港", 52, "三门峡", 53, "锦州", 54, "南昌", 54, "柳州", 54, "三亚", 54, "自贡", 56, "吉林", 56, "阳江", 57, "泸州", 57, "西宁", 57, "宜宾", 58, "呼和浩特", 58, "成都", 58, "大同", 58, "镇江", 59, "桂林", 59, "张家界", 59, "宜兴", 59, "北海", 60, "西安", 61, "金坛", 62, "东营", 62, "牡丹江", 63, "遵义", 63, "绍兴", 63, "扬州", 64, "常州", 64, "潍坊", 65, "重庆", 66, "台州", 67, "南京", 67, "滨州", 70, "贵阳", 71, "无锡", 71, "本溪", 71, "克拉玛依", 72, "渭南", 72, "马鞍山", 72, "宝鸡", 72, "焦作", 75, "句容", 75, "北京", 79, "徐州", 79, "衡水", 80, "包头", 80, "绵阳", 80, "乌鲁木齐", 84, "枣庄", 84, "杭州", 84, "淄博", 85, "鞍山", 86, "溧阳", 86, "库尔勒", 86, "安阳", 90, "开封", 90, "济南", 92, "德阳", 93, "温州", 95, "九江", 96, "邯郸", 98, "临安", 99, "兰州", 99, "沧州", 100, "临沂", 103, "南充", 104, "天津", 105, "富阳", 106, "泰安", 112, "诸暨", 112, "郑州", 113, "哈尔滨", 114, "聊城", 116, "芜湖", 117, "唐山", 119, "平顶山", 119, "邢台", 119, "德州", 120, "济宁", 120, "荆州", 127, "宜昌", 130, "义乌", 132, "丽水", 133, "洛阳", 134, "秦皇岛", 136, "株洲", 143, "石家庄", 147, "莱芜", 148, "常德", 152, "保定", 153, "湘潭", 154, "金华", 157, "岳阳", 169, "长沙", 175, "衢州", 177, "廊坊", 193, "菏泽", 194, "合肥", 229, "武汉", 273, "大庆", 279
"""# 处理数据
items = data_str.strip().split(', ')
data_pairs = [(items[i].strip('"'), int(items[i+1]))for i in range(0, len(items), 2)]# 创建地理坐标系
geo = (Geo(init_opts=opts.InitOpts(width="1200px", height="800px")).add_schema(maptype="china",itemstyle_opts=opts.ItemStyleOpts(color="#323c48", border_color="#111"),).add(series_name="AQI",data_pair=data_pairs,type_=ChartType.SCATTER,symbol_size=8,label_opts=opts.LabelOpts(is_show=False),).set_series_opts().set_global_opts(visualmap_opts=opts.VisualMapOpts(is_piecewise=True,pieces=[{"min": 0, "max": 50, "label": "0-50 (优)", "color": "#93CE07"},{"min": 51, "max": 100, "label": "51-100 (良)", "color": "#FBDB0F"},{"min": 101, "max": 150, "label": "101-150 (轻度)", "color": "#FC7D02"},{"min": 151, "max": 200, "label": "151-200 (中度)", "color": "#FD0100"},{"min": 201, "max": 300, "label": "201-300 (重度)", "color": "#AA069F"},{"min": 301, "label": ">300 (严重)", "color": "#AC3B2A"},],pos_left="10px",pos_bottom="20px",orient="horizontal"),title_opts=opts.TitleOpts(title="中国城市 AQI 分布可视化",subtitle="数据来源:模拟数据",pos_left="center"),tooltip_opts=opts.TooltipOpts(formatter="{b}: {c} AQI",trigger="item"),legend_opts=opts.LegendOpts(is_show=False))
)# 生成HTML文件
geo.render("china_aqi_visualization.html")
代码说明:
数据解析:将文本数据解析成 (城市名, AQI值)
的元组列表。
地理坐标系设置:使用 Geo
组件创建中国地图,设置合适的地图尺寸和背景样式。
可视化效果:使用散点图(Scatter)在地图上标记城市位置,点的大小固定为8像素,关闭城市标签显示避免重叠。
分段颜色映射:根据中国空气质量指数标准分段:0-50:绿色(优),51-100:黄色(良),101-150:橙色(轻度污染),151-200:红色(中度污染),201-300:紫色(重度污染),300:深红色(严重污染)。水平放置的颜色指示条。
交互功能:鼠标悬停时显示城市名称和AQI值,支持地图缩放和拖动。
实验 5:3D 地理 AQI 可视化
任务:结合 Geo3D 和 Bar3D 实现三维地理可视化:
1. 在地球模型上标记城市位置,高度表示 AQI 值
2. 使用 Map3D 配置光照效果和区域颜色(如中国区域高亮)
3. 展示不同地区 AQI 数值
import json
from pyecharts import options as opts
from pyecharts.charts import Map3D
from pyecharts.globals import ChartType, ThemeType# 解析数据文件内容
data_str = '''
"海门", 9, "鄂尔多斯", 12, "招远", 12, "舟山", 12, "齐齐哈尔", 14, "盐城", 15, "赤峰", 16, "青岛", 18, "乳山", 18, "金昌", 19, "泉州", 21, "莱西", 21, "日照", 21, "胶南", 22, "南通", 23, "拉萨", 140, "云浮", 24, "梅州", 25, "文登", 25, "上海", 25, "攀枝花", 25, "威海", 25, "承德", 25, "厦门", 26, "汕尾", 26, "潮州", 26, "丹东", 27, "太仓", 27, "曲靖", 27, "烟台", 28, "福州", 29, "瓦房店", 30, "即墨", 30, "抚顺", 31, "玉溪", 31, "张家口", 31, "阳泉", 31, "莱州", 32, "湖州", 32, "汕头", 32, "昆山", 33, "宁波", 33, "湛江", 33, "揭阳", 34, "荣成", 34, "连云港", 35, "葫芦岛", 35, "常熟", 36, "东莞", 36, "河源", 36, "淮安", 36, "泰州", 36, "南宁", 37, "营口", 37, "惠州", 37, "江阴", 37, "蓬莱", 37, "韶关", 38, "嘉峪关", 38, "广州", 38, "延安", 38, "太原", 39, "清远", 39, "中山", 39, "昆明", 39, "寿光", 40, "盘锦", 40, "长治", 41, "深圳", 41, "珠海", 42, "宿迁", 43, "咸阳", 43, "铜川", 44, "平度", 44, "佛山", 44, "海口", 44, "江门", 45, "章丘", 45, "肇庆", 46, "大连", 47, "临汾", 47, "吴江", 47, "石嘴山", 49, "沈阳", 50, "苏州", 50, "茂名", 50, "嘉兴", 51, "长春", 51, "胶州", 52, "银川", 52, "张家港", 52, "三门峡", 53, "锦州", 54, "南昌", 54, "柳州", 54, "三亚", 54, "自贡", 56, "吉林", 56, "阳江", 57, "泸州", 57, "西宁", 57, "宜宾", 58, "呼和浩特", 58, "成都", 58, "大同", 58, "镇江", 59, "桂林", 59, "张家界", 59, "宜兴", 59, "北海", 60, "西安", 61, "金坛", 62, "东营", 62, "牡丹江", 63, "遵义", 63, "绍兴", 63, "扬州", 64, "常州", 64, "潍坊", 65, "重庆", 66, "台州", 67, "南京", 67, "滨州", 70, "贵阳", 71, "无锡", 71, "本溪", 71, "克拉玛依", 72, "渭南", 72, "马鞍山", 72, "宝鸡", 72, "焦作", 75, "句容", 75, "北京", 79, "徐州", 79, "衡水", 80, "包头", 80, "绵阳", 80, "乌鲁木齐", 84, "枣庄", 84, "杭州", 84, "淄博", 85, "鞍山", 86, "溧阳", 86, "库尔勒", 86, "安阳", 90, "开封", 90, "济南", 92, "德阳", 93, "温州", 95, "九江", 96, "邯郸", 98, "临安", 99, "兰州", 99, "沧州", 100, "临沂", 103, "南充", 104, "天津", 105, "富阳", 106, "泰安", 112, "诸暨", 112, "郑州", 113, "哈尔滨", 114, "聊城", 116, "芜湖", 117, "唐山", 119, "平顶山", 119, "邢台", 119, "德州", 120, "济宁", 120, "荆州", 127, "宜昌", 130, "义乌", 132, "丽水", 133, "洛阳", 134, "秦皇岛", 136, "株洲", 143, "石家庄", 147, "莱芜", 148, "常德", 152, "保定", 153, "湘潭", 154, "金华", 157, "岳阳", 169, "长沙", 175, "衢州", 177, "廊坊", 193, "菏泽", 194, "合肥", 229, "武汉", 273, "大庆", 279
'''# 处理数据
items = [item.strip().replace('"', '') for item in data_str.split(',')]
city_aqi = []
for i in range(0, len(items), 2):city = items[i]aqi = int(items[i + 1])city_aqi.append((city, aqi))# 创建3D地球可视化
map3d = (Map3D(init_opts=opts.InitOpts(width="1600px", height="800px", theme=ThemeType.DARK)).add_schema(# 使用地球模型maptype="world",# 地球样式配置itemstyle_opts=opts.ItemStyleOpts(color="rgba(5, 50, 80, 0.7)", # 海洋颜色border_color="rgba(0, 100, 200, 0.5)", # 边界线),# 光照配置light_opts=opts.Map3DLightOpts(main_color="#fff",main_intensity=1.2,main_alpha=55,main_beta=10,ambient_intensity=0.4),# 视图控制view_control_opts=opts.Map3DViewControlOpts(auto_rotate=True,auto_rotate_speed=5,distance=150,min_distance=50,max_distance=300,alpha=30,beta=20,),# 修改:移除重复的 itemstyle_opts,添加 map3d_label 配置map3d_label=opts.Map3DLabelOpts(is_show=True,text_style=opts.TextStyleOpts(color="#fff",font_size=8,background_color="rgba(0,0,0,0)")),)# 添加数据点 - 使用柱状高度表示AQI值.add(series_name="AQI",data_pair=city_aqi,type_=ChartType.BAR3D,# 柱状图配置bar_size=1.5, # 柱子粗细shading="realistic", # 更真实的渲染效果min_height=0.5, # 最小高度# 柱体样式itemstyle_opts=opts.ItemStyleOpts(color="rgb(255, 100, 0)", # 基础颜色opacity=0.9,border_width=0.5,border_color="rgba(255,255,255,0.5)"),# 标签配置label_opts=opts.LabelOpts(is_show=True,formatter="{b}: {c}",position="top",color="#ffcc00",font_size=10))# 全局配置.set_global_opts(title_opts=opts.TitleOpts(title="全国城市AQI 3D可视化",subtitle="柱状高度表示AQI值",pos_left="center",title_textstyle_opts=opts.TextStyleOpts(color="#fff",font_size=24)),visualmap_opts=opts.VisualMapOpts(is_show=True,dimension=0,pos_left="10",pos_top="center",range_text=["高污染", "低污染"],range_color=["#d73027", "#fc8d59", "#fee090", "#e0f3f8", "#4575b4"],min_=min(aqi for _, aqi in city_aqi),max_=max(aqi for _, aqi in city_aqi),textstyle_opts=opts.TextStyleOpts(color="#fff")),# 图例配置legend_opts=opts.LegendOpts(is_show=False))
)# 生成HTML文件
map3d.render("aqi_3d_earth.html")
上述代码使用 pyecharts`库创建了一个全国城市空气质量指数(AQI)的3D地球可视化图表。
数据处理:从长字符串 data_str中提取城市名称和对应的AQI值,通过字符串分割和处理构建数据对列表 city_aqi将数据转换为 (城市名, AQI值)`的元组形式,便于后续图表使用。创建一个3D地图对象,设置宽度、高度和暗色主题。 使用全球地图作为底图 - 设置海洋颜色为深蓝色,边界线为浅蓝色 - 配置光照效果增强立体感 - 设置自动旋转和视角控制参数 - 添加地图标签样式。
数据可视化 :使用 BAR3D 类型,通过柱状高度表示AQI值,柱子粗细和最小高度 - 真实感渲染效果 - 橙色系颜色主题 - 柱体顶部显示城市名和AQI值标签 ,添加渐变色视觉映射,从蓝色(低污染)到红色(高污染),地球会自动旋转,展示各个角度的城市数据,侧边栏提供污染程度的颜色参考,每个数据点上方显示具体数值 ,支持缩放、旋转和视角调整 ,输出生成一个独立的HTML文件 aqi_3d_earth.html。
【实验总结】
本次实验以全国城市空气质量指数(AQI)为对象,借 Pyecharts 库完成多类型可视化任务。
- 可视化原则落地:遵循交互体验原则,如实验1给横向条形图加数据缩放、均值标记,实验2为饼图设点击弹窗,保障交互高效,助用户理解数据。
- 技术分类实践:覆盖条形图、饼图、Geo、Map3D、Tab仪表盘等可视化类型,验证不同场景技术选型逻辑。
- 核心技术掌握:熟用Pyecharts配置样式、处理交互,结合数据清洗、简单建模,实现“数据 - 可视化 - 交互分析”闭环。
- 地理编码匹配(实验5):初始3D地球因world地图不支持中文城市致数据“扎堆”,改用china地图、聚焦中国视角,解决坐标映射问题。
- 交互逻辑优化(实验2):饼图点击事件需自定义JS实现模态框,精细处理DOM与数据传递,实现“点击扇区 - 弹详情”交互,增强探索性。
- 多图表协同(实验3):构建Tab仪表盘时,统一主题、协调数据维度,分层封装配置,实现多视角连贯展示。