SWAT-CUP软件中批量输入月径流量代码

【投稿赢 iPhone 17】「我的第一个开源项目」故事征集:用代码换C位出道! 10w+人浏览 1.8k人参与

部署运行你感兴趣的模型镜像

摘要:在进行SWAT-CUP进行SWAT模型的调参率定的过程中,输入站点实测径流量的操作(制备Observed rch.txt)中,如果数据量过多,手动输入会导致工作量增加并且导致人为误差,为了减少繁琐程度和工作量,利用AI编写了一个python代码,可以将excel中记录的径流量数据转换为能够直接复制粘贴到“Observed rch.txt”中的txt文本格式,方便调参。本代码操作简单,设置了专门的用户配置区,老少咸宜。

一句话讲明白代码功能:给它一个按年、月顺序整理好的 Excel(或一列顺序的月径流),设定时间范围和列/行位置,它就能自动生成从起始年到结束年,每月一行、格式符合 SWAT-CUP 要求的径流输入 txt,并在最后帮你检查年月是否缺失或数据条数是否匹配。

图1:SWAT-CUP中Observed rch.txt界面

图2:代码所需的excel格式(可以有年月,不是必须,只有径流列也可以生成txt,不过要按日期排序)

具体代码如下:

# -*- coding: utf-8 -*-
"""
功能:
1.自动生成完整年月序列:代码根据你设定的 START_YEAR/START_MONTH 和 END_YEAR/END_MONTH,
从起始年月一直推到结束年月,按月生成一个完整的时间序列。序列中的每一行都会自动生成一个标签,
格式为 FLOW_OUT_月_年,前面带从 1 开始依次递增的行号,为后续径流填充和 SWAT-CUP 读取打好“骨架”。

2.输出 SWAT-CUP 需要的 txt 格式:在生成好年月序列后,代码会把每个月的信息写成一行 txt,
行内用制表符(Tab)分隔,固定格式为 行号\tFLOW_OUT_月_年\t径流量。
最终 txt 的总行数等于起止年月之间包含的全部月份数,已经按时间顺序排好,直接可以作为 SWAT-CUP 的输入文件。

3.两种填充径流方式(由 USE_MONITOR 控制):
当 USE_MONITOR = True 时为监测模式:
你在配置区指定年列、月列、径流列(YEAR_COL_INDEX / MONTH_COL_INDEX / FLOW_COL_INDEX),
程序从 Excel 读取“年、月、径流”,用 (年, 月) 精确匹配每一行的标签,把对应月份的径流填进去;
同时对年、月的解析比较鲁棒,支持纯数字、字符串、带“年”“月”的形式、日期格式等,
并在最后列出哪些年月在 Excel 中没找到径流值(这些月份在 txt 中留空)。
当 USE_MONITOR = False 时为顺序模式:
程序只按照你指定的 FLOW_COL_INDEX 这一列,从上到下顺序读取径流,依次填入每一行,
最后对比 txt 行数和 Excel 径流条数是否一致,帮助你检查时间序列有没有对齐。

4.可控的数据范围与工作表:你可以通过 SHEET_INDEX 指定要使用 Excel 的第几个工作表,
从而在同一个文件中自由切换不同表格的数据。同时通过 DATA_START_ROW 和 DATA_END_ROW 控制从第几行开始读、读到第几行,
既可以轻松跳过表头,也可以只截取某一部分年份或数据段参与生成 txt,避免手动删改原始 Excel。

5.路径和文件名完全可配置:EXCEL_FILE 用来指定输入 Excel 的完整路径和文件名,OUTPUT_TXT
用来指定输出 txt 的完整路径和文件名。你只要在配置区改这两个变量,就可以把同样的转换逻辑应用到不同的数据文件上,
或者生成不同名称的 SWAT-CUP 径流输入文件(例如你现在使用的“江桥 SWAT-CUP 径流输入格式 1994–2016”)
"""

