python实现水质日历热力图

目的

使用 python 将站点水质等级以日历图形式可视化。实现快速掌握站点长期水质状况及时间变化趋势。

成果预览

image.png

功能特点

  1. 查询站点、任意时间段水质等级。
  2. 可自定义参与评价的因子
  3. 日历图有有图例,支持鼠标交互显示时间和水质等级

实现路径

image.png

环境及依赖

还是利用 python streamlit 框架,日历图需要依赖包为 pyecharts、streamlit_echarts。

1
2
3
4
python = 3.12.0
streamlit==1.38.0
streamlit-echarts==0.4.0
pyecharts==2.0.6

代码

数据查询

根据自身情况,用数据库或者 Excel 都可以,只要有监测时间和监测因子就行。数据示例如下。
image.png

等级判断

evaluate_df(df, limits, standard_map) 函数根据以水质监测数据的 DataFrame、包含各因子评价限值的字典、包含评价等级与数值对应关系的字典来获得每个因子的数据等级。程序中加入了异常判断。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
def evaluate_df(df, limits, standard_map):  
"""
评价DataFrame中的水质数据,并返回一个新的带有评价等级的DataFrame。

参数:
df: DataFrame,包含水质监测数据的DataFrame,其中第一列为'监测时间',其他列为不同因子的监测值。
limits: dict,包含各因子评价限值的字典,用于确定各因子的评价等级。
standard_map: dict,包含评价等级与数值对应关系的字典,用于将评价等级映射为数值。

返回:
DataFrame,包含原始数据和各因子评价等级、站点等级的新DataFrame。
""" # 在原有 DataFrame 上添加新列
result_df = df.copy() # 复制原有 DataFrame 以保留原数据

# 遍历df中的每一行
for index, row in df.iterrows():
# 初始化最大等级为未定义
max_level = standard_map["未定义"]
# 遍历每一列,除了'监测时间'
for column in df.columns[1:]:
factor = column # 获取当前列的因子名称

# 检查是否为空值
if pd.isna(row[column]):
result_df.at[index, f'{factor}评价等级'] = standard_map["空值"] # 标记为空值
continue

if column in limits: # 如果列存在于limits中
level = water_quality_levels(column, row[column], limits[column])
numeric_level = standard_map[level]
result_df.at[index, f'{factor}评价等级'] = numeric_level # 添加新列存储等级
# 更新最大等级
max_level = max(max_level, numeric_level)
else:
# 如果列不在limits中,标记为未定义
result_df.at[index, f'{factor}评价等级'] = standard_map["未定义"]

# 计算站点等级并添加到新的DataFrame
result_df.at[index, '站点等级'] = max_level
return result_df

评价限值limits

这个之前有过介绍,是根据《地表水环境质量标准》(GB 3838-2002)要求建立的字典。如果需要自定义 limits 可以通过多选查询对应的限值来替换入参。

1
2
3
4
5
6
7
8
9
10
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],
"化学需氧量": [15, 15, 20, 30, 40],
"五日生化需氧量": [3, 3, 4, 6, 10],
"氟化物": [1, 1, 1, 1.5, 1.5],
"pH": [6, 9], # pH的特殊处理,下限为6,上限为9
"溶解氧": [7.5, 6, 5, 3, 2] # 溶解氧的特殊处理,只有下限
}

standard_map

评价等级与数值对应关系的字典

1
standard_map = {"Ⅰ": 1, "Ⅱ": 2, "Ⅲ": 3, "Ⅳ": 4, "Ⅴ": 5, "劣Ⅴ": 6, "未定义": -1, "空值": -2}

日历图

为了在 streamlit 实现日历图,尝试了很多依赖,streamlit 自身支持的较好的 plotly 没有日历图,matplotlib、camlp 都是静态的,不好看。最终选择了第三方包 pyecharts 绘制,streamlit_echarts 展示。注意最后一定要用 st_pyecharts () 来展示,才能在 web 中很好的显示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
from pyecharts import options as opts  
from pyecharts.charts import Calendar
from streamlit_echarts import st_pyecharts
import streamlit as st


def generate_calendar(df):
"""
使用pyecharts 和streamlit_echarts生成水质日历热力图。
参数:
df (DataFrame): 包含水质数据的Pandas DataFrame,必须包括水质等级和日期(或索引作为日期)。
返回:
calendar_chart (Chart object): 生成的日历热力图对象。
""" # 检查是否有“监测时间”列,如果没有则使用索引作为日期
# 获取年份用于标题
year = df['监测时间'].dt.year.unique()[0]
df['监测时间'] = df['监测时间'].dt.strftime('%Y-%m-%d')
# 确保水质等级是数值类型
df['站点等级'] = pd.to_numeric(df['站点等级'])
# 获取日期和水质等级数据
data = df[['监测时间', '站点等级']].values.tolist()
# 创建日历热力图
calendar_chart = (
Calendar().add("", data, calendar_opts=opts.CalendarOpts(range_=str(year)),
daylabel_opts=opts.CalendarDayLabelOpts(name_map="cn"),
monthlabel_opts=opts.CalendarMonthLabelOpts(name_map="cn"),
tooltip_opts=opts.TooltipOpts(
trigger="item",
# 自定义提示框格式化器
formatter=JsCode("""
function(params) { return params.name + params.value; } """),
))
.set_global_opts(
title_opts=opts.TitleOpts(title=f" {year} 年日历热力图", pos_left="center")
,
visualmap_opts=opts.VisualMapOpts(
max_=6,
min_=1,
orient="horizontal",
pos_top="230px",
pos_left="center",
is_piecewise=True,
pieces=[
{"min": 1, "max": 1, "label": "Ⅰ类", "color": "#0000FF"},
{"min": 2, "max": 2, "label": "Ⅱ类", "color": "#1E90FF"},
{"min": 3, "max": 3, "label": "Ⅲ类", "color": "#32CD32"},
{"min": 4, "max": 4, "label": "Ⅳ类", "color": "#FFD700"},
{"min": 5, "max": 5, "label": "Ⅴ类", "color": "#FFA500"},
{"min": 6, "max": 6, "label": "劣Ⅴ类", "color": "#FF0000"},
],
),
)
)
return calendar_chart
hour_pic = generate_calendar(df_day)
st_pyecharts(hour_pic)

参考文件

Streamlit ECharts Demo · Streamlit
Calendar - Calendar_label_setting - Document (pyecharts.org)
pyecharts/pyecharts-gallery: Just use pyecharts to imitate Echarts official example. (github.com)
andfanilo/streamlit-echarts: A Streamlit component to render ECharts. (github.com)
Examples - Apache ECharts

BY

纯个人经验,如有帮助,请收藏点赞,如需转载,请注明出处。
微信公众号:环境猫 er
CSDN : 细节处有神明
个人博客: https://maoyu92.github.io/


python实现水质日历热力图
https://maoyu92.github.io/2024/09/05/04 经验分享/python实现水质日历热力图/
作者
陈文茂
发布于
2024年9月5日
许可协议