python chapter5:降水过程筛选(时间筛选)
对于 Zhu 等的方法重新界定降水过程:夏季用 Nw>0 连续12分钟;总时长>60分钟:
第1步:找到所有 Nw > 0 的分钟
第2步:找到整场雨的边界
- 开始 = 第一个 Nw > 0 的分钟
- 结束 = 最后一个 Nw > 0 的分钟
第3步:计算总时长 = 结束 - 开始 + 1(包括断档)
如果总时长 < 60 分钟 → 丢弃
第4步:检查在开始到结束之间,是否存在至少一个连续≥12分钟的Nw>0窗口
如果没有 → 丢弃
第5步:如果有,保留从开始到结束的整段数据(包括断档)
数据情况:

#!/usr/bin/env python # -*- coding:utf-8 -*- """ @author: Suyue @file: filter_by_Nw.py @time: 2026/06/29 @desc: 根据Nw筛选降水事件 条件1:总时长(第一个Nw>0 到 最后一个Nw>0)≥ 60分钟 条件2:存在至少一个连续≥12分钟的Nw>0窗口 满足条件则保留整段数据(包括断档) """ import numpy as np import pandas as pd import re import os # ==================== 读取CSV参数文件 ==================== def load_csv(csv_path): """读取参数CSV文件""" df = pd.read_csv(csv_path) # 把time列转为datetime df['time'] = pd.to_datetime(df['time']) # Nw列转为数值,'NaN'字符串转为真正的NaN df['Nw'] = pd.to_numeric(df['Nw'], errors='coerce') return df # ==================== 筛选有效时间区间 ==================== def find_valid_ranges(df, min_continuous=12, min_duration=60): """ 找到所有符合条件的降水时间区间 参数: df: 包含time和Nw列的DataFrame min_continuous: 最小连续分钟数(默认12) min_duration: 最小总时长分钟数(默认60) 返回: valid_ranges: 列表,每个元素为 (开始时间, 结束时间) """ # 标记 Nw > 0 的分钟 df['is_rain'] = (df['Nw'] > 0) & (~df['Nw'].isna()) # 如果没有任何有效分钟,直接返回空 if not df['is_rain'].any(): print(" 没有 Nw > 0 的分钟") return [] # 找到第一个和最后一个 Nw > 0 的分钟 rain_indices = df[df['is_rain']].index start_idx = rain_indices[0] end_idx = rain_indices[-1] start_time = df.loc[start_idx, 'time'] end_time = df.loc[end_idx, 'time'] total_duration = (end_time - start_time).total_seconds() / 60 + 1 print(f" 整场雨边界: {start_time} -> {end_time}") print(f" 总时长: {total_duration:.0f} 分钟") # 条件1:总时长 >= 60分钟 if total_duration < min_duration: print(f" ❌ 总时长 {total_duration:.0f} 分钟 < {min_duration} 分钟,丢弃") return [] # 条件2:检查是否存在连续 >= 12 分钟的 Nw > 0 # 只看从start_idx到end_idx这一段 sub_df = df.loc[start_idx:end_idx] max_consecutive = 0 current_consecutive = 0 consecutive_start = None for idx, row in sub_df.iterrows(): if row['is_rain']: current_consecutive += 1 if current_consecutive > max_consecutive: max_consecutive = current_consecutive # 记录连续段的开始时间 if current_consecutive == 1: consecutive_start = row['time'] else: current_consecutive = 0 print(f" 最大连续Nw>0分钟数: {max_consecutive}") if max_consecutive < min_continuous: print(f" ❌ 最大连续 {max_consecutive} 分钟 < {min_continuous} 分钟,丢弃") return [] print(f" ✅ 满足条件!保留从 {start_time} 到 {end_time} 的全部数据") return [(start_time, end_time)] # ==================== 从原始txt提取数据 ==================== def extract_by_timerange(input_txt, output_txt, valid_ranges): """ 根据时间区间从原始txt中提取数据 参数: input_txt: 原始txt文件路径 output_txt: 输出txt文件路径 valid_ranges: 有效时间区间列表 [(start, end), ...] """ if not valid_ranges: print(" 没有有效区间,跳过") return timestamp_pattern = re.compile(r'^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$') print(f"\n正在读取原始文件: {input_txt}") with open(input_txt, 'r', encoding='utf-8') as f: lines = f.readlines() # 构建时间区间集合,用于快速判断 # 把每个区间内的所有分钟展开成一个集合 keep_times = set() for start, end in valid_ranges: current = start while current <= end: keep_times.add(current.strftime("%Y-%m-%d %H:%M:%S")) current += pd.Timedelta(minutes=1) print(f" 需要保留 {len(keep_times)} 分钟的数据") # 遍历原始文件,提取需要保留的数据 output_lines = [] i = 0 kept_count = 0 total_minutes = 0 while i < len(lines): line = lines[i].strip() if timestamp_pattern.match(line): total_minutes += 1 timestamp = line if timestamp in keep_times: # 保留这一分钟:时间戳 + 32行数据 output_lines.append(line + '\n') for j in range(1, 33): if i + j < len(lines): output_lines.append(lines[i + j]) output_lines.append('\n') # 分钟间空行 kept_count += 1 i += 33 else: # 跳过这一分钟 i += 33 else: i += 1 print(f" 原始总分钟数: {total_minutes}") print(f" 保留分钟数: {kept_count}") # 写入输出文件 with open(output_txt, 'w', encoding='utf-8') as f: f.writelines(output_lines) print(f" 输出文件: {output_txt}") # ==================== 主函数 ==================== def main(): # ===== 配置路径 ===== csv_path = "D:/lianxi/50425-20240824073200-20240824084559-0-clean-DSD.csv" # 参数CSV文件 input_txt = "D:/lianxi/50425-20240824073200-20240824084559-0-clean.txt" # 原始txt output_txt = "D:/lianxi/50425-20240824073200-20240824084559-0-clean_DSD_filtered.txt" # 输出txt # ===== 筛选参数 ===== MIN_CONTINUOUS = 12 # 连续Nw>0的最小分钟数 MIN_DURATION = 60 # 总时长最小分钟数 print("=" * 70) print("雨滴谱数据筛选程序") print("=" * 70) print(f"条件1: 总时长 >= {MIN_DURATION} 分钟") print(f"条件2: 存在连续 >= {MIN_CONTINUOUS} 分钟的 Nw > 0") print("=" * 70) # 1. 读取CSV print("\n[1] 读取参数CSV...") if not os.path.exists(csv_path): print(f" 错误: CSV文件不存在: {csv_path}") return df = load_csv(csv_path) print(f" 共 {len(df)} 分钟数据") # 2. 筛选有效区间 print("\n[2] 筛选有效降水区间...") valid_ranges = find_valid_ranges(df, MIN_CONTINUOUS, MIN_DURATION) if not valid_ranges: print("\n❌ 未找到符合条件的降水事件,程序结束") return # 3. 从原始txt提取数据 print("\n[3] 从原始文件提取数据...") if not os.path.exists(input_txt): print(f" 错误: 原始文件不存在: {input_txt}") return extract_by_timerange(input_txt, output_txt, valid_ranges) print("\n" + "=" * 70) print("✅ 筛选完成!") print("=" * 70) if __name__ == "__main__": main()
就是不对原始数据做任何修改,只是"复制"符合条件的分钟到一个新文件。原始文件完好无损。