import pandas as pd
from pathlib import Path

# =====================【用户可配置区:只改这里就行】=====================

# 1. Excel 文件路径
EXCEL_FILE = r"F:\你的径流excel位置.xlsx"

# 2. Excel 中的数据所在工作表序号(0 = 第 1 个工作表,1 = 第 2 个,以此类推)
SHEET_INDEX = 1

# 3. 从第几行开始读取数据(按 Excel 的“行号”来数)
#    比如:第 1 行是表头“年 月 径流...”,第 2 行开始是 1994  1  22.7
#    那就设为 DATA_START_ROW = 1
DATA_START_ROW = 1      # 第一条有效数据所在“行号”;若没有表头,第 1 行就是数据,则设为 0
DATA_END_ROW = None     # 一般用 None 表示读到最后一行

# 4. 监测开关:
#    - True:使用“年+月”匹配(Excel 中必须有 年列 和 月列)
#    - False:不匹配,径流列按顺序直接填充
USE_MONITOR = True

# 5. 当 USE_MONITOR=True 时,需要告诉程序“年/月/径流”的列号
#    列号:0=A列,1=B列,2=C列 ...
YEAR_COL_INDEX = 0      # 年份所在列号(例如年份在 A 列 → 0)
MONTH_COL_INDEX = 1     # 月份所在列号(例如月份在 B 列 → 1)
FLOW_COL_INDEX = 2      # 径流量所在列号(例如径流在 C 列 → 2)

# 6. 当 USE_MONITOR=False 时,也会用 FLOW_COL_INDEX 这列按顺序填充

# 7. 起始年份、起始月份(对应“序列里的第一个年月”)
START_YEAR = 1994
START_MONTH = 1

# 8. 结束年份、结束月份(对应“序列里的最后一个年月”)
END_YEAR = 2024
END_MONTH = 12

# 9. 输出 txt 文件保存路径(包含文件名)
OUTPUT_TXT = r"F:\输出txt位置和命名.txt"

# =====================【配置区结束,下面一般不用改】=====================


def parse_int_like(x):
    """
    尽量把 Excel 里看起来是“年份/月份”的东西转成整数:
    - 支持 1994, 1994.0
    - 支持 '1994', ' 1994 ', '1994年'
    - 支持 '1', '01', '1月'
    - 支持日期型,如 1994-01-01(会提取里面的数字)
    解析失败会抛出异常,由上层决定是否跳过该行。
    """
    if pd.isna(x):
        raise ValueError("值是 NaN")

    s = str(x).strip()
    # 只保留数字和负号,例如 '1994年'、'1994-01-01'
    digits = "".join(ch for ch in s if (ch.isdigit() or ch == "-"))
    if digits == "":
        raise ValueError(f"找不到数字: {x!r}")
    return int(digits)


def build_month_list(start_year, start_month, end_year, end_month):
    """
    根据起止年月生成一个 [(year, month), ...] 的列表,包含起止两端。
    """
    if (end_year, end_month) < (start_year, start_month):
        raise ValueError("结束年月早于起始年月,请检查 START/END 设置。")

    months = []
    y, m = start_year, start_month
    while True:
        months.append((y, m))
        if (y, m) == (end_year, end_month):
            break
        m += 1
        if m > 12:
            m = 1
            y += 1
    return months


def read_excel_monitor_mode():
    """
    监测模式:
    从 Excel 中读取 年、月、径流 列,生成 {(year, month): flow} 的字典。
    """
    df = pd.read_excel(EXCEL_FILE, sheet_name=SHEET_INDEX)

    start_idx = DATA_START_ROW - 1 if DATA_START_ROW is not None else 0
    if DATA_END_ROW is None:
        df_sub = df.iloc[start_idx:, :]
    else:
        df_sub = df.iloc[start_idx:DATA_END_ROW, :]

    year_series = df_sub.iloc[:, YEAR_COL_INDEX]
    month_series = df_sub.iloc[:, MONTH_COL_INDEX]
    flow_series = df_sub.iloc[:, FLOW_COL_INDEX]

    mapping = {}

    for y, m, f in zip(year_series, month_series, flow_series):
        try:
            yy = parse_int_like(y)
            mm = parse_int_like(m)
        except Exception:
            # 这一行年/月解析失败,直接跳过
            continue
        mapping[(yy, mm)] = f  # 若同一 (年,月) 多次出现,以最后一次为准

    return mapping


