zephyr-backend/radar/plot_original.py
2025-01-08 12:53:55 +08:00

263 lines
9.5 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from io import BytesIO
import pandas as pd
import numpy as np
import os
from scipy.optimize import curve_fit
import matplotlib.pyplot as plt
# 解决绘图中中文不能显示的问题
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)
# 统一管理模型及其参数
MODELS = {
'潮汐波': {
'function': tidal_model,
'param_names': ['v0', 'a1', 'b1', 'a2', 'b2', 'a3', 'b3', 'a4', 'b4'],
'initial_guess': [0, 1, 0, 1, 0, 1, 0, 1, 0],
'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]),
'window': 5 # 窗口为5天
},
'2日行星波': {
'function': planetary_model_2day,
'param_names': ['v0', 'a', 'b'],
'initial_guess': [0, 1, 0],
'bounds': ([-np.inf, 0, -np.inf], [np.inf, np.inf, np.inf]),
'window': 5 # 窗口为5天
},
'5日行星波': {
'function': planetary_model_5day,
'param_names': ['v0', 'a', 'b'],
'initial_guess': [0, 1, 0],
'bounds': ([-np.inf, 0, -np.inf], [np.inf, np.inf, np.inf]),
'window': 15 # 窗口为15天
},
'10日行星波': {
'function': planetary_model_10day,
'param_names': ['v0', 'a', 'b'],
'initial_guess': [0, 1, 0],
'bounds': ([-np.inf, 0, -np.inf], [np.inf, np.inf, np.inf]),
'window': 29 # 窗口为29天
},
'16日行星波': {
'function': planetary_model_16day,
'param_names': ['v0', 'a', 'b'],
'initial_guess': [0, 1, 0],
'bounds': ([-np.inf, 0, -np.inf], [np.inf, np.inf, np.inf]),
'window': 49 # 窗口为49天
},
}
ALL_MODEL_NAMES = list(MODELS.keys())
# ----------------------------------------------------------第一大部分————————————————————————————————————————————————————
# # 根据纬向风(u)、经向风(v),得到武汉左岭镇站/黑龙江漠河站
# 固定年份2022年固定高度90km大气波动6、8、12、24小时潮汐波2日、5日、10日、16日行星波随时间的变化
# 参数设置,选择高度、时间、站点
# station = '黑龙江漠河站'
def get_df(H, year, station):
# 文件路径设置
file_dir = rf'./radar/data/{station}\{year}' # 数据文件路径
output_dir = rf'./radar/out/{station}\{year}' # 参数txt、图片输出路径
os.makedirs(output_dir, exist_ok=True) # 确保输出路径存在
# 1-提取整个文件夹的数据
# 获取所有 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, sep='\s+') # 读取文件
date_part = os.path.splitext(file_name)[0][-8:] # 提取日期部分
# 处理缺失值,将 1000000 替换为 NaN
df.loc[df['uwind'] == 1000000, 'uwind'] = np.nan
df.loc[df['vwind'] == 1000000, 'vwind'] = np.nan
# 筛选出 height 列为 90km 的行
filtered_df = df[df['height'] == H]
# 重命名列,包含日期信息
filtered_df = filtered_df.rename(columns={
'uwind': f'{date_part}_uwind',
'vwind': f'{date_part}_vwind'
})
# 只保留需要的列
filtered_df = filtered_df[['height', 'time',
f'{date_part}_uwind', f'{date_part}_vwind']]
# 如果 final_df 为空,则直接赋值
if final_df.empty:
final_df = filtered_df
else:
# 按 'height' 和 'time' 列对齐合并
final_df = pd.merge(final_df, filtered_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)]
return final_df
# 此时final_df已经是90km包含完整天的分析数据---------------------------------------------------------
# 通用函数:处理风速数据
def final_plot_v1(wind_type, year, H, model_name, station):
final_df = get_df(H, year, station)
"""
处理风速数据并针对多种模型进行拟合和绘图。
"""
wind_data = final_df[[col for col in final_df.columns if wind_type in col]]
wind_data.columns = [col[:8] for col in wind_data.columns]
result = wind_data.to_numpy().T.flatten()
column_names = np.repeat(wind_data.columns.to_numpy(), 24)
combined = pd.DataFrame(np.vstack((column_names, result)))
dates = combined.iloc[0].values
wind_speed = combined.iloc[1].values
date_series = pd.to_datetime(dates)
data = pd.DataFrame({'date': date_series, 'wind_speed': wind_speed})
data.set_index('date', inplace=True)
unique_dates = np.unique(data.index.date)
if model_name not in MODELS:
raise ValueError(f"Model {model_name} not found")
return
model_info = MODELS[model_name]
params = []
model_func = model_info['function']
param_names = model_info['param_names']
initial_guess = model_info['initial_guess']
bounds = model_info['bounds']
window_days = model_info['window'] # 获取窗口长度
for date in unique_dates:
# 根据模型窗口调整时间范围
start_date = pd.to_datetime(
date) - pd.Timedelta(days=(window_days - 1) / 2)
end_date = pd.to_datetime(
date) + pd.Timedelta(days=(window_days - 1) / 2)
window_data = data[start_date:end_date]
t = np.arange(len(window_data)) + 1
y = pd.to_numeric(window_data['wind_speed'], errors='coerce')
valid_mask = ~np.isnan(y)
t = t[valid_mask]
y = y[valid_mask]
if len(y) < len(initial_guess) * 6:
print(
f"Not enough valid data after filtering for date: {date}")
params.append([None] * len(param_names))
continue
try:
popt, _ = curve_fit(
model_func, t, y, p0=initial_guess, bounds=bounds)
params.append(popt)
except RuntimeError:
print(f"Fitting failed for date: {date}")
params.append([None] * len(param_names))
# 保存参数
params_df = pd.DataFrame(
params, columns=param_names, index=unique_dates)
# file_name = f'{year}年_{wind_type}_{H // 1000}km_{model_name}_强度.txt'
# params_df.to_csv(os.path.join(output_dir, file_name),
# sep='\t', index=True, header=True)
# 绘图
if 'a1' in params_df.columns: # 处理潮汐波多分量
plt.figure(figsize=(12, 6))
for i, T in enumerate([6, 8, 12, 24], start=1):
plt.plot(params_df[f'a{i}'], label=f'{T}h', linewidth=2)
else: # 处理行星波单分量
plt.figure(figsize=(12, 6))
plt.plot(
params_df['a'], label=f'{model_name}', color='orange', linewidth=2)
plt.title(
f'{year}{wind_type}_{H // 1000}km_{model_name}_强度', fontsize=16)
plt.xlabel('日期', fontsize=14)
plt.ylabel('幅度 (m/s)', fontsize=14)
plt.xticks(rotation=45)
plt.legend()
plt.tick_params(axis='both', labelsize=12) # 调整刻度字体大小
plt.tight_layout()
# picture_file_name = f'{year}年_{wind_type}_{H // 1000}km_{model_name}_强度.png'
# plt.savefig(os.path.join(output_dir, picture_file_name))
# plt.show()
buffer = BytesIO()
plt.savefig(buffer, format='png')
buffer.seek(0)
plt.close()
return buffer
# 处理纬向风和经向风,适配所有模型
if __name__ == "__main__":
pass
# final_plot_v1('uwind', year, H, "")
# final_plot_v1('vwind', year, H, "")