批量处理数据代码
#!/usr/bin/env python # -*- coding:utf-8 -*- """ @author: Suyue @file: batch_filter_by_Nw.py @time: 2026/07/06 @desc: 批量根据Nw筛选降水事件 条件1:总时长(第一个Nw>0 到 最后一个Nw>0)≥ 60分钟 条件2:存在至少一个连续≥12分钟的Nw>0窗口 满足条件则保留整段数据(包括断档) """ import numpy as np import pandas as pd import re import os # ==================== 读取CSV参数文件 ==================== def load_csv(csv_path): """读取参数CSV文件""" df = pd.read_csv(csv_path) # 把time列转为datetime df['time'] = pd.to_datetime(df['time']) # Nw列转为数值,'NaN'字符串转为真正的NaN df['Nw'] = pd.to_numeric(df['Nw'], errors='coerce') return df # ==================== 筛选有效时间区间 ==================== def find_valid_ranges(df, min_continuous=12, min_duration=60): """ 找到所有符合条件的降水时间区间 参数: df: 包含time和Nw列的DataFrame min_continuous: 最小连续分钟数(默认12) min_duration: 最小总时长分钟数(默认60) 返回: valid_ranges: 列表,每个元素为 (开始时间, 结束时间) """ # 标记 Nw > 0 的分钟 df['is_rain'] = (df['Nw'] > 0) & (~df['Nw'].isna()) # 如果没有任何有效分钟,直接返回空 if not df['is_rain'].any(): print(" 没有 Nw > 0 的分钟") return [] # 找到第一个和最后一个 Nw > 0 的分钟 rain_indices = df[df['is_rain']].index start_idx = rain_indices[0] end_idx = rain_indices[-1] start_time = df.loc[start_idx, 'time'] end_time = df.loc[end_idx, 'time'] total_duration = (end_time - start_time).total_seconds() / 60 + 1 print(f" 整场雨边界: {start_time} -> {end_time}") print(f" 总时长: {total_duration:.0f} 分钟") # 条件1:总时长 >= 60分钟 if total_duration < min_duration: print(f" ❌ 总时长 {total_duration:.0f} 分钟 < {min_duration} 分钟,丢弃") return [] # 条件2:检查是否存在连续 >= 12 分钟的 Nw > 0 sub_df = df.loc[start_idx:end_idx] max_consecutive = 0 current_consecutive = 0 for idx, row in sub_df.iterrows(): if row['is_rain']: current_consecutive += 1 if current_consecutive > max_consecutive: max_consecutive = current_consecutive else: current_consecutive = 0 print(f" 最大连续Nw>0分钟数: {max_consecutive}") if max_consecutive < min_continuous: print(f" ❌ 最大连续 {max_consecutive} 分钟 < {min_continuous} 分钟,丢弃") return [] print(f" ✅ 满足条件!保留从 {start_time} 到 {end_time} 的全部数据") return [(start_time, end_time)] # ==================== 从原始txt提取数据 ==================== def extract_by_timerange(input_txt, output_txt, valid_ranges): """ 根据时间区间从原始txt中提取数据 参数: input_txt: 原始txt文件路径 output_txt: 输出txt文件路径 valid_ranges: 有效时间区间列表 [(start, end), ...] """ if not valid_ranges: print(" 没有有效区间,跳过") return False timestamp_pattern = re.compile(r'^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$') with open(input_txt, 'r', encoding='utf-8') as f: lines = f.readlines() # 构建时间区间集合 keep_times = set() for start, end in valid_ranges: current = start while current <= end: keep_times.add(current.strftime("%Y-%m-%d %H:%M:%S")) current += pd.Timedelta(minutes=1) print(f" 需要保留 {len(keep_times)} 分钟的数据") # 遍历原始文件,提取需要保留的数据 output_lines = [] i = 0 kept_count = 0 total_minutes = 0 while i < len(lines): line = lines[i].strip() if timestamp_pattern.match(line): total_minutes += 1 timestamp = line if timestamp in keep_times: # 保留这一分钟:时间戳 + 32行数据 output_lines.append(line + '\n') for j in range(1, 33): if i + j < len(lines): output_lines.append(lines[i + j]) output_lines.append('\n') # 分钟间空行 kept_count += 1 i += 33 else: # 跳过这一分钟 i += 33 else: i += 1 print(f" 原始总分钟数: {total_minutes}") print(f" 保留分钟数: {kept_count}") # 确保输出目录存在 os.makedirs(os.path.dirname(output_txt), exist_ok=True) # 写入输出文件 with open(output_txt, 'w', encoding='utf-8') as f: f.writelines(output_lines) print(f" 输出文件: {os.path.basename(output_txt)}") return True # ==================== 批量处理函数 ==================== def batch_filter(csv_folder, txt_folder, output_folder, min_continuous=12, min_duration=60): """ 批量处理文件夹中的所有文件 参数: csv_folder: CSV文件所在文件夹 (存放 *-clean-DSD.csv) txt_folder: TXT文件所在文件夹 (存放 *-clean.txt) output_folder: 输出文件夹 min_continuous: 最小连续分钟数 min_duration: 最小总时长分钟数 文件名格式要求: CSV: XXX-clean-DSD.csv (在csv_folder中) TXT: XXX-clean.txt (在txt_folder中) 输出: XXX-clean_DSD_filtered.txt (在output_folder中) """ print("=" * 70) print("雨滴谱数据批量筛选程序") print("=" * 70) print(f"条件1: 总时长 >= {min_duration} 分钟") print(f"条件2: 存在连续 >= {min_continuous} 分钟的 Nw > 0") print("=" * 70) print(f"CSV文件夹: {csv_folder}") print(f"TXT文件夹: {txt_folder}") print(f"输出文件夹: {output_folder}") print("=" * 70) # 检查文件夹是否存在 if not os.path.exists(csv_folder): print(f"错误: CSV文件夹不存在: {csv_folder}") return if not os.path.exists(txt_folder): print(f"错误: TXT文件夹不存在: {txt_folder}") return # 创建输出文件夹 os.makedirs(output_folder, exist_ok=True) # 查找所有CSV文件 (以-clean-DSD.csv结尾) csv_files = [] for file in os.listdir(csv_folder): if file.endswith('-clean-DSD.csv'): csv_files.append(file) if not csv_files: print(f"\n错误: 在 {csv_folder} 中没有找到任何 -clean-DSD.csv 文件") print("请确认CSV文件命名格式为: XXX-clean-DSD.csv") return print(f"\n找到 {len(csv_files)} 个CSV文件") # 处理每个CSV文件 success_count = 0 fail_count = 0 failed_files = [] for i, csv_file in enumerate(csv_files, 1): # 构造对应的txt文件名: XXX-clean-DSD.csv -> XXX-clean.txt txt_file = csv_file.replace('-clean-DSD.csv', '-clean.txt') txt_path = os.path.join(txt_folder, txt_file) # 检查txt文件是否存在 if not os.path.exists(txt_path): print(f"\n[{i}/{len(csv_files)}] ❌ 跳过: {csv_file}") print(f" 找不到对应的txt文件: {txt_file}") print(f" 请确认txt文件在: {txt_folder}") fail_count += 1 failed_files.append(f"{csv_file} (缺少对应的txt)") continue # 构造输出文件名: XXX-clean-DSD.csv -> XXX-clean_DSD_filtered.txt base_name = csv_file.replace('-clean-DSD.csv', '') output_txt = os.path.join(output_folder, f"{base_name}-clean_DSD_filtered.txt") print(f"\n[{i}/{len(csv_files)}] 处理: {base_name}") print("-" * 50) try: # 1. 读取CSV csv_path = os.path.join(csv_folder, csv_file) print(f" 读取CSV: {csv_file}") df = load_csv(csv_path) print(f" CSV数据: {len(df)} 分钟") # 2. 筛选有效区间 valid_ranges = find_valid_ranges(df, min_continuous, min_duration) if not valid_ranges: print(f" ❌ 未找到符合条件的降水事件") fail_count += 1 failed_files.append(f"{csv_file} (不符合条件)") continue # 3. 从原始txt提取数据 print(f" 读取TXT: {txt_file}") success = extract_by_timerange(txt_path, output_txt, valid_ranges) if success: print(f" ✅ 处理成功") success_count += 1 else: print(f" ❌ 提取数据失败") fail_count += 1 failed_files.append(f"{csv_file} (提取失败)") except Exception as e: print(f" ❌ 处理出错: {str(e)}") fail_count += 1 failed_files.append(f"{csv_file} (错误: {str(e)})") # 输出统计 print("\n" + "=" * 70) print("批量处理完成!") print("=" * 70) print(f"总文件数: {len(csv_files)}") print(f"成功处理: {success_count}") print(f"处理失败: {fail_count}") if failed_files: print("\n失败的文件列表:") for f in failed_files: print(f" ❌ {f}") print(f"\n输出文件夹: {output_folder}") print("=" * 70) # ==================== 主函数 ==================== def main(): # ===== 配置路径 ===== csv_folder = "D:/雨滴谱/数据处理/03-DSD/夏季" # CSV文件所在文件夹 (存放 *-clean-DSD.csv) txt_folder = "D:/雨滴谱/数据处理/02-数据清洗/新清洗法/夏季" # TXT文件所在文件夹 (存放 *-clean.txt) output_folder = "D:/雨滴谱/数据处理/04-时间连续/夏季" # 输出文件夹 # ===== 筛选参数 ===== MIN_CONTINUOUS = 12 # 连续Nw>0的最小分钟数 MIN_DURATION = 60 # 总时长最小分钟数 # ===== 开始批量处理 ===== batch_filter(csv_folder, txt_folder, output_folder, MIN_CONTINUOUS, MIN_DURATION) if __name__ == "__main__": main()

浙公网安备 33010602011771号