def read_excel_sequential_mode():
    """
    非监测模式:
    从 Excel 中读取某一列径流量,按顺序返回 list。
    """
    df = pd.read_excel(EXCEL_FILE, sheet_name=SHEET_INDEX)

    start_idx = DATA_START_ROW - 1 if DATA_START_ROW is not None else 0
    if DATA_END_ROW is None:
        flow_series = df.iloc[start_idx:, FLOW_COL_INDEX]
    else:
        flow_series = df.iloc[start_idx:DATA_END_ROW, FLOW_COL_INDEX]

    return list(flow_series)


def generate_txt():
    """
    主函数:
    先生成完整年月行,再根据监测开关选择填充方式,最后写 txt。
    """
    # 1. 构建所有 (year, month) 序列
    ym_list = build_month_list(START_YEAR, START_MONTH, END_YEAR, END_MONTH)
    total_months = len(ym_list)

    # 2. 根据 USE_MONITOR 选择读取 Excel 的方式
    if USE_MONITOR:
        ym_to_flow = read_excel_monitor_mode()
        print(f"监测模式:共从 Excel 中读取到 {len(ym_to_flow)} 个“年-月-径流”记录。")
    else:
        flow_seq = read_excel_sequential_mode()
        excel_flow_count = len(flow_seq)
        print(f"顺序模式:从 Excel 中读取到 {excel_flow_count} 条径流数据。")

    # 3. 输出 txt
    out_path = Path(OUTPUT_TXT)
    out_path.parent.mkdir(parents=True, exist_ok=True)

    missing_months = []  # 只在 USE_MONITOR=True 时用

    with out_path.open("w", encoding="utf-8") as f:
        for idx, (y, m) in enumerate(ym_list, start=1):
            label = f"FLOW_OUT_{m}_{y}"

            if USE_MONITOR:
                # 按 (年, 月) 匹配
                if (y, m) in ym_to_flow:
                    flow_val = ym_to_flow[(y, m)]
                else:
                    flow_val = ""
                    missing_months.append((y, m))
            else:
                # 按顺序填充
                if idx - 1 < len(flow_seq):
                    flow_val = flow_seq[idx - 1]
                else:
                    flow_val = ""  # Excel 不够长时后面的行留空

            line = f"{idx}\t{label}\t{flow_val}\n"
            f.write(line)

    print(f"已生成 txt 文件:{out_path}")
    print(f"总行数(月份数):{total_months}")

    # 4. 额外信息输出
    if USE_MONITOR:
        if missing_months:
            print("监测结果:在下列年月中未在 Excel 中找到径流值(已在 txt 中留空):")
            # 按年份排序输出
            missing_months_sorted = sorted(missing_months)
            for y, m in missing_months_sorted:
                print(f"  {y}-{m}")
        else:
            print("监测结果:所有月份在 Excel 中都找到了对应的径流值,没有缺失。")
    else:
        # 对比 txt 行数和 Excel 径流条数
        same_len = (total_months == excel_flow_count)
        print(f"对比:txt 行数 = {total_months},Excel 径流条数 = {excel_flow_count},是否一致:{same_len}")


if __name__ == "__main__":
    generate_txt()

文章结束,祝各位身体健康 心情愉快 科研顺利 工作有得!

您可能感兴趣的与本文相关的镜像

Python3.10

Python3.10

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值