Pyecharts绘制带timeline的地图网上有很多教程,但大多都是基于csv或txt文件的,我来分享一份使用MySQL数据库中数据的代码。
入门+工具+连接MySQL数据库+数据导入请移步文章:http://t.csdnimg.cn/aRRXV
一、需求+成果展示
Map即地图类,TimeLine为时间轴类,我们需求如下:
一个地图中数据随时间轴的切换而切换的综合图表。下面是程序编写过程。
二、数据预处理
我们的数据格式为(date,spot,temp):日期str,地点str,温度str
显然对应关系为:将数据按日期排序,可视化显示出在某一日期时所有地点对应的温度值。
上一次我们导入数据成功:
import pymysql.cursors
# 连接数据库
db = pymysql.connect(
host="localhost",
user="root",
password="root",
db="pyecharts_test",
charset="utf8",#这里即使之前选择为“utf8mb3”,也写utf8,这是在python中的代码
)
# 查询数据
sql = "SELECT * FROM China_temp_1 "
try:
cursor = db.cursor()
cursor.execute(sql)
result = cursor.fetchall()
except Exception as e:
db.rollback()
print("Error", e)
else:
db.commit()
print("Success")#输出Success即表示数据导入成功,方便以后debug
#debug代码块
# # 打印前 5 行数据
# print("前 5 行数据:")
# for i in range(min(5, len(result))):
# print(result[i])
# # 获取总行数
# total_rows = len(result)
# print(f"总共 {total_rows} 行数据")
下面是示例中的Map类:
from pyecharts import options as opts
from pyecharts.charts import Map
from pyecharts.faker import Faker
c = (
Map()
.add("商家A", [list(z) for z in zip(Faker.provinces, Faker.values())], "china")
.set_global_opts(title_opts=opts.TitleOpts(title="Map-基本示例"))
.render("map_base.html")
)
只需改写x轴和y轴对应的内容,但在此之前,需要对数据进行预处理:
数据处理的库:
import pandas as pd
import re
pyecharts可以识别DataFrame类型的数据,类似表格。
将数据导入得到的result转换为DataFrame形式,并打印测试
# 创建 DataFrame
df = pd.DataFrame(result, columns=['date', 'spot', 'temp'])
print(df)
pyecharts只能识别YYYY/mm/dd的日期形式,所以我们对date数据进行一下格式转换,使用正则匹配:
# 定义日期转换函数
def convert_date(date_str):
# 检查日期格式是否为“2020年1月1日”
match1 = re.match(r"(\d{4})年(\d{1,2})月(\d{1,2})日", date_str)
# 检查日期格式是否为“2020-1-1”
match2 = re.match(r"(\d{4})-(\d{1,2})-(\d{1,2})", date_str)
if match1:
year, month, day = match1.groups()
# 转换为“2020/1/1”格式
return f"{year}/{month}/{day}"
if match2:
year, month, day = match2.groups()
# 转换为“2020/1/1”格式
return f"{year}/{month}/{day}"
return date_str
# 应用日期转换函数
df['date'] = df['date'].apply(convert_date)
这里转换后的格式为String类型,Map类需要datetime类型的数据,转换一下:
# 将日期列转换为 datetime 格式
df['date'] = pd.to_datetime(df['date'], format='%Y/%m/%d', errors='coerce')
为了画出timeline轴,我们肯定希望日期按顺序排列,并且不重复,故先去重在排序。去重我们以【日期+地点】为标准,【】相同的去除。并将日期单独提取出来,作为时间轴之用。
# 去除重复记录
df = df.drop_duplicates(subset=['date', 'spot'], keep='first')
# 按照日期排序
df = df.sort_values(by='date')
# 获取唯一日期
dates = df['date'].dropna().unique() # 去掉 NaT
三、数据传入与绘图
接下来就该创建时间轴了,代码如下:
from pyecharts import options as opts
from pyecharts.charts import Timeline, Map
其中一些样式部分,参照这张图,还有许多可以调整的部分,参照官网的说明,非常详细。
# 创建时间线图实例
timeline = Timeline(init_opts=opts.InitOpts(
#时间轴总的样式部分在这里写
theme='light',
bg_color='rgba(0,0,0,0)' # 设置页面背景颜色为完全透明
)
)
timeline.add_schema(
orient='horizontal', # 时间轴水平显示
is_auto_play=True, # 自动播放
play_interval=10, # 播放间隔时间,单位毫秒
label_opts=opts.LabelOpts(
#标签的样式在这里写
is_show=True, # 显示标签
font_family='Arial', # 标签字体样式
rotate=-5, # 标签旋转角度
# distance='30px', # 垂直偏移量,向下移动30像素
# formatter="{value}", # 设置标签格式为年月日,固定为一月一日
# interval=365 * 24 * 3600 * 1000, # 时间轴标签间隔为一年
# color='#333', # 标签文字颜色
# font_size=10, # 标签文字大小
),
# control_style_opts=opts.ControlStyleOpts(
# position='left', # 控制按钮位置:左侧
# item_size=20 # 控制按钮大小
# )
#其余种种样式见官网
)
接下来,开始导入数据了,我先放出完整代码:
# 遍历每个日期,创建地图并加入时间线
for date in dates:
df_date = df[df['date'] == date]
# 确保数据格式为 [(地区名, 数值), ...]
province_data = df_date[['spot', 'temp']].values.tolist()
# 数据格式检查
if all(isinstance(item, list) and len(item) == 2 for item in province_data):
china_map = (
Map(init_opts=opts.InitOpts(
#样式
width="2000px",
height="1000px",
bg_color='rgba(0,0,0,0)'
)
)
.add('温度', province_data, 'china', is_map_symbol_show=False, is_roam=True) #如果要使用世界地图,将‘china’改为‘world’即可
.set_series_opts(label_opts=opts.LabelOpts(is_show=False, color='#ffffff'))
.set_global_opts(
#全局样式在这里写
title_opts=opts.TitleOpts(
#标题样式
title=f"{date.strftime('%Y-%m-%d')}",
pos_left='center',
pos_right='center',
title_textstyle_opts=opts.TextStyleOpts(
#标题文本样式
color='#DDF3FF',
font_family='Times New Roman',
font_size='30px',
font_weight='bold',
)
),
legend_opts=opts.LegendOpts(
is_show=False #图例文字,显示出来非常乱,一般关闭
),
visualmap_opts=opts.VisualMapOpts(
#可视化核心部分,颜色随数据范围变化
is_piecewise=True, #True就是示例图那样分段,False就是连续的渐变
pos_left='280px',
pos_bottom='250px',
textstyle_opts=opts.TextStyleOpts(
color='#ffffff' # 设置字体颜色为白色
),
#温度-颜色对应关系,来自天气预报网站
pieces=[
{"max": 80, "min": 35, "label": ">35℃", "color": "#E00501"},
{"max": 34.99, "min": 32, "label": "32-35℃", "color": "#FA5702"},
{"max": 31.99, "min": 28, "label": "28-32℃", "color": "#FA9682"},
{"max": 27.99, "min": 24, "label": "24-28℃", "color": "#FFCCA1"},
{"max": 23.99, "min": 20, "label": "20-24℃", "color": "#FFF3C3"},
{"max": 19.99, "min": 16, "label": "16-20℃", "color": "#BAFB95"},
{"max": 15.99, "min": 12, "label": "12~16℃", "color": "#CEFFCE"},
{"max": 11.99, "min": 8, "label": "8~12℃", "color": "#D0FCFF"},
{"max": 7.99, "min": 4, "label": "4~8℃", "color": "#AAE9F8"},
{"max": 3.99, "min": 0, "label": "0~4℃", "color": "#85D0FF"},
{"max": -0.01, "min": -4, "label": "-4~0℃", "color": "#3E9FEC"},
{"max": -4.01, "min": -10, "label": "-10~-4℃", "color": "#2373D2"},
{"max": -10.01, "min": -20, "label": "-20~-10℃", "color": "#1C5D9E"},
{"max": -20.01, "min": -50, "label": "<-20℃", "color": "#002F87"},
]
),
)
)
# 将地图加入时间线
timeline.add(china_map, date.strftime('%Y-%m-%d'))#这就成了
else:
print(f"Data format error for date: {date.strftime('%Y-%m-%d')}")
最后,渲染为html文件,关闭数据库连接
# 渲染时间线图到 HTML 文件
timeline.render(path='AAA_pyecharts_map.html')
# 关闭数据库连接
cursor.close()
db.close()
四、运行结果
运行一下,没有报错
找到html文件并打开,我的数据量太大,没法直接打开,右键
前端部分我就不调了,朋友们自己设计。
完整代码:
import pymysql.cursors
import pandas as pd
import re
from pyecharts import options as opts
from pyecharts.charts import Timeline, Map
# 连接数据库
db = pymysql.connect(
host="localhost",
user="root",
password="root",
db="pyecharts_test",
charset="utf8",#这里即使之前选择为“utf8mb3”,也写utf8,这是在python中的代码
)
# 查询数据
sql = "SELECT * FROM China_temp_1 "
try:
cursor = db.cursor()
cursor.execute(sql)
result = cursor.fetchall()
except Exception as e:
db.rollback()
print("Error", e)
else:
db.commit()
print("Success")#输出Success即表示数据导入成功,方便以后debug
#debug代码块
# # 打印前 5 行数据
# print("前 5 行数据:")
# for i in range(min(5, len(result))):
# print(result[i])
# # 获取总行数
# total_rows = len(result)
# print(f"总共 {total_rows} 行数据")
# 创建 DataFrame
df = pd.DataFrame(result, columns=['date', 'spot', 'temp'])
print(df)
# 定义日期转换函数
def convert_date(date_str):
# 检查日期格式是否为“2020年1月1日”
match1 = re.match(r"(\d{4})年(\d{1,2})月(\d{1,2})日", date_str)
match2 = re.match(r"(\d{4})-(\d{1,2})-(\d{1,2})", date_str)
if match1:
year, month, day = match1.groups()
# 转换为“2020/1/1”格式
return f"{year}/{month}/{day}"
if match2:
year, month, day = match2.groups()
# 转换为“2020/1/1”格式
return f"{year}/{month}/{day}"
return date_str
# 应用日期转换函数
df['date'] = df['date'].apply(convert_date)
# 将日期列转换为 datetime 格式
df['date'] = pd.to_datetime(df['date'], format='%Y/%m/%d', errors='coerce')
# 去除重复记录
df = df.drop_duplicates(subset=['date', 'spot'], keep='first')
# 按照日期排序
df = df.sort_values(by='date')
# 获取唯一日期
dates = df['date'].dropna().unique() # 去掉 NaT
# 创建时间线图实例
timeline = Timeline(init_opts=opts.InitOpts(
theme='light',
bg_color='rgba(0,0,0,0)' # 设置页面背景颜色为完全透明
)
)
timeline.add_schema(
orient='horizontal', # 时间轴水平显示
is_auto_play=True, # 自动播放
play_interval=10, # 播放间隔时间,单位毫秒
label_opts=opts.LabelOpts(
is_show=True, # 显示标签
font_family='Arial', # 标签字体样式
rotate=-5, # 标签旋转角度
),
)
# 遍历每个日期,创建地图并加入时间线
for date in dates:
df_date = df[df['date'] == date]
# 确保数据格式为 [(地区名, 数值), ...]
province_data = df_date[['spot', 'temp']].values.tolist()
# 数据格式检查
if all(isinstance(item, list) and len(item) == 2 for item in province_data):
china_map = (
Map(init_opts=opts.InitOpts(
#样式
width="2000px",
height="1000px",
bg_color='rgba(0,0,0,0)'
)
)
.add('温度', province_data, 'china', is_map_symbol_show=False, is_roam=True) #如果要使用世界地图,将‘china’改为‘world’即可
.set_series_opts(label_opts=opts.LabelOpts(is_show=False, color='#ffffff'))
.set_global_opts(
#全局样式在这里写
title_opts=opts.TitleOpts(
#标题样式
title=f"{date.strftime('%Y-%m-%d')}",
pos_left='center',
pos_right='center',
title_textstyle_opts=opts.TextStyleOpts(
#标题文本样式
color='#DDF3FF',
font_family='Times New Roman',
font_size='30px',
font_weight='bold',
)
),
legend_opts=opts.LegendOpts(
is_show=False #图例文字,显示出来非常乱,一般关闭
),
visualmap_opts=opts.VisualMapOpts(
#可视化核心部分,颜色随数据范围变化
is_piecewise=True, #True就是示例图那样分段,False就是连续的渐变
pos_left='280px',
pos_bottom='250px',
textstyle_opts=opts.TextStyleOpts(
color='#ffffff' # 设置字体颜色为白色
),
#温度-颜色对应关系,来自天气预报网站
pieces=[
{"max": 80, "min": 35, "label": ">35℃", "color": "#E00501"},
{"max": 34.99, "min": 32, "label": "32-35℃", "color": "#FA5702"},
{"max": 31.99, "min": 28, "label": "28-32℃", "color": "#FA9682"},
{"max": 27.99, "min": 24, "label": "24-28℃", "color": "#FFCCA1"},
{"max": 23.99, "min": 20, "label": "20-24℃", "color": "#FFF3C3"},
{"max": 19.99, "min": 16, "label": "16-20℃", "color": "#BAFB95"},
{"max": 15.99, "min": 12, "label": "12~16℃", "color": "#CEFFCE"},
{"max": 11.99, "min": 8, "label": "8~12℃", "color": "#D0FCFF"},
{"max": 7.99, "min": 4, "label": "4~8℃", "color": "#AAE9F8"},
{"max": 3.99, "min": 0, "label": "0~4℃", "color": "#85D0FF"},
{"max": -0.01, "min": -4, "label": "-4~0℃", "color": "#3E9FEC"},
{"max": -4.01, "min": -10, "label": "-10~-4℃", "color": "#2373D2"},
{"max": -10.01, "min": -20, "label": "-20~-10℃", "color": "#1C5D9E"},
{"max": -20.01, "min": -50, "label": "<-20℃", "color": "#002F87"},
]
),
)
)
# 将地图加入时间线
timeline.add(china_map, date.strftime('%Y-%m-%d'))#这就成了
else:
print(f"Data format error for date: {date.strftime('%Y-%m-%d')}")
# 渲染时间线图到 HTML 文件
timeline.render(path='AAA_pyecharts_map.html')
# 关闭数据库连接
cursor.close()
db.close()