with tidi

This commit is contained in:
Dustella 2025-01-15 14:48:48 +08:00
parent eb7f4420ff
commit 104e253e2b
Signed by: Dustella
GPG Key ID: 35AA0AA3DC402D5C
30 changed files with 9560 additions and 9833 deletions

View File

@ -1,6 +1,7 @@
from gevent import pywsgi, monkey
monkey.patch_all()
import tidi
from utils import *
import saber
import radar
@ -9,13 +10,16 @@ from flask import Flask
from flask_cors import CORS
from typing import get_args
import sys
import matplotlib.font_manager as fm
app = Flask(__name__)
fm.fontManager.addfont("./SimHei.ttf")
app.register_blueprint(balloon.balloon_module, url_prefix="/balloon")
app.register_blueprint(radar.radar_module, url_prefix="/radar")
app.register_blueprint(saber.saber_module, url_prefix="/saber")
app.register_blueprint(tidi.tidi_module, url_prefix="/tidi")
# allow cors
CORS(app)
@ -32,4 +36,4 @@ if __name__ == '__main__':
server.serve_forever()
elif 'debug' in args:
app.run(debug=True)
app.run("0.0.0.0",debug=True)

View File

@ -1,14 +1,10 @@
import asyncio
import glob
from io import BytesIO
from flask import Blueprint, request, send_file
from radar.plot_original import final_plot_v1, ALL_MODEL_NAMES
from matplotlib import pyplot as plt
from radar.plot_original import final_render_v2
from radar.plot_prod import final_plot_v2
import threading
plot_v1 = final_plot_v1
plot_v2 = final_plot_v2
all_model_names = ALL_MODEL_NAMES
globed_all_files = glob.glob("./radar/data/**/**.txt", recursive=True)
@ -19,15 +15,15 @@ radar_module = Blueprint("Radar", __name__)
@radar_module.route("/metadata")
def get_all_files():
return globed_all_files
return final_render_v2.get_all_pathes()
@radar_module.route("/metadata/models")
def get_all_models():
return all_model_names
return final_render_v2.get_all_models()
@radar_module.route('/render/v1')
@radar_module.route('/render/heatmap')
def render_v1():
"""
wind_type: Any,
@ -38,13 +34,31 @@ def render_v1():
wind_type = request.args.get('wind_type')
year = request.args.get('year')
H = request.args.get('H')
model_name = request.args.get('model_name')
station = request.args.get('station')
buf = plot_v1(wind_type, int(year), int(H), model_name, station)
model_name = request.args.get('model_name')
mode = request.args.get('mode')
renderer = final_render_v2(int(H), int(year), station, wind_type)
if mode=="day":
day = request.args.get('day')
renderer.render_day(day,model_name)
elif mode=="month":
month = request.args.get('month')
renderer.render_month(int(month),model_name)
elif mode=="year":
renderer.render_year(model_name)
else:
raise ValueError("mode not supported")
buf = BytesIO()
plt.savefig(buf, format='png')
buf.seek(0)
# close the plot
plt.close()
return send_file(buf, mimetype='image/png')
@radar_module.route('/render/v2')
@radar_module.route('/render/changes')
def render_v2():
"""
year: Any,
@ -54,8 +68,17 @@ def render_v2():
year = request.args.get('year')
station = request.args.get('station')
model_name = request.args.get('model_name')
# async_plot_v2 = asyncio.coroutine(plot_v2)
start_month = request.args.get('start_month')
end_month = request.args.get('end_month')
if start_month is not None and end_month is not None:
start_month = int(start_month)
end_month = int(end_month)
month_range = (start_month, end_month)
else:
month_range = (1, 12)
buffer = plot_v2(int(year), station, model_name)
buffer = final_plot_v2(int(year), station, model_name, month_range)
buffer.seek(0)
return send_file(buffer, mimetype='image/png')

View File

@ -1,998 +0,0 @@
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))

View File

