with tidi
This commit is contained in:
parent
eb7f4420ff
commit
104e253e2b
@ -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)
|
||||
|
||||
@ -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')
|
||||
|
||||
998
radar/plot2.py
998
radar/plot2.py
@ -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))
|
||||
|
||||
|
||||
|
||||
@ -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()
|
||||
@ -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,
|
||||
)
|
||||
|
||||
|
||||
@ -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
35
tidi/__init__.py
Normal 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
205
tidi/staged/plot.py
Normal 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
210
tidi/staged/process.py
Normal 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())
|
||||
Loading…
x
Reference in New Issue
Block a user