利用日历热力图可以方便的查看站点水质全年的变化情况。
这一步根据自己实际情况,也可以读取excel、MySQL读取数据。这里把API地址已隐去。
import numpy as np
import calendar
import requests
import json
import pandas as pd
import time
from datetime import date, datetime, timedelta
import matplotlib.pyplot as plt
from matplotlib.pylab import style # 自定义图表风格
# style.use('ggplot')
# 解决中文乱码问题
plt.rcParams['font.sans-serif'] = ['Simhei']# 解决坐标轴刻度负号乱码
plt.rcParams['axes.unicode_minus'] = False
connect_table =pd.read_excel("设备因子对照.xls")
# 根据时间、站点名称参数获取API url
def url_ask(beginTime,endTime,mn,period):
# beginTime 要拉取的数据开始时间节点(yyyy-MM-dd HH:mm:ss)
# endTime = "2023-10-30 00:00:00" #前闭后开
# mn 设备编码
# page当前页码
# 原始接口中包括page和pagesize 默认1和10000
# 这里需要注意 不宜太大,暂内存,二是如果查询内容过大则会超过1页,需要查询total page 字段的数字,并判断
# period 时间周期,实时为空,但要带这个请求参数,1小时为h1,4小时为h4,天数据为d1
page ="1"
pageSize ="10000"
mianurl = " "
ip_port = " "
url =ip_port + mianurl+"beginTime="+beginTime+"&endTime="+endTime +"&mn="+mn +"&page="+page +"&period=" +period +"&pageSize="+pageSize
# 发起API请求获取JSON数据
return(url)
# 根据站点名称求编码
def seak_mn(ask_station):
mn_text = connect_table.loc[connect_table['设备名称'] ==ask_station]
mn = mn_text.mn.unique()[0]
return(mn)
period = "h4"
ask_station = '湾凼'
mn = seak_mn(ask_station)
url =url_ask('2023-01-01 00:00:00','2023-11-20 00:00:00',mn,period)
# 根据URL 获取站点监测数据 未经降维
def mn_value(url):
response = requests.get(url)
response_data = response.json()
data =response_data["data"]
titles =data["titles"]
titles= pd.DataFrame(titles)
factor_code = titles["identifier"]
factor_name = titles["name"]
# 查询监测数据
df_value =data["items"]
df_value = pd.DataFrame(df_value,columns=factor_code)
df_value.columns= factor_name
df_value.rename(columns={'上报时间': '监测时间'}, inplace=True)
return(df_value)
df_value =mn_value(url)
df_value.tail()
输出数据结构如下:
选择 '氨氮', '高锰酸盐指数', '总磷',作为评价因子,以地表水评价导则判断各因子的评价等级,
再按照因子评价等级最高的最为站点评价等级。
df = df_value[['氨氮', '高锰酸盐指数', '总磷', '监测时间']]
# 数据清洗
df = df.replace(0, np.nan) # 将0替换为NaN
df = df.dropna(axis=0, how='any') # 删除包含NaN的行
# 将监测时间转换为日期,并计算日平均值
df['监测时间'] = pd.to_datetime(df['监测时间'])
df['日期'] = df['监测时间'].dt.date
df = df.groupby('日期').mean().reset_index()
# 数据评价
def evaluate_index(value, limits):
for i, limit in enumerate(limits):
if value <= limit:
return i + 1
return len(limits) + 1
# 设置各因子的标准限值
limits = {
'高锰酸盐指数': [2, 4, 6, 10, 15],
'氨氮': [0.15, 0.5, 1, 1.5, 2.0],
'总磷': [0.02, 0.1, 0.2, 0.3, 0.4]
}
# 计算评价等级
for factor, limit in limits.items():
df[f'{factor}评价等级'] = df[factor].apply(lambda x: evaluate_index(x, limit))
# 计算站点评价等级
df['站点评价等级'] = df[['高锰酸盐指数评价等级', '氨氮评价等级', '总磷评价等级']].max(axis=1)
最后的结果如下图
通过简单查询可以知道这个点位的等级范围为2-6类(Ⅱ类到劣Ⅴ类)
import pandas as pd
import calmap
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
# 将日期列转换为日期类型
df['日期'] = pd.to_datetime(df['日期'])
# 确保站点评价等级是数值类型
df['站点评价等级'] = pd.to_numeric(df['站点评价等级'])
# 创建日历热力图
fig, ax = plt.subplots(figsize=(20, 6), dpi=300)
calmap.yearplot(df.set_index('日期')['站点评价等级'], year=2023, cmap='RdYlGn_r', linewidth=0.01, linecolor='grey', monthticks=5, monthlabels='none')
# 添加颜色条
cmap = plt.cm.get_cmap('RdYlGn_r', 6) # 选择颜色映射,这里假设我们将颜色分为7个区间
bounds = [0.5, 1.5, 2.5, 3.5, 4.5, 5.5] # 设置颜色条的分割点
norm = plt.cm.colors.BoundaryNorm(bounds, cmap.N) # 根据分割点创建颜色映射
sm = plt.cm.ScalarMappable(cmap=cmap, norm=norm)
sm.set_array([])
cbar = plt.colorbar(sm, orientation='horizontal', ticks=[1, 2, 3, 4, 5], shrink=0.4) # 设置颜色条的标记位置
cbar.ax.set_xticklabels(['Ⅱ类', 'Ⅲ类', 'Ⅳ类', 'Ⅴ类', '劣Ⅴ类'])
cbar.set_label('水质等级', fontsize=14)
# 自定义月份显示为中文
plt.gca().set_xticklabels(['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'])
# 调整图形布局
plt.subplots_adjust(left=0.05, right=0.95, top=0.95, bottom=0.2)
# 设置图形标题
label_name = ask_station + "2023年水质等级日历热力图"
plt.title(label_name, fontsize=16)
# 保存图形到本地
# plt.savefig("calendar_heatmap.png")
# 显示图形
plt.show()
缺点比较明显 不能交互,且月份之间间隔不明显。
代码更简洁,效果还可以,输出格式为html,可交互,逻辑更清晰。
import pandas as pd
from pyecharts import options as opts
from pyecharts.charts import Calendar
# 将日期列转换为日期类型
df['日期'] = pd.to_datetime(df['日期'])
# 确保站点评价等级是数值类型
df['站点评价等级'] = pd.to_numeric(df['站点评价等级'])
# 获取日期和评价等级数据
data = df[['日期', '站点评价等级']].values.tolist()
# 创建日历热力图
calendar = (
Calendar()
.add("", data, calendar_opts=opts.CalendarOpts(range_='2023'))
.set_global_opts(
title_opts=opts.TitleOpts(title=ask_station + "2023年水质等级日历热力图"),
visualmap_opts=opts.VisualMapOpts(
max_=5,
min_=1,
orient="horizontal",
pos_top="230px",
pos_left="center",
is_piecewise=True,
pieces=[
{"min": 1, "max": 2, "label": "Ⅱ类", "color": "#00E500"},
{"min": 2, "max": 3, "label": "Ⅲ类", "color": "blue"},
{"min": 3, "max": 4, "label": "Ⅳ类", "color": "#FFA500"},
{"min": 4, "max": 5, "label": "Ⅴ类", "color": "#FF0000"},
{"min": 5, "max": 6, "label": "劣Ⅴ类", "color": "#800080"},
],
),
)
.render("calendar_heatmap.html")
)
# 还是有改进的地方,比如根据等级的上下限自动匹配色块
从图中可以看到该站点4-10月水质较差,其他月份水质为Ⅲ类及以下。