测试修改
This commit is contained in:
@ -35,11 +35,12 @@ import pandas as pd
|
||||
|
||||
|
||||
class _SimpleProgress:
|
||||
"""无依赖进度条(控制台单行刷新)。"""
|
||||
"""无依赖进度条(控制台单行刷新,支持 Qt 回调上递)。"""
|
||||
|
||||
def __init__(self, total: int, desc: str = ""):
|
||||
def __init__(self, total: int, desc: str = "", on_step=None):
|
||||
self.total = max(1, int(total))
|
||||
self.desc = desc
|
||||
self.on_step = on_step
|
||||
self.n = 0
|
||||
self._render()
|
||||
|
||||
@ -47,6 +48,10 @@ class _SimpleProgress:
|
||||
self.n = min(self.total, self.n + int(step))
|
||||
self._render()
|
||||
|
||||
def set_description(self, text: str):
|
||||
"""动态更新进度描述文案(用于按段切换分析对象)。"""
|
||||
self.desc = text
|
||||
|
||||
def close(self):
|
||||
# 换行,避免覆盖后续输出
|
||||
print()
|
||||
@ -58,6 +63,11 @@ class _SimpleProgress:
|
||||
bar = "█" * filled + "·" * (bar_len - filled)
|
||||
prefix = f"{self.desc} " if self.desc else ""
|
||||
print(f"\r{prefix}[{bar}] {self.n}/{self.total} ({pct}%)", end="", flush=True)
|
||||
if self.on_step:
|
||||
try:
|
||||
self.on_step(pct, self.desc)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
@dataclass
|
||||
@ -210,98 +220,17 @@ class WaterQualityReportGenerator:
|
||||
}
|
||||
|
||||
# 每个参数对应的图片顺序(统一5张图模式)
|
||||
params_list = ["Chlorophyll", "COD", "DO", "PH", "Temperature",
|
||||
"spCond", "Turbidity", "TDS", "Cl-", "NO3-N",
|
||||
"NH3-N", "BGA", "TT"]
|
||||
self.parameter_images = {
|
||||
"Chlorophyll": [
|
||||
"Chlorophyll_histogram.png",
|
||||
"Chlorophyll_spectrum_comparison.png",
|
||||
"Chlorophyll_scatter_with_confidence.png",
|
||||
"Chlorophyll_boxplot.png",
|
||||
"Chlorophyll_distribution.png"
|
||||
],
|
||||
"COD": [
|
||||
"COD_histogram.png",
|
||||
"COD_spectrum_comparison.png",
|
||||
"COD_scatter_with_confidence.png",
|
||||
"COD_boxplot.png",
|
||||
"COD_distribution.png"
|
||||
],
|
||||
"DO": [
|
||||
"DO_histogram.png",
|
||||
"DO_spectrum_comparison.png",
|
||||
"DO_scatter_with_confidence.png",
|
||||
"DO_boxplot.png",
|
||||
"DO_distribution.png"
|
||||
],
|
||||
"PH": [
|
||||
"PH_histogram.png",
|
||||
"PH_spectrum_comparison.png",
|
||||
"PH_scatter_with_confidence.png",
|
||||
"PH_boxplot.png",
|
||||
"PH_distribution.png"
|
||||
],
|
||||
"Temperature": [
|
||||
"Temperature_histogram.png",
|
||||
"Temperature_spectrum_comparison.png",
|
||||
"Temperature_scatter_with_confidence.png",
|
||||
"Temperature_boxplot.png",
|
||||
"Temperature_distribution.png"
|
||||
],
|
||||
"spCond": [
|
||||
"spCond_histogram.png",
|
||||
"spCond_spectrum_comparison.png",
|
||||
"spCond_scatter_with_confidence.png",
|
||||
"spCond_boxplot.png",
|
||||
"spCond_distribution.png"
|
||||
],
|
||||
"Turbidity": [
|
||||
"Turbidity_histogram.png",
|
||||
"Turbidity_spectrum_comparison.png",
|
||||
"Turbidity_scatter_with_confidence.png",
|
||||
"Turbidity_boxplot.png",
|
||||
"Turbidity_distribution.png"
|
||||
],
|
||||
"TDS": [
|
||||
"TDS_histogram.png",
|
||||
"TDS_spectrum_comparison.png",
|
||||
"TDS_scatter_with_confidence.png",
|
||||
"TDS_boxplot.png",
|
||||
"TDS_distribution.png"
|
||||
],
|
||||
"Cl-": [
|
||||
"Cl-histogram.png",
|
||||
"Cl-spectrum_comparison.png",
|
||||
"Cl-scatter_with_confidence.png",
|
||||
"Cl-boxplot.png",
|
||||
"Cl-distribution.png"
|
||||
],
|
||||
"NO3-N": [
|
||||
"NO3-N_histogram.png",
|
||||
"NO3-N_spectrum_comparison.png",
|
||||
"NO3-N_scatter_with_confidence.png",
|
||||
"NO3-N_boxplot.png",
|
||||
"NO3-N_distribution.png"
|
||||
],
|
||||
"NH3-N": [
|
||||
"NH3-N_histogram.png",
|
||||
"NH3-N_spectrum_comparison.png",
|
||||
"NH3-N_scatter_with_confidence.png",
|
||||
"NH3-N_boxplot.png",
|
||||
"NH3-N_distribution.png"
|
||||
],
|
||||
"BGA": [
|
||||
"BGA_histogram.png",
|
||||
"BGA_spectrum_comparison.png",
|
||||
"BGA_scatter_with_confidence.png",
|
||||
"BGA_boxplot.png",
|
||||
"BGA_distribution.png"
|
||||
],
|
||||
"TT": [
|
||||
"TT_histogram.png",
|
||||
"TT_spectrum_comparison.png",
|
||||
"TT_scatter_with_confidence.png",
|
||||
"TT_boxplot.png",
|
||||
"TT_distribution.png"
|
||||
]
|
||||
param: [
|
||||
f"{param}_histogram.png",
|
||||
f"{param}_spectrum_comparison.png",
|
||||
f"{param}_scatter_true_vs_pred.png",
|
||||
f"{param}_boxplot.png",
|
||||
f"{param}_distribution_enhanced.png"
|
||||
] for param in params_list
|
||||
}
|
||||
|
||||
def apply_ai_config(self, ai_config: ReportGenerationConfig) -> None:
|
||||
@ -397,6 +326,7 @@ class WaterQualityReportGenerator:
|
||||
|
||||
payload: Dict[str, Any] = {
|
||||
"model": self.minimax_text_model,
|
||||
"max_tokens": 4096,
|
||||
"messages": [
|
||||
{"role": "system", "content": system_prompt},
|
||||
{"role": "user", "content": user_prompt},
|
||||
@ -447,6 +377,7 @@ class WaterQualityReportGenerator:
|
||||
|
||||
payload: Dict[str, Any] = {
|
||||
"model": self.minimax_vision_model,
|
||||
"max_tokens": 4096,
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
@ -518,123 +449,90 @@ class WaterQualityReportGenerator:
|
||||
return self._ollama_chat(model, system_prompt, user_prompt, image_path)
|
||||
|
||||
def _get_prompt_for_image(self, image_type: str, param: str, figure_num: int) -> Dict[str, str]:
|
||||
"""按图片类型返回 system/user 提示词,带防幻觉约束。"""
|
||||
"""按图片类型返回 system/user 提示词,注入水质遥感专家级约束。"""
|
||||
system = (
|
||||
"你是一位水质遥感与机器学习建模专家。\n"
|
||||
"研究背景:我们利用高光谱影像数据,结合机器学习算法对研究区的水质参数进行了空间反演,并生成了以下图表。"
|
||||
"现需要撰写自动化分析报告,请严格按照“图表类型→分析重点”的对应关系进行描述。\n\n"
|
||||
"分析要求:\n"
|
||||
"1. 请严格基于图片中可见信息进行分析,禁止编造不存在的数值、区域名称、采样时间或结论。\n"
|
||||
"2. 如果图片无法支撑某项判断,必须明确写“根据本图无法判断”。\n"
|
||||
"3. 不允许引用图片之外的背景知识来补全细节。"
|
||||
"你是一位资深的水环境遥感与水生态学专家。现需为一份高光谱水质参数反演报告撰写专业分析。\n"
|
||||
"【绝对禁忌】:严禁写“看图说话”式的废话(如“曲线先升后降”、“柱子集中在中间”)。\n"
|
||||
"【核心规范】:\n"
|
||||
"1. 必须结合【水色光学机理】和【水环境地学意义】进行解释。\n"
|
||||
"2. 提及波长时,必须解释其对应的物理/生化意义(如叶绿素红光吸收谷、悬浮物散射峰、水体吸收特性等)。\n"
|
||||
"3. 分析浓度数值时,必须结合自然水体的常规背景值或富营养化状态进行定性评价(如“处于清洁水平”或“存在水华风险”)。\n"
|
||||
"4. 严格基于图中可见的规律,不编造图中没有的具体坐标或日期。"
|
||||
)
|
||||
|
||||
# 为每种图表类型单独定义:分析要点 + 结论聚焦
|
||||
type_specs = {
|
||||
"histogram": {
|
||||
"analysis": (
|
||||
"分析要点:\n"
|
||||
"- 分布形态:是左偏、右偏还是对称?是否存在多峰?\n"
|
||||
"- 集中范围:数据主要集中在哪个区间?(参照横轴和纵轴柱高)\n"
|
||||
"- 离群值:是否有明显孤立于主体分布的小柱,位于何处?\n"
|
||||
"- 若图中包含拟合曲线,描述其形状(正态、指数等)。"
|
||||
),
|
||||
"conclusion": (
|
||||
"结论应聚焦于:该参数的分布形态(如左偏/右偏/对称)、主要集中区间、是否存在极端离群值。"
|
||||
"用一句话概括数据分布的核心特征,不推测成因。"
|
||||
f"- 结合自然水体中 {param} 的常规阈值,评估该水域当前的整体水平(清洁、轻度污染或富营养化)。\n"
|
||||
"- 从生态学角度解释这种数值分布形态(如多峰分布可能暗示存在多个不同性质的污染源或水团交汇)。\n"
|
||||
"- 关注极端离群值,指出其可能代表的局部异常环境事件。"
|
||||
),
|
||||
"conclusion": "结论应聚焦:该水质参数的整体健康水平评估及主要生态风险提示。",
|
||||
},
|
||||
"spectrum_comparison": {
|
||||
"analysis": (
|
||||
"分析要点:\n"
|
||||
"- 多条曲线的整体趋势是否一致?\n"
|
||||
"- 在哪些波段(参照横轴波长位置)出现明显分离?\n"
|
||||
"- 是否存在系统性的整体偏移(一条曲线全程高于另一条)?\n"
|
||||
"- 图中是否有阴影或误差带表示置信区间?若有,描述其范围。"
|
||||
),
|
||||
"conclusion": (
|
||||
"结论应聚焦于:各光谱曲线的整体一致性、关键差异波段、是否存在系统性偏移。"
|
||||
"用一句话概括光谱对比的主要特征,不推测物理原因。"
|
||||
f"- 结合 {param} 的固有光学特性,重点分析400-900nm区间内的特征波段响应(如吸收谷、反射峰、双峰效应等)。\n"
|
||||
"- 对比不同浓度组别的光谱差异,说明浓度变化是如何改变水体对光吸收和后向散射规律的。\n"
|
||||
"- 指出对该参数反演最具区分度的关键波段区间,验证模型的物理可解释性。"
|
||||
),
|
||||
"conclusion": "结论应聚焦:浓度梯度引起的光谱响应规律及其对应的光学机制验证。",
|
||||
},
|
||||
"scatter_with_confidence": {
|
||||
"analysis": (
|
||||
"分析要点:\n"
|
||||
"- 点云整体是否沿1:1线(对角线)分布?\n"
|
||||
"- 点云在低值区/高值区是否存在系统性偏离(如整体偏上/偏下)?\n"
|
||||
"- 置信带(若存在)覆盖了多少点?是否所有点都在置信带内?\n"
|
||||
"- 是否有明显离群点(远离主体点云)?"
|
||||
),
|
||||
"conclusion": (
|
||||
"结论应聚焦于:模型预测精度(点云与1:1线贴合程度)、偏差方向、置信带覆盖情况。"
|
||||
"用一句话评价模型性能,不推测误差来源。"
|
||||
"- 评估机器学习反演模型在该参数上的鲁棒性。点云对1:1线的贴合度反映了反演精度。\n"
|
||||
"- 重点分析在极低值区或极高值区是否存在系统性高估/低估(这是水色遥感的常见难点,如高浓度下的光谱饱和效应)。\n"
|
||||
"- 结合置信带宽度,说明模型在不同浓度区间的预测不确定性。"
|
||||
),
|
||||
"conclusion": "结论应聚焦:反演模型的整体精度表现、局限性及可靠的浓度预测区间。",
|
||||
},
|
||||
"boxplot": {
|
||||
"analysis": (
|
||||
"分析要点:\n"
|
||||
"- 中位数(箱体中间线)的位置。\n"
|
||||
"- 四分位间距(箱体高度)反映的离散程度。\n"
|
||||
"- 须(whisker)的长度,是否超出1.5倍IQR的离群点(用圆点/星号标示)。\n"
|
||||
"- 若多个箱线图并排,比较各组的中心趋势和离散度。"
|
||||
),
|
||||
"conclusion": (
|
||||
"结论应聚焦于:各组的中心趋势(中位数)、离散程度(四分位距)、是否存在离群点。"
|
||||
"用一句话概括数据分布的统计特征,若有多组则简述对比。"
|
||||
"- 结合中位数和四分位距,分析不同类别(或区域)间水质差异的显著性。\n"
|
||||
"- 解释离散程度大(箱体长)可能代表的强烈时空异质性。\n"
|
||||
"- 指出箱线图上下的离群点,探讨其作为局部水质突变信号的价值。"
|
||||
),
|
||||
"conclusion": "结论应聚焦:核心对比趋势及数据整体的时空变异特征。",
|
||||
},
|
||||
"distribution": {
|
||||
"analysis": (
|
||||
"分析要点:\n"
|
||||
"- 高值区域:位于图中的哪个方位(如东北部、中部偏西、东南沿岸等)?呈现何种形状(斑块状、条带状、片状)?\n"
|
||||
"- 低值区域:位置及形态。\n"
|
||||
"- 梯度变化:是否存在明显的从某方位向另一方位递减或递增的趋势?\n"
|
||||
"- 聚集特征:高值区是否成片聚集,还是零星散布?\n"
|
||||
"注意:仅使用方位描述位置(如上、下、左、右、中心、边缘、沿岸等),禁止使用具体经纬度坐标或地名。"
|
||||
),
|
||||
"conclusion": (
|
||||
"结论应聚焦于:高值区与低值区的空间方位、聚集形态、主要梯度方向。"
|
||||
"用一句话概括空间分布格局,不推测污染源或成因。"
|
||||
f"- 分析 {param} 高值区与低值区的空间异质性特征。\n"
|
||||
"- 推断污染/物质来源类型:高值区呈斑块状/点状(通常提示点源排放或局部水华),还是呈沿岸带状/梯度扩散(通常提示面源径流或水动力扩散)。\n"
|
||||
"- 结合常见水动力学特征,简述物质可能的输移趋势。"
|
||||
),
|
||||
"conclusion": "结论应聚焦:水质参数的空间格局特征及其指示的宏观环境动力学过程。",
|
||||
},
|
||||
"correlation_heatmap": {
|
||||
"analysis": (
|
||||
"分析要点:\n"
|
||||
"- 各变量对之间的相关性强度:颜色深浅对应的相关系数大小(参照图例)。\n"
|
||||
"- 正相关与负相关:红色/蓝色分别代表正负(根据图例),描述主要的高正相关对和高负相关对。\n"
|
||||
"- 若图中包含数值标注,可提及范围(如“大多数相关系数介于0.6~0.8”),但不得编造具体数字。\n"
|
||||
"- 若单元格颜色过于接近难以区分,则写“根据本图无法判断具体相关性强弱”。"
|
||||
),
|
||||
"conclusion": (
|
||||
"结论应聚焦于:变量间相关性的整体强弱水平、最主要的正负相关对。"
|
||||
"用一句话概括相关性矩阵的核心特征,不推测因果关系。"
|
||||
"- 挖掘关键水质参数间的生物地球化学联系。如叶绿素与总氮/总磷的正相关提示营养盐驱动,与浊度的正相关提示藻类为主导的悬浮物等。\n"
|
||||
"- 识别拮抗作用(强负相关),并解释其潜在的生化机制(如高浊度遮蔽光照导致叶绿素降低)。\n"
|
||||
"- 基于相关性聚类,推断水体中的核心主导污染因子群。"
|
||||
),
|
||||
"conclusion": "结论应聚焦:水质指标间的核心协同/拮抗机制及水环境的主要驱动力。",
|
||||
},
|
||||
}
|
||||
|
||||
# 默认规格(如果类型未定义)
|
||||
default_spec = {
|
||||
"analysis": "重点:概括图中主要信息,列出可见的轴标签、图例、数据特征。",
|
||||
"conclusion": "结论应基于可见信息,概括图中主要趋势或数据特征,不添加外部知识。",
|
||||
"analysis": "结合水环境遥感原理,深入解读图中展现的数据分布或空间格局特征。",
|
||||
"conclusion": "结论应聚焦:该图表传递的核心水质遥感科学结论。",
|
||||
}
|
||||
|
||||
spec = type_specs.get(image_type, default_spec)
|
||||
analysis_part = spec["analysis"]
|
||||
conclusion_part = spec["conclusion"]
|
||||
|
||||
common_output = (
|
||||
"输出格式:\n"
|
||||
"请结合坐标轴、图例、曲线、点云、颜色条等可见元素,描述数据特征(如分布形态、对比关系、空间位置等),引用图中具体元素但不编造数值。"
|
||||
"随后用一句话总结该图揭示的主要趋势或数据质量。总结必须严格基于前文描述的可见信息,不得引入图中未呈现的外部知识、推测原因或隐含假设。"
|
||||
"若信息不足以得出明确结论,则写“根据本图无法得出明确结论”。"
|
||||
"要求:直接输出分析内容,不要使用“第一段”“第二段”等标记,两段之间不要留空行。")
|
||||
|
||||
|
||||
user = (
|
||||
f"图号:图{figure_num}\n"
|
||||
f"参数:{param}\n"
|
||||
f"图类型:{image_type}\n\n"
|
||||
f"{analysis_part}\n\n"
|
||||
f"{common_output}"
|
||||
f"当前分析参数:{param}\n"
|
||||
f"图表类型:{image_type}\n\n"
|
||||
"【专业要求】:\n"
|
||||
f"{spec['analysis']}\n\n"
|
||||
"【输出格式】:\n"
|
||||
"直接输出一段(不要分段)150~300字的专业分析。前半部分描述关键数据现象并深挖其光学或生态机制,最后用一句“总之,…”作为全文的科学性总结。\n"
|
||||
f"【最终落脚点要求】:{spec['conclusion']}\n"
|
||||
)
|
||||
return {"system": system, "user": user}
|
||||
|
||||
@ -735,13 +633,20 @@ class WaterQualityReportGenerator:
|
||||
self._save_ai_cache(cache)
|
||||
return text
|
||||
|
||||
def _create_progress(self, total: int, desc: str = "进度"):
|
||||
"""创建进度条:优先 tqdm,否则使用简单进度条。"""
|
||||
def _create_progress(self, total: int, desc: str = "进度", on_step=None):
|
||||
"""创建进度条:优先 tqdm,否则使用简单进度条。
|
||||
|
||||
Args:
|
||||
on_step: 可选回调,签名 on_step(percent: int, text: str)。用于驱动 Qt QProgressBar / QThread 进度信号。
|
||||
"""
|
||||
# 如果有 UI 回调需求,强制使用自带的 _SimpleProgress,防止 tqdm 吞掉信号
|
||||
if on_step is not None:
|
||||
return _SimpleProgress(total=total, desc=desc, on_step=on_step)
|
||||
try:
|
||||
from tqdm import tqdm # type: ignore
|
||||
return tqdm(total=total, desc=desc, unit="步", ncols=90)
|
||||
except Exception:
|
||||
return _SimpleProgress(total=total, desc=desc)
|
||||
return _SimpleProgress(total=total, desc=desc, on_step=on_step)
|
||||
|
||||
def _analyze_statistics(self, stats_data: List[Dict[str, Any]], param_names: List[str]) -> str:
|
||||
"""对水质参数统计数据进行 AI 分析"""
|
||||
@ -768,14 +673,19 @@ class WaterQualityReportGenerator:
|
||||
return self._ai_chat(self.ollama_text_model, system, user, image_path=None)
|
||||
|
||||
|
||||
def generate_report(self,
|
||||
def generate_report(self,
|
||||
work_dir: str = None,
|
||||
parameters: List[str] = None,
|
||||
report_title: str = "水质参数反演分析报告",
|
||||
output_path: Optional[str] = None) -> str:
|
||||
output_path: Optional[str] = None,
|
||||
on_progress=None) -> str:
|
||||
"""
|
||||
生成 Word 报告 - 所有数据均来自工作目录(work_dir)
|
||||
可视化图片、统计数据等均从 work_dir/14_visualization 和 work_dir/4_processed_data 中读取
|
||||
|
||||
Args:
|
||||
on_progress: 可选回调,签名 on_progress(percent: int, text: str)。
|
||||
会在进度更新时被调用,用于驱动 Qt QProgressBar/QThread 信号。
|
||||
"""
|
||||
# 设置工作目录(整个流程的核心)
|
||||
if work_dir is not None:
|
||||
@ -805,7 +715,7 @@ class WaterQualityReportGenerator:
|
||||
# 进度条(按“图片处理 + 汇总”计步)
|
||||
total_images = sum(len(self.parameter_images.get(p, [])) for p in parameters)
|
||||
total_steps = total_images + 1 + 1 # +1 相关性热力图(尝试一次),+1 综合总结
|
||||
progress = self._create_progress(total=total_steps, desc="生成Word报告")
|
||||
progress = self._create_progress(total=total_steps, desc="生成Word报告", on_step=on_progress)
|
||||
|
||||
# 创建文档
|
||||
doc = Document()
|
||||
@ -847,6 +757,7 @@ class WaterQualityReportGenerator:
|
||||
base_section_num = 5
|
||||
last_param_section_num = base_section_num + len(parameters) - 1
|
||||
for section_num, param in enumerate(parameters, base_section_num):
|
||||
progress.set_description(f"正在分析 {param} 数据 ({section_num - base_section_num + 1}/{len(parameters)})")
|
||||
figure_counter = self._add_parameter_section(
|
||||
doc,
|
||||
param,
|
||||
@ -873,15 +784,19 @@ class WaterQualityReportGenerator:
|
||||
]
|
||||
)
|
||||
system = (
|
||||
"你是一位水质遥感与报告撰写专家。"
|
||||
"只能基于提供的“逐图分析文本”做总结,禁止引入任何外部事实或猜测。"
|
||||
"若信息不足,必须明确说明“根据现有分析无法判断”。"
|
||||
"你是一位水环境管理决策专家与遥感首席科学家。现需根据前面生成的各参数逐图分析文本,提炼出一份执行摘要级别的综合结论。\n"
|
||||
"必须具备宏观视角,能够将离散的参数分析整合成对该水域整体健康状况的系统性诊断。"
|
||||
)
|
||||
user = (
|
||||
"以下是逐图分析文本,请给出报告级别的综合总结,要求:\n"
|
||||
"- 150~300字中文\n"
|
||||
"- 结构:总体概况 / 主要异常或热点 / 参数间关系(如有)/ 建议关注点\n"
|
||||
"- 不要编造具体数值、地名、日期\n\n"
|
||||
"以下是各个水质参数的详尽逐图分析文本,请基于此撰写一份最终的综合分析总结。\n"
|
||||
"【内容结构需包含】:\n"
|
||||
"1. 整体水质评估(如营养状态、主要污染程度)。\n"
|
||||
"2. 关键时空热点与驱动因子(最需关注的高值区域及核心主导参数)。\n"
|
||||
"3. 遥感反演模型可靠性综合评价。\n"
|
||||
"4. 宏观水环境管理与保护建议。\n"
|
||||
"【⚠️强制要求】:\n"
|
||||
"- 总结的字数必须严格控制在 300 到 450 字之间!\n"
|
||||
"- 必须输出完整的结尾标点符号,绝不允许出现话说一半突然截断的情况!高度精炼,切勿啰嗦。\n\n"
|
||||
f"{analyses_text}"
|
||||
)
|
||||
summary_text = self._ai_chat(self.ollama_text_model, system, user, image_path=None)
|
||||
@ -959,30 +874,47 @@ class WaterQualityReportGenerator:
|
||||
for i, img_name in enumerate(image_list):
|
||||
figure_num = start_figure_num + i
|
||||
|
||||
# 选择子文件夹
|
||||
|
||||
# 选择子文件夹与动态寻址
|
||||
if "boxplot" in img_name.lower():
|
||||
sub_dir = vis_dir / "boxplots"
|
||||
title_key = "boxplot"
|
||||
elif "scatter" in img_name.lower() or "confidence" in img_name.lower():
|
||||
sub_dir = vis_dir / "scatter_plots"
|
||||
title_key = "scatter_with_confidence"
|
||||
elif "histogram" in img_name.lower():
|
||||
sub_dir = vis_dir
|
||||
title_key = "histogram"
|
||||
elif "spectrum" in img_name.lower():
|
||||
sub_dir = vis_dir
|
||||
title_key = "spectrum_comparison"
|
||||
elif "distribution" in img_name.lower():
|
||||
sub_dir = vis_dir
|
||||
title_key = "distribution"
|
||||
else:
|
||||
sub_dir = vis_dir
|
||||
title_key = "histogram"
|
||||
|
||||
img_path = sub_dir / img_name
|
||||
if not img_path.exists():
|
||||
img_path = vis_dir / img_name
|
||||
|
||||
elif "scatter" in img_name.lower() or "pred" in img_name.lower() or "confidence" in img_name.lower():
|
||||
title_key = "scatter_with_confidence"
|
||||
img_path = vis_dir / "scatter_plots" / img_name
|
||||
if not img_path.exists():
|
||||
img_path = vis_dir / img_name
|
||||
elif "histogram" in img_name.lower():
|
||||
title_key = "histogram"
|
||||
img_path = vis_dir / img_name
|
||||
elif "spectrum" in img_name.lower():
|
||||
title_key = "spectrum_comparison"
|
||||
img_path = vis_dir / img_name
|
||||
elif "distribution" in img_name.lower():
|
||||
title_key = "distribution"
|
||||
# 颜色地图由预测步骤生成,开启全盘指索
|
||||
search_dirs = [
|
||||
vis_dir.parent / "9_water_quality_prediction",
|
||||
vis_dir.parent / "11_12_13_predictions",
|
||||
vis_dir.parent / "9_Concentration" / "charts",
|
||||
vis_dir.parent / "9_Concentration",
|
||||
vis_dir
|
||||
]
|
||||
found_map = False
|
||||
for s_dir in search_dirs:
|
||||
if s_dir.exists():
|
||||
candidates = list(s_dir.glob(f"*{param}*.png")) + list(s_dir.glob(f"*{param}*.jpg"))
|
||||
# 剔除掉属于其他类型的图
|
||||
candidates = [c for c in candidates if not any(x in c.name.lower() for x in ("scatter", "histogram", "spectrum", "boxplot", "preview"))]
|
||||
if candidates:
|
||||
img_path = candidates[0]
|
||||
found_map = True
|
||||
break
|
||||
if not found_map:
|
||||
img_path = vis_dir / img_name
|
||||
else:
|
||||
title_key = "histogram"
|
||||
img_path = vis_dir / img_name
|
||||
if img_path.exists():
|
||||
param_cn = param.replace("Chlorophyll", "叶绿素").replace("NO3-N", "硝酸盐氮").replace("NH3-N", "氨氮")
|
||||
cn_title = title_map.get(title_key, "分析图")
|
||||
@ -1245,7 +1177,7 @@ class WaterQualityReportGenerator:
|
||||
vis_dir = self.visualization_dir
|
||||
|
||||
# 0. 航线规划图
|
||||
flight_path_img_path = work_dir_path / "14_visualization" / "flight_maps"
|
||||
flight_path_img_path = work_dir_path / "14_visualization" / "flight_paths"
|
||||
h3 = doc.add_heading("航线规划:", level=3)
|
||||
self._style_heading(h3, level=3)
|
||||
|
||||
@ -1636,7 +1568,7 @@ class WaterQualityReportGenerator:
|
||||
|
||||
# 从工作目录的4_processed_data文件夹查找CSV文件
|
||||
work_dir_path = vis_dir.parent
|
||||
processed_data_dir = work_dir_path / "4_processed_data"
|
||||
processed_data_dir = work_dir_path / "5_Data_Cleaning"
|
||||
|
||||
if not processed_data_dir.exists():
|
||||
doc.add_paragraph(f"未找到数据处理目录: {processed_data_dir}")
|
||||
|
||||
@ -328,31 +328,40 @@ class WaterQualityVisualization:
|
||||
plt.close()
|
||||
output_paths['boxplot'] = str(boxplot_path)
|
||||
|
||||
# 2. 直方图 (每个水质参数列)
|
||||
|
||||
# 2. 直方图与单参数箱线图 (每个水质参数列)
|
||||
for col in numeric_cols:
|
||||
fig, ax = plt.subplots(figsize=(10, 6))
|
||||
data = df[col].dropna()
|
||||
if len(data) > 1:
|
||||
ax.hist(data, bins=30, edgecolor='black', alpha=0.7, color='skyblue')
|
||||
ax.set_xlabel(f'{col} 数值', fontsize=12, fontweight='bold')
|
||||
ax.set_ylabel('频数', fontsize=12, fontweight='bold')
|
||||
ax.set_title(f'{col} 分布直方图', fontsize=14, fontweight='bold')
|
||||
ax.grid(True, alpha=0.3, axis='y')
|
||||
|
||||
# 添加统计信息
|
||||
mean_val = data.mean()
|
||||
std_val = data.std() if len(data) > 1 else 0
|
||||
ax.axvline(mean_val, color='red', linestyle='--', linewidth=2, label=f'均值: {mean_val:.4f}')
|
||||
ax.legend()
|
||||
|
||||
plt.tight_layout()
|
||||
|
||||
safe_name = "".join(c for c in col if c.isalnum() or c in ('-', '_', '.'))
|
||||
hist_path = output_dir / f"{safe_name}_histogram.png"
|
||||
plt.savefig(hist_path, dpi=300, bbox_inches='tight')
|
||||
plt.close()
|
||||
if len(data) > 0:
|
||||
# === 直方图生成 ===
|
||||
fig, ax = plt.subplots(figsize=(10, 6))
|
||||
if len(data) > 1:
|
||||
ax.hist(data, bins=30, edgecolor='black', alpha=0.7, color='skyblue')
|
||||
ax.set_xlabel(f'{col} 数值', fontsize=12, fontweight='bold')
|
||||
ax.set_ylabel('频数', fontsize=12, fontweight='bold')
|
||||
ax.set_title(f'{col} 分布直方图', fontsize=14, fontweight='bold')
|
||||
ax.grid(True, alpha=0.3, axis='y')
|
||||
mean_val = data.mean()
|
||||
ax.axvline(mean_val, color='red', linestyle='--', linewidth=2, label=f'均值: {mean_val:.4f}')
|
||||
ax.legend()
|
||||
plt.tight_layout()
|
||||
safe_name = "".join(c for c in col if c.isalnum() or c in ('-', '_', '.'))
|
||||
hist_path = output_dir / f"{safe_name}_histogram.png"
|
||||
plt.savefig(hist_path, dpi=300, bbox_inches='tight')
|
||||
plt.close(fig)
|
||||
output_paths[f'histogram_{col}'] = str(hist_path)
|
||||
|
||||
|
||||
# === 新增:单参数箱线图生成 ===
|
||||
fig_box, ax_box = plt.subplots(figsize=(6, 8))
|
||||
ax_box.boxplot([data], labels=[col])
|
||||
ax_box.set_ylabel('数值', fontsize=12, fontweight='bold')
|
||||
ax_box.set_title(f'{col} 箱线图', fontsize=14, fontweight='bold')
|
||||
ax_box.grid(True, alpha=0.3, axis='y')
|
||||
plt.tight_layout()
|
||||
box_path = output_dir / f"{safe_name}_boxplot.png"
|
||||
plt.savefig(box_path, dpi=300, bbox_inches='tight')
|
||||
plt.close(fig_box)
|
||||
output_paths[f'boxplot_{col}'] = str(box_path)
|
||||
# 3. 相关性热力图
|
||||
if len(numeric_cols) >= 2:
|
||||
corr_matrix = df[numeric_cols].corr()
|
||||
|
||||
Reference in New Issue
Block a user