import os import pandas as pd import numpy as np import matplotlib.pyplot as plt from scipy.optimize import curve_fit import seaborn as sns # 解决绘图中中文不能显示的问题 import matplotlib # 设置中文显示和负号正常显示 matplotlib.rcParams['font.sans-serif'] = ['SimHei'] # 显示中文 matplotlib.rcParams['axes.unicode_minus'] = False # 正常显示负号 # 定义所有模型 def tidal_model(t, v0, a1, b1, a2, b2, a3, b3, a4, b4): """潮汐波模型""" T1, T2, T3, T4 = 6, 8, 12, 24 # 周期以小时为单位 return (v0 + a1 * np.sin((2 * np.pi / T1) * t + b1) + a2 * np.sin((2 * np.pi / T2) * t + b2) + a3 * np.sin((2 * np.pi / T3) * t + b3) + a4 * np.sin((2 * np.pi / T4) * t + b4)) def planetary_model_2day(t, v0, a, b): """2日行星波模型""" T = 48 # 周期为2天(48小时) return v0 + a * np.sin((2 * np.pi / T) * t + b) def planetary_model_5day(t, v0, a, b): """5日行星波模型""" T = 120 # 周期为5天(120小时) return v0 + a * np.sin((2 * np.pi / T) * t + b) def planetary_model_10day(t, v0, a, b): """10日行星波模型""" T = 240 # 周期为10天(240小时) return v0 + a * np.sin((2 * np.pi / T) * t + b) def planetary_model_16day(t, v0, a, b): """16日行星波模型""" T = 384 # 周期为16天(384小时) return v0 + a * np.sin((2 * np.pi / T) * t + b) # ----------------------------------------------------------第二大部分———————————————————————————————————————————————————— # # 根据纬向风(u)、经向风(v),得到武汉左岭镇站/黑龙江漠河站 # 大气波动(6、8、12、24小时潮汐波,2日、5日、10日、16日行星波)随时空变化热力图 # 设置文件路径 year = 2022 station = '武汉左岭镇站' # station = '黑龙江漠河站' # 文件路径设置 file_dir = rf'D:\wu\gravity wave\Meteor\流星雷达分析数据\{station}\{year}' # 数据文件路径 output_dir = rf'D:\wu\gravity wave\Meteor\输出\{station}\{year}\时空' # 参数txt、图片输出路径 os.makedirs(output_dir, exist_ok=True) # 确保输出路径存在 # 获取目录下所有txt文件 file_list = [f for f in os.listdir(file_dir) if f.endswith('.txt')] # 初始化空的DataFrame,用于合并所有文件的数据 final_df = pd.DataFrame() # 批量处理每个txt文件 for file_name in file_list: file_path = os.path.join(file_dir, file_name) df = pd.read_csv(file_path, delim_whitespace=True) date_part = os.path.splitext(file_name)[0][-8:] # 假设文件名最后8个字符是日期 # 替换异常值为 NaN df.loc[df['uwind'] == 1000000, 'uwind'] = np.nan df.loc[df['vwind'] == 1000000, 'vwind'] = np.nan # 筛选出所需的列 df = df[['height', 'time', 'uwind', 'vwind']] df = df.rename(columns={ 'uwind': f'{date_part}_uwind', 'vwind': f'{date_part}_vwind' }) if final_df.empty: final_df = df else: final_df = pd.merge(final_df, df, on=['height', 'time'], how='outer') # 生成完整日期范围的列名,补全缺失的天,使得平年365,闰年366 all_dates = pd.date_range(f'{year}-01-01', f'{year}-12-31').strftime('%Y%m%d') expected_columns = [f'{date}_uwind' for date in all_dates] + [f'{date}_vwind' for date in all_dates] # 确保 final_df 包含所有日期的列,缺失列补为 NaN for col in expected_columns: if col not in final_df.columns: final_df[col] = np.nan # 按列名排序(确保日期顺序) final_df = final_df[['height', 'time'] + sorted(expected_columns)] # 此时,final_df已经是全高度包含完整天的分析数据--------------------------------------------------------- # -----------------------------------------得到潮汐波拟合参数------------------------------------------- # 定义单高度的拟合函数 def fit_wind_speed(data, window=5): # 动态调整最小有效数据点数量:参数数量 * 6 num_params = 9 # 模型参数数量 # 转换为 pandas DataFrame 并设置时间为索引 data["date"] = pd.to_datetime(data["date"]) data.set_index("date", inplace=True) # 获取唯一日期 unique_dates = np.unique(data.index.date) # 存储拟合参数 params = [] for date in unique_dates: start_date = pd.to_datetime(date) - pd.Timedelta(days=(window - 1) / 2) end_date = pd.to_datetime(date) + pd.Timedelta(days=(window - 1) / 2) # 获取这几天的数据 window_data = data[start_date:end_date] t = np.arange(len(window_data)) + 1 # 时间点 y = window_data['wind_speed'].values # 风速数据 # 将 y 转换为浮点类型,以防有非数值类型数据 y = pd.to_numeric(y, errors='coerce') # 将非数值转换为 NaN # 去掉包含nan的那一列,筛选有效数据 valid_mask = ~np.isnan(y) # 创建布尔掩码,标记非 NaN 的位置 t = t[valid_mask] y = y[valid_mask] # 检查筛选后的数据是否足够 if len(y) < num_params*6: # 确保数据足够 # print(f"Not enough valid data after filtering for date: {date}") params.append([None] * 9) # 如果数据不足,记录None continue # 设置初始参数 initial_guess = [0, 1, 0, 1, 0, 1, 0, 1, 0] # v0, a1, b1, a2, b2, a3, b3, a4, b4 # 设置参数界限 bounds = ([-np.inf, 0, -np.inf, 0, -np.inf, 0, -np.inf, 0, -np.inf], # 下界 [np.inf, np.inf, np.inf, np.inf, np.inf, np.inf, np.inf, np.inf, np.inf]) # 上界 # 拟合模型 try: popt, _ = curve_fit(tidal_model, t, y, p0=initial_guess, bounds=bounds) params.append(popt) # 存储拟合参数 except RuntimeError: # print(f"Fitting failed for date: {date}") params.append([None] *9) # 如果拟合失败,记录None # 转换拟合参数为 DataFrame params_df = pd.DataFrame( params, columns=["v0", "a1", "b1", "a2", "b2", "a3", "b3", "a4", "b4"], index=unique_dates, ) return params_df # 1-拟合纬向风------------------------------------------ # 提取高度、时间和纬向风数据 uwind_data = final_df[["height", "time"] + [col for col in final_df.columns if "_uwind" in col]] uwind_data.columns = [col[:8] for col in uwind_data.columns] # 遍历所有高度 heights = uwind_data["height"].unique() u_result_dict = {} for height in heights: print(f"Processing height: {height} m") # 获取当前高度的纬向风数据 height_data = uwind_data[uwind_data["height"] == height] # 转换为长格式(适配拟合函数) long_data = pd.melt( height_data, id_vars=["time", "height"], var_name="date", value_name="wind_speed", ) long_data["date"] = pd.to_datetime(long_data["date"], format="%Y%m%d") # 调用拟合函数 params_df = fit_wind_speed(long_data) u_result_dict[height] = params_df # 汇总结果为 DataFrame u_final_result = pd.concat(u_result_dict, names=["height", "date"]) # 将结果保存为 TXT 文档 file_name1 = f'{year}年_纬向风_潮汐波振幅参数.txt' u_final_result.to_csv( os.path.join(output_dir, file_name), sep='\t', index=True, float_format='%.2f', # 控制数值列保留 2 位小数 header=True) # 2-拟合经向风------------------------------------------ # 提取高度、时间和经向风数据 vwind_data = final_df[["height", "time"] + [col for col in final_df.columns if "_vwind" in col]] vwind_data.columns = [col[:8] for col in vwind_data.columns] # 遍历所有高度 heights = vwind_data["height"].unique() v_result_dict = {} for height in heights: print(f"Processing height: {height} m") height_data = vwind_data[vwind_data["height"] == height] long_data = pd.melt( height_data, id_vars=["time", "height"], var_name="date", value_name="wind_speed", ) long_data["date"] = pd.to_datetime(long_data["date"], format="%Y%m%d") params_df = fit_wind_speed(long_data) v_result_dict[height] = params_df # 汇总结果为 DataFrame v_final_result = pd.concat(v_result_dict, names=["height", "date"]) # 将结果保存为 TXT 文档 # 保存参数 file_name2 = f'{year}年_经向风_潮汐波振幅参数.txt' v_final_result.to_csv( os.path.join(output_dir, file_name), sep='\t', index=True, float_format='%.2f', # 控制数值列保留 2 位小数 header=True) # 画热力图----------------------------------------- ''' # 读取数据,设置第一列和第二列为多重索引 u_final_result = pd.read_csv(os.path.join(output_dir, file_name1), sep='\t') u_final_result = u_final_result.set_index([u_final_result.columns[0], u_final_result.columns[1]]) v_final_result = pd.read_csv(os.path.join(output_dir, file_name2), sep='\t') v_final_result = v_final_result.set_index([v_final_result.columns[0], v_final_result.columns[1]]) ''' # 定义振幅参数 amplitude_param = 'a4' # 代表 24 小时周期振幅 # todo: 选择a4为周日潮,a3半日潮 # 提取纬向风数据,重置索引为平坦结构 u_heatmap_data = u_final_result[amplitude_param].unstack(level=0) # 将高度作为列,日期作为行 u_heatmap_data.index = pd.to_datetime(u_heatmap_data.index) # 确保日期为时间格式 u_heatmap_data = u_heatmap_data.iloc[:, ::-1] # 按高度降序排列 # 提取经向风数据,重置索引为平坦结构 v_heatmap_data = v_final_result[amplitude_param].unstack(level=0) # 将高度作为列,日期作为行 v_heatmap_data.index = pd.to_datetime(v_heatmap_data.index) # 确保日期为时间格式 v_heatmap_data = v_heatmap_data.iloc[:, ::-1] # 按高度降序排列 # 创建画布和子图 fig, axes = plt.subplots(1, 2, figsize=(18, 8)) # 1行2列的布局 fig.suptitle('2022年周日潮汐波振幅时空变化', fontsize=20) # 添加总标题 # 绘制第一幅热力图(纬向风) sns.heatmap( u_heatmap_data.T, # 转置,横轴为时间,纵轴为高度 cmap="viridis", # 配色方案 vmax=100, # 设置颜色范围的最大值 cbar_kws={'label': '振幅 (m/s)'}, # 设置颜色条标签 xticklabels=True, # 保留真实的横轴刻度标签 #yticklabels=u_heatmap_data.columns.astype(str), # 显示纵轴的高度 yticklabels=[f'{height / 1000}' for height in u_heatmap_data.columns], # 转换高度为千米并格式化 ax=axes[0] # 指定子图 ) axes[0].set_title('纬向风振幅变化', fontsize=14) axes[0].set_xlabel('日期', fontsize=12) axes[0].set_ylabel('高度 (km)', fontsize=12) # 绘制第二幅热力图(经向风) sns.heatmap( v_heatmap_data.T, # 转置,横轴为时间,纵轴为高度 cmap="viridis", # 配色方案 vmax=100, # 设置颜色范围的最大值 cbar_kws={'label': '振幅 (m/s)'}, # 设置颜色条标签 xticklabels=True, # 保留真实的横轴刻度标签 #yticklabels=v_heatmap_data.columns.astype(str), # 显示纵轴的高度 yticklabels=[f'{height / 1000}' for height in v_heatmap_data.columns], # 转换高度为千米并格式化 ax=axes[1] # 指定子图 ) axes[1].set_title('经向风振幅变化', fontsize=14) axes[1].set_xlabel('日期', fontsize=12) axes[1].set_ylabel('高度 (km)', fontsize=12) # 调整布局 plt.tight_layout(rect=[0, 0, 1, 0.95]) # 为总标题留出空间 # 显示图像 plt.show() # 输出路径和文件名 picture_file_name = f'{year}年_周日_潮汐波振幅时空变化.png' plt.savefig(os.path.join(output_dir, picture_file_name)) # -----------------------------------------得到2日行星波拟合参数------------------------------------------- # 定义新的拟合函数 def fit_wind_speed_v1(data, window=5): # 动态调整最小有效数据点数量:参数数量 * 6 num_params = 3 # 模型参数数量 # 转换为 pandas DataFrame 并设置时间为索引 data["date"] = pd.to_datetime(data["date"]) data.set_index("date", inplace=True) # 获取唯一日期 unique_dates = np.unique(data.index.date) # 存储拟合参数 params = [] for date in unique_dates: start_date = pd.to_datetime(date) - pd.Timedelta(days=(window - 1) / 2) end_date = pd.to_datetime(date) + pd.Timedelta(days=(window - 1) / 2) # 获取这几天的数据 window_data = data[start_date:end_date] t = np.arange(len(window_data)) + 1 # 时间点 y = window_data['wind_speed'].values # 风速数据 # 将 y 转换为浮点类型,以防有非数值类型数据 y = pd.to_numeric(y, errors='coerce') # 将非数值转换为 NaN # 去掉包含nan的那一列,筛选有效数据 valid_mask = ~np.isnan(y) # 创建布尔掩码,标记非 NaN 的位置 t = t[valid_mask] y = y[valid_mask] # 检查筛选后的数据是否足够 if len(y) < num_params * 6: # 确保数据足够 # print(f"Not enough valid data after filtering for date: {date}") params.append([None] * 3) # 如果数据不足,记录None continue # 设置初始参数 initial_guess = [0, 1, 0] # 新模型的初始猜测参数 bounds = ( [-np.inf, 0, -np.inf], # 下界 [np.inf, np.inf, np.inf], # 上界 ) # 拟合模型 try: popt, _ = curve_fit(planetary_model_2day, t, y, p0=initial_guess, bounds=bounds) params.append(popt) # 存储拟合参数 except RuntimeError: params.append([None] * 3) # 如果拟合失败,记录None # 转换拟合参数为 DataFrame params_df = pd.DataFrame( params, columns=["v0", "a", "b"], index=unique_dates, ) return params_df # 1-拟合纬向风------------------------------------------ u_result_dict = {} for height in heights: print(f"Processing height: {height} m") # 获取当前高度的纬向风数据 height_data = uwind_data[uwind_data["height"] == height] # 转换为长格式(适配拟合函数) long_data = pd.melt( height_data, id_vars=["time", "height"], var_name="date", value_name="wind_speed", ) long_data["date"] = pd.to_datetime(long_data["date"], format="%Y%m%d") # 调用修改后的拟合函数 fit_wind_speed_v2 params_df = fit_wind_speed_v1(long_data) u_result_dict[height] = params_df # 汇总结果为 DataFrame u_final_result = pd.concat(u_result_dict, names=["height", "date"]) # 2-拟合经向风------------------------------------------ v_result_dict = {} for height in heights: print(f"Processing height: {height} m") height_data = vwind_data[vwind_data["height"] == height] long_data = pd.melt( height_data, id_vars=["time", "height"], var_name="date", value_name="wind_speed", ) long_data["date"] = pd.to_datetime(long_data["date"], format="%Y%m%d") # 调用修改后的拟合函数 fit_wind_speed_v2 params_df = fit_wind_speed_v1(long_data) v_result_dict[height] = params_df # 汇总结果为 DataFrame v_final_result = pd.concat(v_result_dict, names=["height", "date"]) # 定义振幅参数 amplitude_param = 'a' # 提取纬向风数据,重置索引为平坦结构 u_heatmap_data = u_final_result[amplitude_param].unstack(level=0) # 将高度作为列,日期作为行 u_heatmap_data.index = pd.to_datetime(u_heatmap_data.index) # 确保日期为时间格式 u_heatmap_data = u_heatmap_data.iloc[:, ::-1] # 按高度降序排列 # 提取经向风数据,重置索引为平坦结构 v_heatmap_data = v_final_result[amplitude_param].unstack(level=0) # 将高度作为列,日期作为行 v_heatmap_data.index = pd.to_datetime(v_heatmap_data.index) # 确保日期为时间格式 v_heatmap_data = v_heatmap_data.iloc[:, ::-1] # 按高度降序排列 # 创建画布和子图 fig, axes = plt.subplots(1, 2, figsize=(18, 8)) # 1行2列的布局 fig.suptitle('2022年2日_行星波振幅时空变化', fontsize=20) # 添加总标题 # 绘制第一幅热力图(纬向风) sns.heatmap( u_heatmap_data.T, # 转置,横轴为时间,纵轴为高度 cmap="viridis", # 配色方案 vmax=100, # 设置颜色范围的最大值 cbar_kws={'label': '振幅 (m/s)'}, # 设置颜色条标签 xticklabels=False, # 禁用默认的横轴刻度标签 #yticklabels=u_heatmap_data.columns.astype(str), # 显示纵轴的高度 yticklabels=[f'{height // 1000}' for height in u_heatmap_data.columns], # 转换高度为千米并格式化 ax=axes[0] # 指定子图 ) axes[0].set_title('纬向风振幅变化', fontsize=14) axes[0].set_xlabel('日期', fontsize=12) axes[0].set_ylabel('高度 (km)', fontsize=12) # 绘制第二幅热力图(经向风) sns.heatmap( v_heatmap_data.T, # 转置,横轴为时间,纵轴为高度 cmap="viridis", # 配色方案 vmax=100, # 设置颜色范围的最大值 cbar_kws={'label': '振幅 (m/s)'}, # 设置颜色条标签 xticklabels=False, # 禁用默认的横轴刻度标签 #yticklabels=v_heatmap_data.columns.astype(str), # 显示纵轴的高度 yticklabels=[f'{height // 1000}' for height in v_heatmap_data.columns], # 转换高度为千米并格式化 ax=axes[1] # 指定子图 ) axes[1].set_title('经向风振幅变化', fontsize=14) axes[1].set_xlabel('日期', fontsize=12) axes[1].set_ylabel('高度 (km)', fontsize=12) # 调整布局 plt.tight_layout(rect=[0, 0, 1, 0.95]) # 为总标题留出空间 plt.show() # 输出路径和文件名 picture_file_name = f'{year}年_2日_行星波振幅时空变化.png' plt.savefig(os.path.join(output_dir, picture_file_name)) # -----------------------------------------得到5日行星波拟合参数------------------------------------------- # 定义新的拟合函数 def fit_wind_speed_v2(data, window=15): # 动态调整最小有效数据点数量:参数数量 * 6 num_params = 3 # 模型参数数量 # 转换为 pandas DataFrame 并设置时间为索引 data["date"] = pd.to_datetime(data["date"]) data.set_index("date", inplace=True) # 获取唯一日期 unique_dates = np.unique(data.index.date) # 存储拟合参数 params = [] for date in unique_dates: start_date = pd.to_datetime(date) - pd.Timedelta(days=(window - 1) / 2) end_date = pd.to_datetime(date) + pd.Timedelta(days=(window - 1) / 2) # 获取这几天的数据 window_data = data[start_date:end_date] t = np.arange(len(window_data)) + 1 # 时间点 y = window_data['wind_speed'].values # 风速数据 # 将 y 转换为浮点类型,以防有非数值类型数据 y = pd.to_numeric(y, errors='coerce') # 将非数值转换为 NaN # 去掉包含nan的那一列,筛选有效数据 valid_mask = ~np.isnan(y) # 创建布尔掩码,标记非 NaN 的位置 t = t[valid_mask] y = y[valid_mask] # 检查筛选后的数据是否足够 if len(y) < num_params * 6: # 确保数据足够 # print(f"Not enough valid data after filtering for date: {date}") params.append([None] * 3) # 如果数据不足,记录None continue # 设置初始参数 initial_guess = [0, 1, 0] # 新模型的初始猜测参数 bounds = ( [-np.inf, 0, -np.inf], # 下界 [np.inf, np.inf, np.inf], # 上界 ) # 拟合模型 try: popt, _ = curve_fit(planetary_model_5day, t, y, p0=initial_guess, bounds=bounds) params.append(popt) # 存储拟合参数 except RuntimeError: params.append([None] * 3) # 如果拟合失败,记录None # 转换拟合参数为 DataFrame params_df = pd.DataFrame( params, columns=["v0", "a", "b"], index=unique_dates, ) return params_df # 1-拟合纬向风------------------------------------------ u_result_dict = {} for height in heights: print(f"Processing height: {height} m") # 获取当前高度的纬向风数据 height_data = uwind_data[uwind_data["height"] == height] # 转换为长格式(适配拟合函数) long_data = pd.melt( height_data, id_vars=["time", "height"], var_name="date", value_name="wind_speed", ) long_data["date"] = pd.to_datetime(long_data["date"], format="%Y%m%d") # 调用修改后的拟合函数 fit_wind_speed_v2 params_df = fit_wind_speed_v2(long_data) u_result_dict[height] = params_df # 汇总结果为 DataFrame u_final_result = pd.concat(u_result_dict, names=["height", "date"]) ''' # 将结果保存为 TXT 文档 file_name = f'{year}年_纬向风_5日行星波振幅参数.txt' u_final_result.to_csv( os.path.join(output_dir, file_name), sep='\t', index=True, float_format='%.2f', # 控制数值列保留 2 位小数 header=True) ''' # 2-拟合经向风------------------------------------------ v_result_dict = {} for height in heights: print(f"Processing height: {height} m") height_data = vwind_data[vwind_data["height"] == height] long_data = pd.melt( height_data, id_vars=["time", "height"], var_name="date", value_name="wind_speed", ) long_data["date"] = pd.to_datetime(long_data["date"], format="%Y%m%d") # 调用修改后的拟合函数 fit_wind_speed_v2 params_df = fit_wind_speed_v2(long_data) v_result_dict[height] = params_df # 汇总结果为 DataFrame v_final_result = pd.concat(v_result_dict, names=["height", "date"]) ''' # 保存参数 file_name = f'{year}年_经向风_5日行星波振幅参数.txt' v_final_result.to_csv( os.path.join(output_dir, file_name), sep='\t', index=True, float_format='%.2f', # 控制数值列保留 2 位小数 header=True) ''' # 定义振幅参数 amplitude_param = 'a' # 提取纬向风数据,重置索引为平坦结构 u_heatmap_data = u_final_result[amplitude_param].unstack(level=0) # 将高度作为列,日期作为行 u_heatmap_data.index = pd.to_datetime(u_heatmap_data.index) # 确保日期为时间格式 u_heatmap_data = u_heatmap_data.iloc[:, ::-1] # 按高度降序排列 # 提取经向风数据,重置索引为平坦结构 v_heatmap_data = v_final_result[amplitude_param].unstack(level=0) # 将高度作为列,日期作为行 v_heatmap_data.index = pd.to_datetime(v_heatmap_data.index) # 确保日期为时间格式 v_heatmap_data = v_heatmap_data.iloc[:, ::-1] # 按高度降序排列 # 创建画布和子图 fig, axes = plt.subplots(1, 2, figsize=(18, 8)) # 1行2列的布局 fig.suptitle('2022年5日_行星波振幅时空变化', fontsize=20) # 添加总标题 # 绘制第一幅热力图(纬向风) sns.heatmap( u_heatmap_data.T, # 转置,横轴为时间,纵轴为高度 cmap="viridis", # 配色方案 vmax=100, # 设置颜色范围的最大值 cbar_kws={'label': '振幅 (m/s)'}, # 设置颜色条标签 xticklabels=False, # 禁用默认的横轴刻度标签 #yticklabels=u_heatmap_data.columns.astype(str), # 显示纵轴的高度 yticklabels=[f'{height // 1000}' for height in u_heatmap_data.columns], # 转换高度为千米并格式化 ax=axes[0] # 指定子图 ) axes[0].set_title('纬向风振幅变化', fontsize=14) axes[0].set_xlabel('日期', fontsize=12) axes[0].set_ylabel('高度 (km)', fontsize=12) # 绘制第二幅热力图(经向风) sns.heatmap( v_heatmap_data.T, # 转置,横轴为时间,纵轴为高度 cmap="viridis", # 配色方案 vmax=100, # 设置颜色范围的最大值 cbar_kws={'label': '振幅 (m/s)'}, # 设置颜色条标签 xticklabels=False, # 禁用默认的横轴刻度标签 #yticklabels=v_heatmap_data.columns.astype(str), # 显示纵轴的高度 yticklabels=[f'{height // 1000}' for height in v_heatmap_data.columns], # 转换高度为千米并格式化 ax=axes[1] # 指定子图 ) axes[1].set_title('经向风振幅变化', fontsize=14) axes[1].set_xlabel('日期', fontsize=12) axes[1].set_ylabel('高度 (km)', fontsize=12) # 调整布局 plt.tight_layout(rect=[0, 0, 1, 0.95]) # 为总标题留出空间 plt.show() # 输出路径和文件名 picture_file_name = f'{year}年_5日_行星波振幅时空变化.png' plt.savefig(os.path.join(output_dir, picture_file_name)) # -----------------------------------------得到10日行星波拟合参数------------------------------------------- # 定义新的拟合函数 def fit_wind_speed_v3(data, window=29): # 动态调整最小有效数据点数量:参数数量 * 6 num_params = 3 # 模型参数数量 # 转换为 pandas DataFrame 并设置时间为索引 data["date"] = pd.to_datetime(data["date"]) data.set_index("date", inplace=True) # 获取唯一日期 unique_dates = np.unique(data.index.date) # 存储拟合参数 params = [] for date in unique_dates: start_date = pd.to_datetime(date) - pd.Timedelta(days=(window - 1) / 2) end_date = pd.to_datetime(date) + pd.Timedelta(days=(window - 1) / 2) # 获取这几天的数据 window_data = data[start_date:end_date] t = np.arange(len(window_data)) + 1 # 时间点 y = window_data['wind_speed'].values # 风速数据 # 将 y 转换为浮点类型,以防有非数值类型数据 y = pd.to_numeric(y, errors='coerce') # 将非数值转换为 NaN # 去掉包含nan的那一列,筛选有效数据 valid_mask = ~np.isnan(y) # 创建布尔掩码,标记非 NaN 的位置 t = t[valid_mask] y = y[valid_mask] # 检查筛选后的数据是否足够 if len(y) < num_params * 6: # 确保数据足够 # print(f"Not enough valid data after filtering for date: {date}") params.append([None] * 3) # 如果数据不足,记录None continue # 设置初始参数 initial_guess = [0, 1, 0] # 新模型的初始猜测参数 bounds = ( [-np.inf, 0, -np.inf], # 下界 [np.inf, np.inf, np.inf], # 上界 ) # 拟合模型 try: popt, _ = curve_fit(planetary_model_10day, t, y, p0=initial_guess, bounds=bounds) params.append(popt) # 存储拟合参数 except RuntimeError: params.append([None] * 3) # 如果拟合失败,记录None # 转换拟合参数为 DataFrame params_df = pd.DataFrame( params, columns=["v0", "a", "b"], index=unique_dates, ) return params_df # 1-拟合纬向风------------------------------------------ u_result_dict = {} for height in heights: print(f"Processing height: {height} m") # 获取当前高度的纬向风数据 height_data = uwind_data[uwind_data["height"] == height] # 转换为长格式(适配拟合函数) long_data = pd.melt( height_data, id_vars=["time", "height"], var_name="date", value_name="wind_speed", ) long_data["date"] = pd.to_datetime(long_data["date"], format="%Y%m%d") # 调用修改后的拟合函数 fit_wind_speed_v3 params_df = fit_wind_speed_v3(long_data) u_result_dict[height] = params_df # 汇总结果为 DataFrame u_final_result = pd.concat(u_result_dict, names=["height", "date"]) # 2-拟合经向风------------------------------------------ v_result_dict = {} for height in heights: print(f"Processing height: {height} m") height_data = vwind_data[vwind_data["height"] == height] long_data = pd.melt( height_data, id_vars=["time", "height"], var_name="date", value_name="wind_speed", ) long_data["date"] = pd.to_datetime(long_data["date"], format="%Y%m%d") # 调用修改后的拟合函数 fit_wind_speed_v3 params_df = fit_wind_speed_v3(long_data) v_result_dict[height] = params_df # 汇总结果为 DataFrame v_final_result = pd.concat(v_result_dict, names=["height", "date"]) # 定义振幅参数 amplitude_param = 'a' # 提取纬向风数据,重置索引为平坦结构 u_heatmap_data = u_final_result[amplitude_param].unstack(level=0) # 将高度作为列,日期作为行 u_heatmap_data.index = pd.to_datetime(u_heatmap_data.index) # 确保日期为时间格式 u_heatmap_data = u_heatmap_data.iloc[:, ::-1] # 按高度降序排列 # 提取经向风数据,重置索引为平坦结构 v_heatmap_data = v_final_result[amplitude_param].unstack(level=0) # 将高度作为列,日期作为行 v_heatmap_data.index = pd.to_datetime(v_heatmap_data.index) # 确保日期为时间格式 v_heatmap_data = v_heatmap_data.iloc[:, ::-1] # 按高度降序排列 # 创建画布和子图 fig, axes = plt.subplots(1, 2, figsize=(18, 8)) # 1行2列的布局 fig.suptitle('2022年10日_行星波振幅时空变化', fontsize=20) # 添加总标题 # 绘制第一幅热力图(纬向风) sns.heatmap( u_heatmap_data.T, # 转置,横轴为时间,纵轴为高度 cmap="viridis", # 配色方案 vmax=100, # 设置颜色范围的最大值 cbar_kws={'label': '振幅 (m/s)'}, # 设置颜色条标签 xticklabels=False, # 禁用默认的横轴刻度标签 #yticklabels=u_heatmap_data.columns.astype(str), # 显示纵轴的高度 yticklabels=[f'{height // 1000}' for height in u_heatmap_data.columns], # 转换高度为千米并格式化 ax=axes[0] # 指定子图 ) axes[0].set_title('纬向风振幅变化', fontsize=14) axes[0].set_xlabel('日期', fontsize=12) axes[0].set_ylabel('高度 (m)', fontsize=12) # 绘制第二幅热力图(经向风) sns.heatmap( v_heatmap_data.T, # 转置,横轴为时间,纵轴为高度 cmap="viridis", # 配色方案 vmax=100, # 设置颜色范围的最大值 cbar_kws={'label': '振幅 (m/s)'}, # 设置颜色条标签 xticklabels=False, # 禁用默认的横轴刻度标签 #yticklabels=v_heatmap_data.columns.astype(str), # 显示纵轴的高度 yticklabels=[f'{height // 1000}' for height in u_heatmap_data.columns], # 转换高度为千米并格式化 ax=axes[1] # 指定子图 ) axes[1].set_title('经向风振幅变化', fontsize=14) axes[1].set_xlabel('日期', fontsize=12) axes[1].set_ylabel('高度 (m)', fontsize=12) # 调整布局 plt.tight_layout(rect=[0, 0, 1, 0.95]) # 为总标题留出空间 # 显示或保存图像 plt.show() # 输出路径和文件名 picture_file_name = f'{year}年_10日_行星波振幅时空变化.png' plt.savefig(os.path.join(output_dir, picture_file_name)) # -----------------------------------------得到16日行星波拟合参数------------------------------------------- # 定义新的拟合函数 def fit_wind_speed_v4(data, window=49): # 动态调整最小有效数据点数量:参数数量 * 6 num_params = 3 # 模型参数数量 # 转换为 pandas DataFrame 并设置时间为索引 data["date"] = pd.to_datetime(data["date"]) data.set_index("date", inplace=True) # 获取唯一日期 unique_dates = np.unique(data.index.date) # 存储拟合参数 params = [] for date in unique_dates: start_date = pd.to_datetime(date) - pd.Timedelta(days=(window - 1) / 2) end_date = pd.to_datetime(date) + pd.Timedelta(days=(window - 1) / 2) # 获取这几天的数据 window_data = data[start_date:end_date] t = np.arange(len(window_data)) + 1 # 时间点 y = window_data['wind_speed'].values # 风速数据 # 将 y 转换为浮点类型,以防有非数值类型数据 y = pd.to_numeric(y, errors='coerce') # 将非数值转换为 NaN # 去掉包含nan的那一列,筛选有效数据 valid_mask = ~np.isnan(y) # 创建布尔掩码,标记非 NaN 的位置 t = t[valid_mask] y = y[valid_mask] # 检查筛选后的数据是否足够 if len(y) < num_params * 6: # 确保数据足够 # print(f"Not enough valid data after filtering for date: {date}") params.append([None] * 3) # 如果数据不足,记录None continue # 设置初始参数 initial_guess = [0, 1, 0] # 新模型的初始猜测参数 bounds = ( [-np.inf, 0, -np.inf], # 下界 [np.inf, np.inf, np.inf], # 上界 ) # 拟合模型 try: popt, _ = curve_fit(planetary_model_16day, t, y, p0=initial_guess, bounds=bounds) params.append(popt) # 存储拟合参数 except RuntimeError: params.append([None] * 3) # 如果拟合失败,记录None # 转换拟合参数为 DataFrame params_df = pd.DataFrame( params, columns=["v0", "a", "b"], index=unique_dates, ) return params_df # 1-拟合纬向风------------------------------------------ u_result_dict = {} for height in heights: print(f"Processing height: {height} m") # 获取当前高度的纬向风数据 height_data = uwind_data[uwind_data["height"] == height] # 转换为长格式(适配拟合函数) long_data = pd.melt( height_data, id_vars=["time", "height"], var_name="date", value_name="wind_speed", ) long_data["date"] = pd.to_datetime(long_data["date"], format="%Y%m%d") # 调用修改后的拟合函数 fit_wind_speed_v4 params_df = fit_wind_speed_v4(long_data) u_result_dict[height] = params_df # 汇总结果为 DataFrame u_final_result = pd.concat(u_result_dict, names=["height", "date"]) # 2-拟合经向风------------------------------------------ v_result_dict = {} for height in heights: print(f"Processing height: {height} m") height_data = vwind_data[vwind_data["height"] == height] long_data = pd.melt( height_data, id_vars=["time", "height"], var_name="date", value_name="wind_speed", ) long_data["date"] = pd.to_datetime(long_data["date"], format="%Y%m%d") # 调用修改后的拟合函数 fit_wind_speed_v4 params_df = fit_wind_speed_v4(long_data) v_result_dict[height] = params_df # 汇总结果为 DataFrame v_final_result = pd.concat(v_result_dict, names=["height", "date"]) # 定义振幅参数 amplitude_param = 'a' # 提取纬向风数据,重置索引为平坦结构 u_heatmap_data = u_final_result[amplitude_param].unstack(level=0) # 将高度作为列,日期作为行 u_heatmap_data.index = pd.to_datetime(u_heatmap_data.index) # 确保日期为时间格式 u_heatmap_data = u_heatmap_data.iloc[:, ::-1] # 按高度降序排列 # 提取经向风数据,重置索引为平坦结构 v_heatmap_data = v_final_result[amplitude_param].unstack(level=0) # 将高度作为列,日期作为行 v_heatmap_data.index = pd.to_datetime(v_heatmap_data.index) # 确保日期为时间格式 v_heatmap_data = v_heatmap_data.iloc[:, ::-1] # 按高度降序排列 # 创建画布和子图 fig, axes = plt.subplots(1, 2, figsize=(18, 8)) # 1行2列的布局 fig.suptitle('2022年16日_行星波振幅时空变化', fontsize=20) # 添加总标题 # 绘制第一幅热力图(纬向风) sns.heatmap( u_heatmap_data.T, # 转置,横轴为时间,纵轴为高度 cmap="viridis", # 配色方案 vmax=100, # 设置颜色范围的最大值 cbar_kws={'label': '振幅 (m/s)'}, # 设置颜色条标签 xticklabels=False, # 禁用默认的横轴刻度标签 #yticklabels=u_heatmap_data.columns.astype(str), # 显示纵轴的高度 yticklabels=[f'{height // 1000}' for height in u_heatmap_data.columns], # 转换高度为千米并格式化 ax=axes[0] # 指定子图 ) axes[0].set_title('纬向风振幅变化', fontsize=14) axes[0].set_xlabel('日期', fontsize=12) axes[0].set_ylabel('高度 (m)', fontsize=12) # 绘制第二幅热力图(经向风) sns.heatmap( v_heatmap_data.T, # 转置,横轴为时间,纵轴为高度 cmap="viridis", # 配色方案 vmax=100, # 设置颜色范围的最大值 cbar_kws={'label': '振幅 (m/s)'}, # 设置颜色条标签 xticklabels=False, # 禁用默认的横轴刻度标签 #yticklabels=v_heatmap_data.columns.astype(str), # 显示纵轴的高度 yticklabels=[f'{height // 1000}' for height in v_heatmap_data.columns], # 转换高度为千米并格式化 ax=axes[1] # 指定子图 ) axes[1].set_title('经向风振幅变化', fontsize=14) axes[1].set_xlabel('日期', fontsize=12) axes[1].set_ylabel('高度 (m)', fontsize=12) # 调整布局 plt.tight_layout(rect=[0, 0, 1, 0.95]) # 为总标题留出空间 # 显示或保存图像 plt.show() # 输出路径和文件名 picture_file_name = f'{year}年_16日_行星波振幅时空变化.png' plt.savefig(os.path.join(output_dir, picture_file_name))