@ -1,9 +1,19 @@
from io import BytesIO
# 此代码可以绘制某年、某高度的纬向风/经向风的,潮汐波和行星波的
# 年振幅图、月振幅图、日拟合正弦波图
# 基本思想是1-先处理某高度全年观测数据得到final_df
# 2-再选择风的类型,得到此高度全年每一天的纬向风/经向风大气长波拟合参数函数process_wind_data_with_models
# 3-下一步就是根据参数绘制图形,可选择年振幅图、月振幅图、日拟合正弦波图(#todo:上一步风的类型已确定)
# plot_yearly_params、plot_month_params、plot_sine_wave_for_day
import glob
import pandas as pd
import numpy as np
import os
from scipy.optimize import curve_fit
import matplotlib.pyplot as plt
from datetime import datetime
# 解决绘图中中文不能显示的问题
import matplotlib
@ -12,8 +22,6 @@ 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 # 周期以小时为单位
@ -22,31 +30,26 @@ def tidal_model(t, v0, a1, b1, a2, b2, a3, b3, a4, b4):
+ 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 = {
'潮汐波': {
@ -87,23 +90,298 @@ MODELS = {
},
}
ALL_MODEL_NAMES = list(MODELS.keys())
################################ 选定风的类型,得到整年的每一天拟合参数
def process_wind_data_with_models(final_df, wind_type, year, H, selected_models=None):
"""
处理风速数据并针对多种模型进行拟合和绘图
# ----------------------------------------------------------第一大部分————————————————————————————————————————————————————
# # 根据纬向风(u)、经向风(v),得到武汉左岭镇站/黑龙江漠河站
# 固定年份2022年固定高度90km大气波动6、8、12、24小时潮汐波2日、5日、10日、16日行星波随时间的变化
:param selected_models: 可选指定使用的模型类型列表如果为 None则处理所有模型
:return: 整年所有模型的拟合参数字典
"""
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 selected_models is None:
models_to_process = MODELS.keys() # 默认处理所有模型
else:
models_to_process = selected_models # 使用指定的模型列表
all_params = {} # 存储所有模型的参数数据框
for model_name in models_to_process:
if model_name not in MODELS:
print(f"Model {model_name} is not valid.")
continue
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) * 2: # todo认为如果数据点个数少于参数的2倍则认为数据不够拟合之前是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)
all_params[model_name] = params_df # 将每个模型的参数数据框存入字典
return all_params # 返回包含所有模型参数的数据字典
# 外部绘制全年图形
def plot_yearly_params(params_dict, model_name,year, wind_type, H):
"""
绘制全年的参数图形
:param params_dict: 存储所有模型的参数数据框字典
:param model_name: 模型名称
"""
params_df = params_dict[model_name]
# 绘图
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()
# 绘制月振幅图
def plot_month_params(params_dict, model_name, wind_type, year, month, H):
"""
绘制指定月份的参数图形
:param params_dict: 存储所有模型的参数数据框字典
:param model_name: 模型名称
:param wind_type: 风速类型
:param year: 需要绘制的年份
:param month: 需要绘制的月份
:param output_dir: 输出目录
:param H: 水平尺度
"""
# 获取该年份的数据框
params_df = params_dict[model_name]
# 确保索引是 datetime 类型
if not pd.api.types.is_datetime64_any_dtype(params_df.index):
# print("Index is not datetime. Converting to datetime...")
params_df.index = pd.to_datetime(params_df.index)
# 筛选出指定年份和月份的数据
params_df_month = params_df[(params_df.index.year == year) & (params_df.index.month == month)]
# 检查筛选后的数据是否为空
if params_df_month.empty:
print(f"No data available for {year}-{month}.")
return
# 绘图
plt.figure(figsize=(12, 6))
# 判断是潮汐波还是行星波
if model_name == '潮汐波': # 处理潮汐波多分量
for i, T in enumerate([6, 8, 12, 24], start=1):
col_name = f'a{i}'
if col_name in params_df_month.columns:
plt.plot(params_df_month[col_name], label=f'{T}h', linewidth=2)
else:
print(f"Warning: Column '{col_name}' not found in the dataframe.")
else: # 处理行星波单分量
if 'a' in params_df_month.columns:
plt.plot(params_df_month['a'], label=f'{model_name}', color='orange', linewidth=2)
else:
print(f"Warning: Column 'a' not found in the dataframe.")
# 设置标题和标签
plt.title(f'{year}{month}{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()
# 绘制日拟合正弦波图
def plot_sine_wave_for_day(params_df, date, wind_type, H, model_name):
"""
根据指定日期的参数绘制标准的正弦曲线图
:param params_df: 存储所有模型的参数数据框
:param date: 指定日期例如 '2024-03-17'
:param wind_type: 风速类型例如 'uwind'
:param H: 水平尺度
:param model_name: 模型名称例如 '5日行星波', '10日行星波', '潮汐波'
"""
# 将字符串日期转换为 datetime 格式
try:
date = datetime.strptime(date, '%Y-%m-%d')
except ValueError:
print(f"Error: Invalid date format '{date}', expected 'YYYY-MM-DD'.")
return
# 检查 params_df.index 是否为 datetime 类型,若不是,则将其转换
if not isinstance(params_df.index, pd.DatetimeIndex):
print("Warning: params_df index is not of type datetime. Converting index to datetime...")
params_df.index = pd.to_datetime(params_df.index)
# 获取指定日期的参数
if date not in params_df.index:
print(f"Warning: {date.strftime('%Y-%m-%d')} not found in the parameter dataframe.")
return
params = params_df.loc[date]
# 检查参数是否为 NaN 或 Inf
if params.isnull().any() or np.isinf(params).any():
print(f"Warning: Parameters for {date.strftime('%Y-%m-%d')} contain NaN or Inf values. Skipping the plot.")
return
# 判断是否是潮汐波模型
if 'a1' in params: # 如果是潮汐波模型
T1, T2, T3, T4 = 6, 8, 12, 24 # 潮汐波的周期
# 生成时间数据
t = np.linspace(0, 48, 1000) # 生成0到48小时的1000个点
# 从参数中提取值
v0 = params['v0']
a1 = params['a1']
b1 = params['b1']
a2 = params['a2']
b2 = params['b2']
a3 = params['a3']
b3 = params['b3']
a4 = params['a4']
b4 = params['b4']
# 计算正弦波的值
y1 = v0 * np.ones_like(t) # 常量项
y2 = a1 * np.sin((2 * np.pi / T1) * t + b1)
y3 = a2 * np.sin((2 * np.pi / T2) * t + b2)
y4 = a3 * np.sin((2 * np.pi / T3) * t + b3)
y5 = a4 * np.sin((2 * np.pi / T4) * t + b4)
# 绘制图形
plt.figure(figsize=(12, 8))
plt.plot(t, y1, label='常量', color='purple')
plt.plot(t, y2, label='6h', color='blue')
plt.plot(t, y3, label='8h', color='orange')
plt.plot(t, y4, label='12h', color='green')
plt.plot(t, y5, label='24h', color='red')
# 添加图例和标签
plt.title(f'{date.strftime("%Y-%m-%d")} {wind_type}风速拟合潮汐波', fontsize=16)
plt.xlabel('时间 (小时)', fontsize=14)
plt.ylabel('幅度 (m/s)', fontsize=14)
plt.legend()
plt.grid(True)
plt.xlim(0, 48) # 0到48小时
plt.ylim(min(y1.min(), y2.min(), y3.min(), y4.min(), y5.min()-1),
max(y1.max(), y2.max(), y3.max(), y4.max(), y5.max())+1)
plt.tight_layout()
else: # 处理行星波模型
# 根据传入的模型名称选择周期
if model_name == '2日行星波':
T = 48 # 2日行星波的周期为48小时
elif model_name == '5日行星波':
T = 120 # 5日行星波的周期为120小时
elif model_name == '10日行星波':
T = 240 # 10日行星波的周期为240小时
elif model_name == '16日行星波':
T = 384 # 16日行星波的周期为384小时
else:
print(f"Error: Unsupported planetary wave model '{model_name}'.")
return
# 生成时间数据
t = np.linspace(0, T*2, 1000) # 根据周期生成对应的时间范围
# 从参数中提取值
v0 = params['v0']
a = params['a']
b = params['b']
# 计算正弦波的值
y1 = v0 * np.ones_like(t) # 常量项
y2 = a * np.sin((2 * np.pi / T) * t + b)
# 绘制图形
plt.figure(figsize=(12, 8))
plt.plot(t, y1, label='常量', color='purple')
plt.plot(t, y2, label=f'{model_name} ({T}小时)', color='orange')
# 添加图例和标签
plt.title(f'{date.strftime("%Y-%m-%d")} {wind_type}风速拟合 {model_name}', fontsize=16)
plt.xlabel('时间 (小时)', fontsize=14)
plt.ylabel('幅度 (m/s)', fontsize=14)
plt.legend()
plt.grid(True)
plt.xlim(0, T*2) # 根据周期调整时间范围
plt.ylim(min(y1.min(), y2.min()-1), max(y1.max(), y2.max())+1)
plt.tight_layout()
def get_final_df(H, year, station):
# 下边处理,绘图
# 参数设置,选择高度、时间、站点
# station = '黑龙江漠河站'
def get_df(H, year, station):
# # station = '黑龙江漠河站'
# 文件路径设置
file_dir = rf'./radar/data/{station}\{year}' # 数据文件路径
output_dir = rf'./radar/out/{station}\{year}' # 参数txt、图片输出路径
file_dir = rf'./radar/data/{station}/{year}' # 数据文件路径
output_dir = rf'./out\{station}\{year}' # 参数txt、图片输出路径
os.makedirs(output_dir, exist_ok=True) # 确保输出路径存在
# 1-提取整个文件夹的数据
# 1------------------------提取整个文件夹的数据
# 获取所有 txt 文件
file_list = [f for f in os.listdir(file_dir) if f.endswith('.txt')]
@ -113,7 +391,7 @@ def get_df(H, year, station):
# 批量处理每个 txt 文件
for file_name in file_list:
file_path = os.path.join(file_dir, file_name) # 拼接文件路径
df = pd.read_csv(file_path, sep='\s+') # 读取文件
df = pd.read_csv(file_path, delim_whitespace=True) # 读取文件
date_part = os.path.splitext(file_name)[0][-8:] # 提取日期部分
# 处理缺失值,将 1000000 替换为 NaN
@ -130,22 +408,18 @@ def get_df(H, year, station):
})
# 只保留需要的列
filtered_df = filtered_df[['height', 'time',
f'{date_part}_uwind', 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')
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]
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:
@ -155,108 +429,59 @@ def get_df(H, year, station):
# 按列名排序(确保日期顺序)
final_df = final_df[['height', 'time'] + sorted(expected_columns)]
return final_df
# 此时final_df已经是90km包含完整天的分析数据---------------------------------------------------------
# 此时final_df已经是90km包含完整天的观测数据---------------------------------------------------------
# 2---------------------------整年纬向风/经向风拟合参数
# params_dict = process_wind_data_with_models(final_df,'uwind', output_dir, year, H,selected_models=['潮汐波','2日行星波'])
# 通用函数:处理风速数据
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
# params_dict = process_wind_data_with_models(final_df,
# 'uwind', output_dir, year, H,
# selected_models=None)
# 处理纬向风和经向风,适配所有模型
if __name__ == "__main__":
pass
# final_plot_v1('uwind', year, H, "")
# final_plot_v1('vwind', year, H, "")
class final_render_v2():
def __init__(self, H, year, station, wind_type):
self.H , self.year, self.station, self.wind_type = H, year, station, wind_type
final_df = get_final_df(H, year, station)
self.params_dict = process_wind_data_with_models(final_df, wind_type, year, H, selected_models=None)
def render_day(self, date, model_name):
plot_sine_wave_for_day(self.params_dict[model_name], date, self.wind_type, self.H, model_name)
def render_month(self, month, model_name):
plot_month_params(self.params_dict, model_name, self.wind_type, self.year, month, self.H)
def render_year(self, model_name):
plot_yearly_params(self.params_dict, model_name, self.year, self.wind_type, self.H)
@staticmethod
def get_all_models():
return list(MODELS.keys())
@staticmethod
def get_all_pathes():
result = glob.glob(f"./radar/data/**/*.txt", recursive=True)
# normalize path
result = [os.path.normpath(path).replace("\\", "/") for path in result]
return result
# todo:此时得到的参数,年份、高度、风的类型已经固定
# 3---------------------------绘图
# 使用示例:绘制全年图形
if __name__ == '__main__':
# H = 94000 # 高度为 90km
# year = 2022
import matplotlib.font_manager as fm
fm.fontManager.addfont("./SimHei.ttf")
# station = '武汉左岭镇站'
renderer = final_render_v2(94000, 2022, '武汉左岭镇站', 'uwind')
renderer.render_year('潮汐波')
plt.show()
renderer.render_month(3, '潮汐波')
plt.show()
renderer.render_day('2022-03-17', '潮汐波')
plt.show()

View File

@ -1,5 +1,7 @@
import glob
from io import BytesIO
import os
import re
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
@ -30,13 +32,27 @@ def planetary_model(t, v0, a, b, period):
# ------------------------------ 数据处理工具函数 ------------------------------
def preprocess_data(file_dir, year):
def preprocess_data(file_list, year, month_range=(1,12)):
def filter_given_month(month_range, file_list):
begin_month, end_month = month_range
get_date_pattern = r'_(\d{8})\.txt'
filtered_file_list = []
for file in file_list:
date = re.search(get_date_pattern, file).group(1)
month = int(date[4:6])
if month >= begin_month and month <= end_month:
filtered_file_list.append(file)
return filtered_file_list
if file_list == []:
raise ValueError("No data files found.")
file_list = filter_given_month(month_range, file_list)
"""读取数据文件并合并成完整的 DataFrame"""
file_list = [f for f in os.listdir(file_dir) if f.endswith('.txt')]
# file_list = [f for f in os.listdir(file_dir) if f.endswith('.txt')]
final_df = pd.DataFrame()
for file_name in file_list:
file_path = os.path.join(file_dir, file_name)
for file_path in file_list:
file_name = file_path.split('/')[-1]
df = pd.read_csv(file_path, sep='\s+')
# 替换异常值为 NaN
@ -57,8 +73,12 @@ def preprocess_data(file_dir, year):
final_df, df, on=['height', 'time'], how='outer')
# 补全缺失列并排序
begin_month, end_month = month_range
# calculate begin and end date
begin_date = f'{year}-{begin_month:02d}-01'
end_date = f'{year}-{end_month+1:02d}-01' if end_month < 12 else f'{year}-12-31'
all_dates = pd.date_range(
f'{year}-01-01', f'{year}-12-31').strftime('%Y%m%d')
begin_date, end_date).strftime('%Y%m%d')
expected_columns = [f'{date}_uwind' for date in all_dates] + \
[f'{date}_vwind' for date in all_dates]
for col in expected_columns:
@ -94,7 +114,7 @@ def fit_wind_speed(data, model_func, initial_guess, bounds, window, num_params):
t = t[valid_mask]
y = y[valid_mask]
if len(y) < num_params * 6:
if len(y) < num_params * 2:
params.append([None] * len(initial_guess))
continue
@ -121,7 +141,7 @@ def plot_heatmaps(result_dict, param, title, vmax):
heatmap_data = data[param].unstack(level=0)
heatmap_data.index = pd.to_datetime(heatmap_data.index)
heatmap_data = heatmap_data.iloc[:, ::-1]
heatmap_data.index = heatmap_data.index.strftime('%Y-%m-%d')
sns.heatmap(
heatmap_data.T,
cmap="viridis",
@ -140,7 +160,7 @@ def plot_heatmaps(result_dict, param, title, vmax):
# ------------------------------ 主逻辑 ------------------------------
def final_plot_v2(year, station, model_name):
def final_plot_v2(year, station, model_name, month_range=(1,12)):
"""
主逻辑函数用于处理数据并绘制选定模型的热力图
@ -150,12 +170,13 @@ def final_plot_v2(year, station, model_name):
- model_name: str模型名称 '潮汐波', '2日行星波', '5日行星波', '10日行星波', '16日行星波'
"""
# 配置文件路径
file_dir = rf'./radar/data\{station}\{year}'
file_dir = rf'./radar/data/{station}/{year}'
output_dir = rf'./radar/tmp\{station}\{year}\时空'
os.makedirs(output_dir, exist_ok=True)
# 数据预处理
final_df = preprocess_data(file_dir, year)
file_list = glob.glob(file_dir + '/**/*.txt', recursive=True)
final_df = preprocess_data(file_list, year, month_range)
uwind_data = final_df[["height", "time"] +
[col for col in final_df.columns if "_uwind" in col]]
vwind_data = final_df[["height", "time"] +
@ -202,7 +223,7 @@ def final_plot_v2(year, station, model_name):
plot_heatmaps(
{"纬向风": u_final_result, "经向风": v_final_result},
param="param_1", # 振幅参数
title=f'{year}{model_name}振幅时空变化',
title=f'{year}{month_range[0]}-{month_range[1]}{model_name}振幅时空变化',
vmax=100,
)

View File

@ -30,6 +30,8 @@ all_saber_files = glob.glob("./saber/data/**/**.nc", recursive=True)
@saber_module.route("/metadata")
def get_files():
# normalizing the path, and replace \\ with /
all_saber_files = [path.replace("\\", "/") for path in all_saber_files]
return all_saber_files

35
tidi/__init__.py Normal file
View File

@ -0,0 +1,35 @@
import glob
from io import BytesIO
from flask import Blueprint, request, send_file
from matplotlib import pyplot as plt
from tidi.staged.plot import tidi_render
tidi_module = Blueprint("Tidi", __name__)
@tidi_module.route('/metadata')
def get_all_years():
res = glob.glob("./tidi/data/**/**.txt", recursive=True)
# search for the folder name that is year
return {
"path": res
}
@tidi_module.route('/render/wave')
def render_wave():
mode = request.args.get('mode')
year = request.args.get('year')
k = request.args.get('k')
T = request.args.get('T')
year = int(year)
k = int(k)
T = int(T)
tidi_render(mode, year, k, T)
buffer = BytesIO()
plt.savefig(buffer, format="png")
buffer.seek(0)
return send_file(buffer, mimetype="image/png")

205
tidi/staged/plot.py Normal file
View File

@ -0,0 +1,205 @@
#此代码是对数据处理后的txt数据进行行星波参数提取绘图
#2006_TIDI_V_Meridional_data.txt也可以做同样处理一个是经向风提取的行星波一个是纬向风的
import pandas as pd
import numpy as np
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 # 正常显示负号
# 读取一年的数据文件
# 设置初始参数
# initial_guess = [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0] # v0, a1, b1, a2, b2, a3, b3
initial_guess = [0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5,0.5, 0.5, 0.5, 0.5,0.5, 0.5, 0.5, 0.5] # 9个 a 和 9个 b 参数
# 设置参数界限
bounds = (
[0, -np.inf, 0, -np.inf, 0, -np.inf, 0, -np.inf, 0, -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, np.inf, np.inf, np.inf, np.inf, np.inf, np.inf,
np.inf, np.inf, np.inf]) # 上界
# 定义拟合函数
def tidi_render(mode, year, k, T=10):
# 用于存储拟合参数结果的列表
all_fit_results = []
# 设置最小数据量的阈值
min_data_points = 36
if mode != 'V_Zonal' and mode != 'V_Meridional':
raise ValueError('mode must be V_Zonal or V_Meridional')
if k not in list(range(-4, 5)):
raise ValueError('k must be in range(-4, 5)')
df = pd.read_csv(f'./tidi/data/{year}/TIDI_{mode}_data.txt', sep='\s+')
# 删除有 NaN 的行
# V_Meridional
df = df.dropna(subset=[mode])
# 进行多个时间窗口的拟合
#T应该取5、10、16
# todo:对于5日波滑动窗口选择15天
def u_func(x, *params):
a1, b1, a2, b2, a3, b3, a4, b4, a5, b5, a6, b6, a7, b7, a8, b8, a9, b9 = params
return (
a1 * np.sin((2 * np.pi / T) * t - 4 * x + b1)
+ a2 * np.sin((2 * np.pi / T) * t - 3 * x + b2)
+ a3 * np.sin((2 * np.pi / T) * t - 2 * x + b3)
+ a4 * np.sin((2 * np.pi / T) * t - x + b4)
+ a5 * np.sin((2 * np.pi / T) * t + b5)
+ a6 * np.sin((2 * np.pi / T) * t + x + b6)
+ a7 * np.sin((2 * np.pi / T) * t + 2 * x + b7)
+ a8 * np.sin((2 * np.pi / T) * t + 3 * x + b8)
+ a9 * np.sin((2 * np.pi / T) * t + 4 * x + b9)
)
for start_day in range(0, 365-3*T): # 最后一个窗口为[351, 366]
end_day = start_day + 3 * T # 每个窗口的结束时间为 start_day + 3*T
# 选择当前窗口的数据
df_8 = df[(df['UTime'] >= start_day) & (df['UTime'] <= end_day)]
# 检查当前窗口的数据量
if len(df_8) < min_data_points:
# 输出数据量不足的警告
print(f"数据量不足,无法拟合:{start_day}{end_day},数据点数量:{len(df_8)}")
# 将拟合参数设置为 NaN
all_fit_results.append([np.nan] * 18)
continue # 跳过当前时间窗口,继续下一个窗口
# 提取时间、经度、温度数据
t = np.array(df_8['UTime']) # 时间
x = np.array(df_8['Longitude_Radians']) # 经度弧度制
temperature = np.array(df_8[mode]) # 经向风速,因变量
# 用T进行拟合
popt, pcov = curve_fit(u_func, x, temperature, p0=initial_guess, bounds=bounds, maxfev=50000)
# 将拟合结果添加到列表中
all_fit_results.append(popt)
# 将结果转换为DataFrame
columns = ['a1', 'b1', 'a2', 'b2', 'a3', 'b3', 'a4', 'b4', 'a5', 'b5', 'a6', 'b6', 'a7', 'b7', 'a8', 'b8', 'a9', 'b9']
fit_df = pd.DataFrame(all_fit_results, columns=columns) # fit_df即为拟合的参数汇总
# -------------------------------画图----------------------------
#a1-a9,对应波数-4、-3、-2、-1、0、1、2、3、4的行星波振幅
a_columns = ['a1', 'a2', 'a3', 'a4', 'a5', 'a6', 'a7', 'a8', 'a9']
k_values = list(range(-4, 5)) # 从 -4 到 4
# 创建一个字典映射 k 值到 a_columns
k_to_a = {f'k={k}': a for k, a in zip(k_values, a_columns)}
# 获取索引并转换为 numpy 数组
x_values = fit_df.index.to_numpy()
# 对每一列生成独立的图
col = k_to_a[f'k={k}']
plt.figure(figsize=(8, 6)) # 创建新的图形
plt.plot(x_values, fit_df[col].values)
plt.title(f'k={k} 振幅图')
plt.xlabel('Day')
plt.ylabel('振幅')
# 设置横坐标的动态调整
adjusted_x_values = x_values + (3 * T + 1) / 2
if len(adjusted_x_values) > 50:
step = 30
tick_positions = adjusted_x_values[::step] # 选择每30个点
tick_labels = [f'{int(val)}' for val in tick_positions]
else:
tick_positions = adjusted_x_values
tick_labels = [f'{int(val)}' for val in tick_positions]
plt.xticks(ticks=tick_positions, labels=tick_labels)
# plt.show() # 显示图形
if __name__ == '__main__':
tidi_render('V_Zonal', 2015, 1)
tidi_render('V_Meridional', 2015, 1)
# # 用于存储拟合参数结果的列表
# all_fit_results = []
#
# # 设置最小数据量的阈值
# min_data_points = 36
#
# # 进行多个时间窗口的拟合
# # todo:对于5日波滑动窗口选择15天
# for start_day in range(0, 351): # 最后一个窗口为[351, 366]
# end_day = start_day + 15 # 设置每个窗口的结束时间
#
# # 选择当前窗口的数据
# df_8 = df[(df['UTime'] >= start_day) & (df['UTime'] <= end_day)]
#
# # 检查当前窗口的数据量
# if len(df_8) < min_data_points:
# # 输出数据量不足的警告
# print(f"数据量不足,无法拟合:{start_day} 到 {end_day},数据点数量:{len(df_8)}")
# # 将拟合参数设置为 NaN
# all_fit_results.append([np.nan] * 18)
# continue # 跳过当前时间窗口,继续下一个窗口
#
#
# # 提取时间、经度、温度数据
# t = np.array(df_8['UTime']) # 时间
# x = np.array(df_8['Longitude_Radians']) # 经度弧度制
# temperature = np.array(df_8['V_Zonal']) # 经向风速,因变量
#
# # 用T=5进行拟合
# for T in [5]:
# popt, pcov = curve_fit(u_func, x, temperature, p0=initial_guess, bounds=bounds, maxfev=50000)
#
# # 将拟合结果添加到列表中
# all_fit_results.append(popt)
#
# # 将结果转换为DataFrame
# columns = ['a1', 'b1', 'a2', 'b2', 'a3', 'b3', 'a4', 'b4', 'a5', 'b5', 'a6', 'b6', 'a7', 'b7', 'a8', 'b8', 'a9', 'b9']
# fit_df = pd.DataFrame(all_fit_results, columns=columns) # fit_df即为拟合的参数汇总
#
# # -------------------------------画图----------------------------
# # 定义 a1 到 a9 的列名
# a_columns = ['a1', 'a2', 'a3', 'a4', 'a5', 'a6', 'a7', 'a8', 'a9']
# a_values = fit_df[a_columns].values
#
# # 获取索引并转换为 numpy 数组
# x_values = fit_df.index.to_numpy()
#
# # 对每一列生成独立的图
# for i, col in enumerate(a_columns):
# plt.figure(figsize=(8, 6)) # 创建新的图形
# plt.plot(x_values, a_values[:, i])
# plt.title(f'{col} 振幅图')
# plt.xlabel('Day')
# plt.ylabel('振幅')
# plt.show() # 显示图形
# # 提取 a1 到 a9 列的数据
# a_columns = ['a1', 'a2', 'a3', 'a4', 'a5', 'a6', 'a7', 'a8', 'a9']
# a_values = fit_df[a_columns].values
# # 获取索引并转换为 numpy 数组
# x_values = fit_df.index.to_numpy()
#
# # 创建一个图形和子图
# plt.figure(figsize=(10, 6))
#
# # 绘制每一条线
# for i, col in enumerate(a_columns):
# plt.plot(x_values, a_values[:, i], label=col)
#
# # 添加标题、标签和图例
# plt.title('a1 to a9 振幅图')
# plt.xlabel('Day')
# plt.ylabel('振幅')
# plt.legend()
# # 显示图形
# plt.tight_layout()
# plt.show()

210
tidi/staged/process.py Normal file
View File

@ -0,0 +1,210 @@
import os
import numpy as np
from scipy.io import loadmat
import pandas as pd
#第156行代码纬度可以自己选择但是都是每5°做一个选择
#70km-120km每2.5km一个共21个第六个就是82.5km高度的情况,高度也可以自己选择
fixed_height_index = 6
# 初始化空列表来存储每天的数据
height_list = []
lat_list = []
lon_list = []
vmeridional_list = []
vzonal_list = []
# 文件路径
base_path = "./tidi/data/2022/"
# 循环读取从第1天到第365天的数据
for day in range(1, 366): # 365天数据确保循环次数为365
# 构建文件名(使用三位数格式)
day_str = f"{day:03d}" # 格式化数字为三位数前面补0
height_file = f"{day_str}_Height.mat"
lat_file = f"{day_str}_Lat.mat"
lon_file = f"{day_str}_Lon.mat"
vmeridional_file = f"{day_str}_VMerdional.mat"
vzonal_file = f"{day_str}_Vzonal.mat"
# 检查文件是否存在
try:
if not os.path.exists(os.path.join(base_path, height_file)):
raise FileNotFoundError(f"{height_file} not found")
if not os.path.exists(os.path.join(base_path, lat_file)):
raise FileNotFoundError(f"{lat_file} not found")
if not os.path.exists(os.path.join(base_path, lon_file)):
raise FileNotFoundError(f"{lon_file} not found")
if not os.path.exists(os.path.join(base_path, vmeridional_file)):
raise FileNotFoundError(f"{vmeridional_file} not found")
if not os.path.exists(os.path.join(base_path, vzonal_file)):
raise FileNotFoundError(f"{vzonal_file} not found")
# 读取.mat文件
height_data = loadmat(os.path.join(base_path, height_file))
lat_data = loadmat(os.path.join(base_path, lat_file))
lon_data = loadmat(os.path.join(base_path, lon_file))
vmeridional_data = loadmat(os.path.join(base_path, vmeridional_file))
vzonal_data = loadmat(os.path.join(base_path, vzonal_file))
# 将数据转换为DataFrame
height_df = pd.DataFrame(height_data['Height'])
lat_df = pd.DataFrame(lat_data['Lat'])
lon_df = pd.DataFrame(lon_data['Lon'])
vmeridional_df = pd.DataFrame(vmeridional_data['VMerdional'])
vzonal_df = pd.DataFrame(vzonal_data['Vzonal'])
# 将每天的数据添加到列表中
height_list.append(height_df)
lat_list.append(lat_df)
lon_list.append(lon_df)
vmeridional_list.append(vmeridional_df)
vzonal_list.append(vzonal_df)
except FileNotFoundError as e:
print(f"Error: {e}")
continue # 跳过当前文件,继续处理其他文件
# 合并所有天的数据
height_df = pd.concat(height_list, ignore_index=True)
lat_df = pd.concat(lat_list, ignore_index=True)
lon_df = pd.concat(lon_list, ignore_index=True)
vmeridional_df = pd.concat(vmeridional_list, axis=1) # 按列合并
vzonal_df = pd.concat(vzonal_list, axis=1) # 按列合并
# 初始化空列表来存储每天的数据
UTime_list = []
# 初始化累加的时间偏移量
time_offset = 0
# 循环读取从第1天到第365天的数据
for day in range(1, 366): # 365天数据
# 构建文件名(使用三位数格式)
day_str = f"{day:03d}" # 格式化数字为三位数前面补0
UTime_file = f"{day_str}_UTime.mat"
# 检查UTime文件是否存在
try:
if not os.path.exists(os.path.join(base_path, UTime_file)):
raise FileNotFoundError(f"{UTime_file} not found")
# 读取.mat文件
UTime_data = loadmat(os.path.join(base_path, UTime_file))
# 将数据转换为DataFrame
UTime_df = pd.DataFrame(UTime_data['UTime'])
# 对UTime进行累加处理
UTime_df += time_offset
# 将每天的数据添加到列表中
UTime_list.append(UTime_df)
# 更新时间偏移量为下一天增加24小时
time_offset += 24
except FileNotFoundError as e:
print(f"Error: {e}")
continue # 跳过当前文件,继续处理其他文件
# 合并所有天的数据
UTime_df = pd.concat(UTime_list, ignore_index=True)
# 将UTime_df的单位从小时转换为天除以24
UTime_df = UTime_df / 24
# 获取固定高度下的纬度数据
fixed_height_lat = lat_df.iloc[:, 0].values
# 获取固定高度下的经度数据
fixed_height_lon = lon_df.iloc[:, 0].values
# 获取固定高度下的经向风数据
fixed_height_vmeridional = vmeridional_df.iloc[fixed_height_index, :].values
# 获取固定高度下的纬向风数据
fixed_height_vzonal = vzonal_df.iloc[fixed_height_index, :].values
# 获取对应的世界时数据这里直接使用整个UTime数据你可以根据实际情况判断是否需要处理下格式等
fixed_height_utime = UTime_df.iloc[:, 0].values
# 将这些数据整合到一个新的DataFrame可选操作方便后续查看等
combined_data = pd.DataFrame({
'Latitude': fixed_height_lat,
'Longitude': fixed_height_lon,
'V_Meridional': fixed_height_vmeridional,
'V_Zonal': fixed_height_vzonal,
'UTime': fixed_height_utime
})
# 简单打印下整合后的数据前几行查看下内容
print(combined_data)
# 确定纬度和经度的范围
lat_min, lat_max = np.min(fixed_height_lat), np.max(fixed_height_lat)
lon_min, lon_max = np.min(fixed_height_lon), np.max(fixed_height_lon)
# 计算网格的边界
lat_bins = np.linspace(lat_min, lat_max, 10) # 纬度分为6个网格需要7个边界
lon_bins = np.linspace(lon_min, lon_max, 13) # 经度分为20个网格需要21个边界
# 将数据分配到网格中
grid_data = []
for i in range(len(fixed_height_lat)):
lat_idx = np.digitize(fixed_height_lat[i], lat_bins) - 1 # 找到纬度所在的网格索引
lon_idx = np.digitize(fixed_height_lon[i], lon_bins) - 1 # 找到经度所在的网格索引
grid_idx = lat_idx * 12 + lon_idx # 计算网格的唯一索引9x12=108
grid_data.append([fixed_height_lat[i], fixed_height_lon[i], fixed_height_vmeridional[i], fixed_height_vzonal[i], fixed_height_utime[i], grid_idx])
# 将网格数据转换为DataFrame
grid_df = pd.DataFrame(grid_data, columns=['Latitude', 'Longitude', 'V_Meridional', 'V_Zonal', 'UTime', 'Grid_Index'])
# 打印网格数据的前几行查看
print(grid_df.head())
# 筛选纬度在 0 到 5° 范围内的数据
filtered_df = grid_df[(grid_df['Latitude'] >= 0) & (grid_df['Latitude'] <= 5)]
# 将经度从度转换为弧度
filtered_df['Longitude_Radians'] = np.radians(filtered_df['Longitude'])
# 选择需要输出的列
output_columns = ['Longitude_Radians', 'UTime', 'V_Zonal']
# 将数据输出为txt文件
output_file = base_path+'TIDI_V_Zonal_data.txt'#经向风速分量
filtered_df[output_columns].to_csv(output_file, sep='\t', index=False)
# 打印结果确认
print(f"数据已保存到 {output_file}")
# 选择需要输出的列
output_columns1 = ['Longitude_Radians', 'UTime', 'V_Meridional']
# 将数据输出为txt文件
output_file1 = base_path+'TIDI_V_Meridional_data.txt'#纬向风速分量
filtered_df[output_columns1].to_csv(output_file1, sep='\t', index=False)
# 打印结果确认
print(f"数据已保存到 {output_file1}")
# # 对网格数据按照Grid_Index进行分组并计算每个网格的统计数据
# grid_stats = grid_df.groupby('Grid_Index').agg({
# 'Latitude': 'mean', # 计算纬度的平均值
# 'Longitude': 'mean', # 计算经度的平均值
# 'V_Meridional': 'mean', # 计算经向风的平均值、最大值和最小值
# 'V_Zonal': 'mean', # 计算纬向风的平均值、最大值和最小值
# 'UTime': 'mean' # 计算UTime的平均值
# }).reset_index()
#
# # 重命名列,使其更具可读性
# grid_stats.columns = ['Grid_Index', 'Mean_Latitude', 'Mean_Longitude', 'Mean_VMeridional', 'Mean_VZonal', 'Mean_UTime']
# # 将经度转换为2π范围
# grid_stats['Mean_Longitude_2pi'] = (grid_stats['Mean_Longitude'] / 360) * (2 * np.pi)
# # 定义纬度的最小值和最大值起码要40°区间否则数据太少无法拟合
# latmin =0
# latmax=40
# # 筛选出Mean_Latitude在lat_min到lat_max之间的数据
# filtered_grid_stats = grid_stats[(grid_stats['Mean_Latitude'] >= latmin) & (grid_stats['Mean_Latitude'] <= latmax)]
#
# # 检查筛选后的数据行数是否小于18
# if filtered_grid_stats.shape[0] < 18:
# print("无法拟合行星波")
# else:
# # 打印筛选后的网格数据的前几行查看
# print(filtered_grid_stats.head())