利用日历热力图可以方便的查看站点水质全年的变化情况。
这一步根据自己实际情况,也可以读取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月水质较差,其他月份水质为Ⅲ类及以下。