backup: version 1
This commit is contained in:
commit
eb7f4420ff
8
.gitignore
vendored
Normal file
8
.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
# Ignore everything
|
||||
*
|
||||
# But not .py files
|
||||
!*.py
|
||||
# Include .gitignore itself
|
||||
!.gitignore
|
||||
# Recursively allow .py files in subdirectories
|
||||
!*/
|
||||
35
backend.py
Normal file
35
backend.py
Normal file
@ -0,0 +1,35 @@
|
||||
from gevent import pywsgi, monkey
|
||||
monkey.patch_all()
|
||||
|
||||
from utils import *
|
||||
import saber
|
||||
import radar
|
||||
import balloon
|
||||
from flask import Flask
|
||||
from flask_cors import CORS
|
||||
from typing import get_args
|
||||
import sys
|
||||
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
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")
|
||||
|
||||
# allow cors
|
||||
CORS(app)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# get args '--prod'
|
||||
args = sys.argv
|
||||
if 'prod' in args:
|
||||
|
||||
# app.run()
|
||||
# import gevent
|
||||
server = pywsgi.WSGIServer(('0.0.0.0', 5000), app)
|
||||
server.serve_forever()
|
||||
|
||||
elif 'debug' in args:
|
||||
app.run(debug=True)
|
||||
77
balloon/__init__.py
Normal file
77
balloon/__init__.py
Normal file
@ -0,0 +1,77 @@
|
||||
from flask import Blueprint
|
||||
import balloon.extract_wave
|
||||
import balloon.read_data
|
||||
import balloon.plot_once
|
||||
import balloon.plot_year
|
||||
from flask import Flask, jsonify, request, send_file
|
||||
from balloon.plot_once_backend import render_by_mode_single
|
||||
from balloon.plot_year_backend import get_all_modes, render_based_on_mode
|
||||
from utils import *
|
||||
|
||||
all_year_data = pd.read_parquet("./cache/ballon_data_lin.parquet")
|
||||
|
||||
all_ballon_files = glob.glob("./data/探空气球/**/**.nc", recursive=True)
|
||||
|
||||
def get_dataframe_between_year(start_year, end_year):
|
||||
res = all_year_data
|
||||
filtered_res = res[(res['file_name'].str.extract(r'LIN-(\d{4})')[0].astype(int) >= start_year) &
|
||||
(res['file_name'].str.extract(r'LIN-(\d{4})')[0].astype(int) <= end_year)]
|
||||
return filtered_res
|
||||
|
||||
|
||||
plot_once = balloon.plot_once.plot_once
|
||||
plot_year = balloon.plot_year.plot_year
|
||||
is_terrain_wave = balloon.extract_wave.is_terrain_wave
|
||||
read_data = balloon.read_data.read_data
|
||||
extract_wave = balloon.extract_wave.extract_wave
|
||||
|
||||
|
||||
balloon_module = Blueprint("Balloon", __name__)
|
||||
|
||||
|
||||
@balloon_module.route('/')
|
||||
def home():
|
||||
return jsonify(message="Welcome to the Flask balloon_module!")
|
||||
|
||||
|
||||
@balloon_module.route("/metadata/modes")
|
||||
def supermeta():
|
||||
return jsonify({
|
||||
"combos": comboType,
|
||||
"comboMode": comboMode,
|
||||
"date": comboDate
|
||||
})
|
||||
|
||||
|
||||
@balloon_module.route("/metadata")
|
||||
def list_ballon():
|
||||
return all_ballon_files
|
||||
|
||||
|
||||
@balloon_module.route("/metadata/year")
|
||||
def list_ballon_year_modes():
|
||||
return get_all_modes()
|
||||
|
||||
|
||||
@balloon_module.route("/render/year")
|
||||
def render_full_year():
|
||||
# get start_year and end_year from query
|
||||
start_year = request.args.get('start_year')
|
||||
end_year = request.args.get('end_year')
|
||||
mode = request.args.get('mode')
|
||||
season = request.args.get('season')
|
||||
print(start_year, end_year)
|
||||
df = get_dataframe_between_year(int(start_year), int(end_year))
|
||||
buff = render_based_on_mode(df, mode, season)
|
||||
return send_file(buff, mimetype='image/png')
|
||||
|
||||
|
||||
@balloon_module.route("/render/single")
|
||||
def render_single_path():
|
||||
path = request.args.get('path')
|
||||
mode = request.args.get('mode')
|
||||
data = balloon.read_data(path)
|
||||
buff = render_by_mode_single(data, mode)
|
||||
if buff is None:
|
||||
return jsonify(message="No wave detected")
|
||||
return send_file(buff, mimetype='image/png')
|
||||
413
balloon/extract_wave.py
Normal file
413
balloon/extract_wave.py
Normal file
@ -0,0 +1,413 @@
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from scipy.optimize import curve_fit
|
||||
from scipy.signal import lombscargle, hilbert
|
||||
from sklearn.decomposition import PCA
|
||||
import sympy as sp
|
||||
|
||||
|
||||
def calculate_n(Av, Au, Bu, Bv):
|
||||
# 计算 Fuv
|
||||
Fuv = Av * Au * np.cos(Bu - Bv)
|
||||
# 根据条件计算 n
|
||||
if Au > Av:
|
||||
n = 1
|
||||
elif Au < Av:
|
||||
if Fuv > 0:
|
||||
n = 0
|
||||
else: # Fuv <= 0
|
||||
n = 2
|
||||
else:
|
||||
raise ValueError("Au 和 Av 不应相等")
|
||||
return n
|
||||
|
||||
|
||||
def calculate_seta(Av, Au, Bu, Bv):
|
||||
# 计算 n
|
||||
n = calculate_n(Av, Au, Bu, Bv)
|
||||
# 计算 Fuv
|
||||
Fuv = Av * Au * np.cos(Bu - Bv)
|
||||
# 计算 arctan 部分
|
||||
arctan_value = np.atan2(2 * Fuv, Av**2 - Au**2) # 更通用,返回的角度范围是 ([-π, π])
|
||||
# arctan_value = np.atan(2 * Fuv / (Av ** 2 - Au ** 2)) # 返回的角度范围是 ([-π/2, π/2])
|
||||
# 计算 seta
|
||||
seta = round((1 / 2) * (n * np.pi + arctan_value), 2)
|
||||
return seta # 返回结果为弧度
|
||||
|
||||
|
||||
# 判断椭圆旋转方向
|
||||
def analyze_direction(df1, df2, seta_degrees):
|
||||
# 初始值
|
||||
# a = 0
|
||||
# b = 0
|
||||
|
||||
# 计算元素
|
||||
element1 = df1.iloc[0, 1] * df1.iloc[1, 2] - \
|
||||
df1.iloc[0, 2] * df1.iloc[1, 1]
|
||||
element2 = df2.iloc[0, 1] * df2.iloc[1, 2] - \
|
||||
df2.iloc[0, 2] * df2.iloc[1, 1]
|
||||
|
||||
# 判断 element1
|
||||
if element1 < 0:
|
||||
a = 1 # 表示顺时针,重力波向上传播
|
||||
else:
|
||||
a = -1
|
||||
|
||||
# 判断 element2
|
||||
if element2 < 0:
|
||||
b = seta_degrees # 表示顺时针,水平传播方向与假设方向同,seta
|
||||
else:
|
||||
b = seta_degrees + 180 # 逆时针,seta+180
|
||||
return a, b
|
||||
|
||||
|
||||
# 计算u_v椭圆长短轴之比(先标准化椭圆,再计算最远距离最近距离之比)
|
||||
def calculate_axial_ratio(u_fit, v_fit):
|
||||
# 将 ucmp 和 vcmp 组成一个数据矩阵
|
||||
data_matrix = np.vstack((u_fit, v_fit)).T
|
||||
|
||||
# 使用 PCA 进行主成分分析
|
||||
pca = PCA(n_components=2)
|
||||
transformed_data = pca.fit_transform(data_matrix)
|
||||
|
||||
# 提取主成分
|
||||
principal_components = pca.components_
|
||||
|
||||
# 计算每个数据点在标准椭圆上的坐标
|
||||
scaling_factors = np.linalg.norm(principal_components, axis=1)
|
||||
standardized_data = transformed_data / scaling_factors
|
||||
|
||||
# 创建 DataFrame
|
||||
standardized_df = pd.DataFrame(standardized_data, columns=["PC1", "PC2"])
|
||||
# 获取第一列和第二列的最大值和最小值
|
||||
col1_max = standardized_df.iloc[:, 0].max()
|
||||
col1_min = standardized_df.iloc[:, 0].min()
|
||||
col2_max = standardized_df.iloc[:, 1].max()
|
||||
col2_min = standardized_df.iloc[:, 1].min()
|
||||
|
||||
# 计算长短轴比值
|
||||
axial_ratio = (col1_max - col1_min) / (col2_max - col2_min)
|
||||
|
||||
return axial_ratio
|
||||
|
||||
|
||||
def calculate_wave(data: pd.DataFrame, lat: float, g: float):
|
||||
height = data["alt"].values
|
||||
temp = data["temp"].values
|
||||
ucmp = data["u"].values
|
||||
vcmp = data["v"].values
|
||||
wcmp = data["wspeed"].values
|
||||
press = (data["press"].values) * 100 # 单位Pa
|
||||
|
||||
coef_temp = np.polyfit(height, temp, 2)
|
||||
poly_temp = np.poly1d(coef_temp)
|
||||
coef_ucmp = np.polyfit(height, ucmp, 2)
|
||||
poly_ucmp = np.poly1d(coef_ucmp)
|
||||
coef_vcmp = np.polyfit(height, vcmp, 2)
|
||||
poly_vcmp = np.poly1d(coef_vcmp)
|
||||
coef_wcmp = np.polyfit(height, wcmp, 2)
|
||||
poly_wcmp = np.poly1d(coef_wcmp)
|
||||
|
||||
residual_temp = temp - poly_temp(height)
|
||||
residual_ucmp = ucmp - poly_ucmp(height)
|
||||
residual_vcmp = vcmp - poly_vcmp(height)
|
||||
residual_wcmp = wcmp - poly_wcmp(height)
|
||||
|
||||
ff = np.linspace(0.01, 25, 1000) # 横坐标范围
|
||||
pgram_temp = lombscargle(height, residual_temp, ff)
|
||||
pgram_ucmp = lombscargle(height, residual_ucmp, ff)
|
||||
pgram_vcmp = lombscargle(height, residual_vcmp, ff)
|
||||
|
||||
# todo:我的频率等于垂直波数(单位10-3rad/m)?
|
||||
main_frequency_temp = round(ff[np.argmax(pgram_temp)], 2)
|
||||
main_frequency_ucmp = round(ff[np.argmax(pgram_ucmp)], 2)
|
||||
main_frequency_vcmp = round(ff[np.argmax(pgram_vcmp)], 2)
|
||||
|
||||
# 由波数计算波长
|
||||
λ1 = round(1 / ((main_frequency_temp / 2.5) * 0.4), 2)
|
||||
λ2 = round(1 / ((main_frequency_ucmp / 2.5) * 0.4), 2)
|
||||
λ3 = round(1 / ((main_frequency_vcmp / 2.5) * 0.4), 2)
|
||||
|
||||
mean = round(sum([λ1, λ2, λ3]) / 3, 2)
|
||||
rsd_temp = abs(round(((λ1 - mean) / λ1) * 100, 2))
|
||||
rsd_ucmp = abs(round(((λ2 - mean) / λ2) * 100, 2))
|
||||
rsd_vcmp = abs(round(((λ3 - mean) / λ3) * 100, 2))
|
||||
|
||||
if not (rsd_temp < 20 and rsd_ucmp < 20 and rsd_vcmp < 20):
|
||||
return []
|
||||
|
||||
# 定义正弦波模型函数
|
||||
def sine_wave(height, A, B, omega):
|
||||
omega = 2 * np.pi / mean
|
||||
return A * np.sin(omega * height + B)
|
||||
|
||||
def fit_sine_wave(height, data, mean):
|
||||
# 提供初始猜测值,例如 A=1, B=0, omega=2 * np.pi / mean
|
||||
initial_guess = [1, 0, 2 * np.pi / mean]
|
||||
# 设置参数的边界:A的下限为0(确保A为正值),B和omega没有边界限制
|
||||
bounds = ([0, -np.inf, -np.inf], [np.inf, np.inf, np.inf])
|
||||
# curve_fit 函数通过调整 sine_wave 函数的参数(A、B 和 omega)来最小化模型预测值与实际数据 data 之间的残差平方和。
|
||||
params, covariance = curve_fit(
|
||||
sine_wave, height, data, p0=initial_guess, bounds=bounds)
|
||||
return params
|
||||
|
||||
# 对 u 风、v 风、温度扰动量进行拟合
|
||||
params_u = fit_sine_wave(height, residual_ucmp, mean) # 输出3个参数分别是振幅,相位,角频率
|
||||
u_fit = sine_wave(height, *params_u)
|
||||
|
||||
params_v = fit_sine_wave(height, residual_vcmp, mean)
|
||||
v_fit = sine_wave(height, *params_v)
|
||||
|
||||
params_T = fit_sine_wave(height, residual_temp, mean)
|
||||
T_fit = sine_wave(height, *params_T)
|
||||
|
||||
params_w = fit_sine_wave(height, residual_wcmp, mean)
|
||||
w_fit = sine_wave(height, *params_w)
|
||||
|
||||
# 将高度、u_fit、v_fit创建一个数据框df1
|
||||
df1 = pd.DataFrame({"Height": height, "u_fit": u_fit, "v_fit": v_fit})
|
||||
|
||||
# 计算假设的水平传播方向
|
||||
rad_seta = calculate_seta(
|
||||
params_v[0], params_u[0], params_u[1], params_v[1]) # 输入、输出都为弧度
|
||||
# 转换为角度并保留2位小数
|
||||
seta_degrees = round(np.degrees(rad_seta), 2) # 38.41°
|
||||
|
||||
# 计算水平风
|
||||
uh = u_fit * np.sin(seta_degrees * np.pi / 180) + \
|
||||
v_fit * np.cos(seta_degrees * np.pi / 180) # 水平风
|
||||
# temp = T_fit
|
||||
# 将高度、水平风、温度创建一个数据框df2
|
||||
df2 = pd.DataFrame({"Height": height, "uh": uh, "temp": T_fit})
|
||||
a, b = analyze_direction(df1, df2, seta_degrees) # 计算垂直、水平传播方向
|
||||
|
||||
Axial_ratio = round(calculate_axial_ratio(u_fit, v_fit),
|
||||
2) # 计算长短轴之比并保留2位小数,后续计算本征频率会用到
|
||||
|
||||
# 计算剩余几个参数
|
||||
# 常量
|
||||
# f = 1.32e-4 # rad/s
|
||||
# N = 2.13e-2 # 浮力频数,单位 rad/s
|
||||
|
||||
# 根据站点纬度,计算惯性频率f
|
||||
ou = 7.27e-5 # 单位rad/s,地球自转频率
|
||||
# lat = 64.5
|
||||
f = 2 * ou * np.sin(lat * np.pi / 180)
|
||||
# print(f"f = {f:.3e} rad/s")
|
||||
|
||||
# 求浮力频率N
|
||||
df3 = pd.DataFrame({"height": height, "temp": temp,
|
||||
"temp_bar": poly_temp(height)})
|
||||
|
||||
Cp = 1005 # 单位:J/kgK
|
||||
df3["temp_bar_derivative"] = np.gradient(
|
||||
df3["temp_bar"], (df3["height"]) * 1000) # 求背景温度对高度的偏导
|
||||
gen = (g / temp) * (df3["temp_bar_derivative"] + (g / Cp))
|
||||
sqrt_gen = np.sqrt(np.abs(gen)) # np.sqrt()对列表求根号,np.sqrt()对数字求根号
|
||||
N = sqrt_gen.mean() # 计算平均值,得到浮力频率的均值
|
||||
|
||||
# 1. 本征频率omega_upper
|
||||
omega_upper = sp.symbols("omega_upper")
|
||||
eq1 = sp.Eq(Axial_ratio, omega_upper / f)
|
||||
omega_upper = (sp.solve(eq1, omega_upper))[0]
|
||||
omega_upper = sp.N(omega_upper)
|
||||
|
||||
# 求本征频率与惯性频率的比值
|
||||
w_f = omega_upper / f # todo:总是大于1
|
||||
|
||||
# 本征周期
|
||||
zhou_qi = 2 * np.pi / omega_upper / 3600
|
||||
|
||||
# 2. 垂直波长λ_z、水平波长λ_h
|
||||
|
||||
# 垂直波数
|
||||
λ_z = mean # 保留两位小数
|
||||
# 在北半球,重力波向上传播,垂直波数k_z<0
|
||||
if a == 1:
|
||||
k_z = -(2 * np.pi / λ_z / 1000)
|
||||
else:
|
||||
k_z = 2 * np.pi / λ_z / 1000
|
||||
|
||||
# 水平波数
|
||||
k_h = sp.symbols("k_h")
|
||||
eq2 = sp.Eq(k_h**2, (omega_upper**2 - f**2) * (k_z**2) / N**2)
|
||||
k_h = sp.solve(eq2, k_h)
|
||||
k_h = [sol.evalf() for sol in k_h if sol.evalf() > 0][0]
|
||||
k_h = sp.N(k_h) # 正值
|
||||
|
||||
# 在北半球,重力波向上传播,垂直波数k_h<0
|
||||
if b > 180: # 说明水平传播方向与假定方向相反
|
||||
k_h = -k_h
|
||||
else:
|
||||
k_h = k_h
|
||||
|
||||
λ_h = abs(round((2 * np.pi / k_h / 1000), 2)) # 水平波长,取绝对值,因为波数可能为负数
|
||||
|
||||
# 纬向、经向水平波长λ_x, λ_y
|
||||
# λ_x = abs(λ_h * np.sin(b * np.pi / 180))
|
||||
# λ_y = abs(λ_h * np.cos(b * np.pi / 180))
|
||||
|
||||
# 本征相速度
|
||||
c_x = omega_upper / (abs(k_h) * np.sin(b * np.pi / 180))
|
||||
c_y = omega_upper / (abs(k_h) * np.cos(b * np.pi / 180))
|
||||
c_z = omega_upper / k_z
|
||||
|
||||
"""
|
||||
# 群速度
|
||||
C_gz = -((omega_upper ** 2 - f ** 2)) / (omega_upper * k_z)
|
||||
C_gh = sp.symbols('C_gh')
|
||||
eq3 = sp.Eq(C_gz / C_gh, sp.sqrt(omega_upper ** 2 - f ** 2) / N)
|
||||
C_gh = (sp.solve(eq3, C_gh))[0]
|
||||
C_gh = sp.N(C_gh) # 转成数值
|
||||
"""
|
||||
|
||||
# 波动能E_k
|
||||
u2_mean = np.mean(np.square(u_fit))
|
||||
v2_mean = np.mean(np.square(v_fit))
|
||||
Ek = (1 / 2) * (u2_mean + v2_mean) # 3.11 J/kg
|
||||
|
||||
# 势能E_p
|
||||
T_normalized = T_fit / poly_temp(height) # 归一化处理
|
||||
T2_mean = np.mean(np.square(T_normalized))
|
||||
E_p = ((g**2) * T2_mean) / (2 * (N**2)) # 1.02J/kg
|
||||
|
||||
# 动量通量
|
||||
# 经、纬向风动量通量
|
||||
P = press # 单位Pa
|
||||
R = 287 # 单位:J/(kgK),气体常量
|
||||
T = temp # 温度已经是开尔文
|
||||
densi = P / (R * T) # 向量
|
||||
c = (omega_upper * g) / (N**2)
|
||||
S = 1 - (f**2) / (omega_upper**2) # 常数
|
||||
|
||||
# 归一化温度90°相位变换
|
||||
|
||||
hilb = hilbert(T_normalized)
|
||||
hilb1 = np.imag(hilb) # 获取虚部作为90°相位变换的结果
|
||||
|
||||
uT = u_fit * hilb1 # 向量
|
||||
uT1 = np.mean(uT) # 均值
|
||||
MFu = (np.mean(-densi * c * uT1 * S)) * 1000 # 单位mPa
|
||||
|
||||
vT = v_fit * hilb1
|
||||
vT1 = np.mean(vT)
|
||||
MFv = (np.mean(-densi * c * vT1 * S)) * 1000
|
||||
|
||||
# 调整动量通量符号与本征相速度符号一致
|
||||
# 调整b的符号与a一致
|
||||
MFu1 = abs(MFu) if c_x >= 0 else -abs(MFu)
|
||||
MFv1 = abs(MFv) if c_y >= 0 else -abs(MFv)
|
||||
|
||||
omega_upper = float(omega_upper)
|
||||
# make wf, c_x, c_y, c_z, MFu, MFv, zhouqi float
|
||||
w_f = float(w_f)
|
||||
c_x = float(c_x)
|
||||
c_y = float(c_y)
|
||||
c_z = float(c_z)
|
||||
MFu1 = float(MFu1)
|
||||
MFv1 = float(MFv1)
|
||||
zhou_qi = float(zhou_qi)
|
||||
|
||||
return [
|
||||
a,
|
||||
b,
|
||||
omega_upper,
|
||||
w_f,
|
||||
λ_z,
|
||||
λ_h,
|
||||
c_x,
|
||||
c_y,
|
||||
c_z,
|
||||
Ek,
|
||||
E_p,
|
||||
MFu1,
|
||||
MFv1,
|
||||
params_u[0],
|
||||
params_v[0],
|
||||
params_T[0],
|
||||
ucmp,
|
||||
vcmp,
|
||||
temp,
|
||||
height,
|
||||
poly_ucmp,
|
||||
poly_vcmp,
|
||||
poly_wcmp,
|
||||
poly_temp,
|
||||
residual_ucmp,
|
||||
residual_vcmp,
|
||||
residual_temp,
|
||||
u_fit,
|
||||
v_fit,
|
||||
T_fit,
|
||||
uh,
|
||||
zhou_qi,
|
||||
]
|
||||
|
||||
|
||||
def extract_wave(data: pd.DataFrame, lat: float, g: float):
|
||||
wave = calculate_wave(
|
||||
data[(data["alt"] >= 15) & (data["alt"] <= 25)], lat, g)
|
||||
if len(wave) == 0:
|
||||
return []
|
||||
return [
|
||||
wave[0],
|
||||
wave[1],
|
||||
round(wave[2], 6),
|
||||
round(wave[3], 2),
|
||||
round(wave[4], 2),
|
||||
round(wave[5], 2),
|
||||
round(wave[6], 2),
|
||||
round(wave[7], 2),
|
||||
round(wave[8], 3),
|
||||
round(wave[9], 4),
|
||||
round(wave[10], 4),
|
||||
round(wave[11], 4),
|
||||
round(wave[12], 4),
|
||||
round(wave[13], 2),
|
||||
round(wave[14], 2),
|
||||
round(wave[15], 2),
|
||||
round(wave[31], 2),
|
||||
]
|
||||
|
||||
|
||||
def is_terrain_wave(data: pd.DataFrame, lat: float, g: float):
|
||||
wave = calculate_wave(
|
||||
data[(data["alt"] >= 2) & (data["alt"] <= 10)], lat, g)
|
||||
if len(wave) == 0:
|
||||
return False
|
||||
|
||||
# 计算观测u风v风的均值,为后边合成背景速度
|
||||
mean_ucmp = np.mean(wave[16]) # 11.34
|
||||
mean_vcmp = np.mean(wave[17]) # 0.38
|
||||
|
||||
x1, y1 = mean_ucmp, mean_vcmp # 背景风的分量
|
||||
x2, y2 = float(wave[6]), float(wave[7]) # 相速度的分量
|
||||
# wave[18](wave[19])
|
||||
|
||||
"""
|
||||
# 二阶拟合的u风v风
|
||||
mean_ucmp = np.mean(poly_ucmp(height))
|
||||
mean_vcmp = np.mean(poly_vcmp(height))
|
||||
|
||||
x1, y1 = mean_ucmp, mean_vcmp # 背景风的分量
|
||||
x2, y2 = float(c_x), float(c_y) # 相速度的分量
|
||||
"""
|
||||
# 计算两个风的方向
|
||||
direction1 = np.arctan2(y1, x1)
|
||||
direction2 = np.arctan2(y2, x2)
|
||||
# 转化为角度(该角度是以x轴逆时针旋转),取特殊值用于验证。
|
||||
# direction1_degrees = np.degrees(direction1)
|
||||
# direction2_degrees = np.degrees(direction2)
|
||||
|
||||
# 计算夹角(弧度)
|
||||
angle_radians = np.abs(direction1 - direction2)
|
||||
# 确保夹角在0到π之间
|
||||
if angle_radians > np.pi:
|
||||
angle_radians = 2 * np.pi - angle_radians
|
||||
# 将夹角转换为度
|
||||
angle_degrees = np.degrees(angle_radians)
|
||||
|
||||
# 判断是否超过150,或者165
|
||||
if round(angle_degrees, 2) > 150:
|
||||
return True
|
||||
return False
|
||||
167
balloon/plot_once.py
Normal file
167
balloon/plot_once.py
Normal file
@ -0,0 +1,167 @@
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
from balloon.extract_wave import calculate_wave, is_terrain_wave
|
||||
|
||||
|
||||
def plot_once(data: pd.DataFrame, path: str, lat: float, g: float):
|
||||
wave = calculate_wave(data[(data["alt"] >= 15) & (data["alt"] <= 25)], lat, g)
|
||||
if len(wave) == 0:
|
||||
return []
|
||||
|
||||
c = is_terrain_wave(data, lat, g)
|
||||
a, b, omega_upper, w_f, λ_z, λ_h, c_x, c_y, c_z, Ek, E_p, MFu1, MFv1, params_u, params_v, params_T = wave[:16]
|
||||
ucmp, vcmp, temp, height, poly_ucmp, poly_vcmp, _, poly_temp, residual_ucmp, residual_vcmp, residual_temp, u_fit, v_fit, T_fit, uh, _ = wave[16:]
|
||||
|
||||
plt.figure(figsize=(16, 10))
|
||||
|
||||
# 二阶多项式拟合曲线
|
||||
|
||||
# u 风扰动量
|
||||
plt.subplot(2, 6, 1)
|
||||
plt.plot(ucmp, height, label="", linestyle="-")
|
||||
plt.plot(poly_ucmp(height), height, label="")
|
||||
plt.ylabel("Height(km)")
|
||||
plt.xlabel("Zonal wind (m/s)")
|
||||
plt.grid(True)
|
||||
|
||||
# v 风扰动量
|
||||
plt.subplot(2, 6, 2)
|
||||
plt.plot(vcmp, height, label="", linestyle="-")
|
||||
plt.plot(poly_vcmp(height), height, label="")
|
||||
plt.ylabel("Height(km)")
|
||||
plt.xlabel("Meridional wind (m/s)")
|
||||
plt.grid(True)
|
||||
|
||||
plt.title("观测的二阶多项式拟合", fontsize=16)
|
||||
|
||||
# 温度扰动量
|
||||
plt.subplot(2, 6, 3)
|
||||
plt.plot(temp, height, label="", linestyle="-")
|
||||
plt.plot(poly_temp(height), height, label="")
|
||||
plt.ylabel("Height(km)")
|
||||
plt.xlabel("Temperature(K)")
|
||||
plt.grid(True)
|
||||
|
||||
# 绘制正弦拟合图
|
||||
|
||||
# u 风扰动量
|
||||
plt.subplot(2, 6, 4)
|
||||
plt.plot(residual_ucmp, height, label="", marker="o", linestyle="None")
|
||||
plt.plot(u_fit, height, label="")
|
||||
plt.ylabel("Height(km)")
|
||||
plt.xlabel("Zonal wind (m/s)")
|
||||
plt.grid(True)
|
||||
|
||||
# v 风扰动量
|
||||
plt.subplot(2, 6, 5)
|
||||
plt.plot(residual_vcmp, height, label="", marker="o", linestyle="None")
|
||||
plt.plot(v_fit, height, label="")
|
||||
plt.ylabel("Height(km)")
|
||||
plt.xlabel("Meridional wind (m/s)")
|
||||
plt.grid(True)
|
||||
|
||||
plt.title("扰动分量的正弦波拟合", fontsize=16)
|
||||
|
||||
# 温度扰动量
|
||||
plt.subplot(2, 6, 6)
|
||||
plt.plot(residual_temp, height, label="", marker="o", linestyle="None")
|
||||
plt.plot(T_fit, height, label="")
|
||||
plt.ylabel("Height(km)")
|
||||
plt.xlabel("Temperature(K)")
|
||||
plt.grid(True)
|
||||
|
||||
# 标记3个特定高度
|
||||
specified_heights = [15, 15.05, 15.1]
|
||||
markers = ["*", "D", "^"] # 星号,菱形,三角形
|
||||
|
||||
# a. U-V矢量曲线
|
||||
plt.subplot(2, 2, 3)
|
||||
plt.plot(u_fit, v_fit) # 绘制U-V曲线
|
||||
for h, marker in zip(specified_heights, markers):
|
||||
index = np.abs(height - h).argmin() # 找到离指定高度最近的索引
|
||||
plt.scatter(u_fit[index], v_fit[index], marker=marker, s=100, label=f"{h} km")
|
||||
|
||||
# 设置坐标轴范围和比例
|
||||
plt.xlim(-8, 8)
|
||||
plt.ylim(-4, 4)
|
||||
# plt.gca().set_aspect("equal") # 确保横纵坐标刻度长短一致
|
||||
plt.axvline(0, color="gray", linestyle="--")
|
||||
plt.axhline(0, color="gray", linestyle="--")
|
||||
plt.xlabel("Zonal Wind (m/s)")
|
||||
plt.ylabel("Meridional Wind (m/s)")
|
||||
plt.legend() # 显示图例
|
||||
plt.grid(True) # 显示网格线
|
||||
plt.text(0.05, 1, "(a)", transform=plt.gca().transAxes, fontsize=14, verticalalignment="top")
|
||||
plt.title("径向风-纬向风矢量图")
|
||||
|
||||
# b. 水平风-温度的矢端曲线
|
||||
plt.subplot(2, 2, 4)
|
||||
plt.plot(uh, T_fit) # 绘制水平风-温度曲线
|
||||
for h, marker in zip(specified_heights, markers):
|
||||
index = np.abs(height - h).argmin() # 找到离指定高度最近的索引
|
||||
plt.scatter(uh[index], T_fit[index], marker=marker, s=100, label=f"{h} km")
|
||||
|
||||
# 设置坐标轴范围和比例
|
||||
plt.xlim(-4, 4)
|
||||
plt.ylim(-2, 2)
|
||||
# plt.gca().set_aspect("equal") # 确保横纵坐标刻度长短一致
|
||||
plt.axvline(0, color="gray", linestyle="--")
|
||||
plt.axhline(0, color="gray", linestyle="--")
|
||||
plt.xlabel("Horizontal wind (m/s)")
|
||||
plt.ylabel("Temp (K)")
|
||||
plt.legend()
|
||||
plt.grid(True)
|
||||
plt.text(0.05, 1, "(b)", transform=plt.gca().transAxes, fontsize=14, verticalalignment="top")
|
||||
plt.title("温度-水平风矢量图")
|
||||
|
||||
# 设置中文显示和负号正常显示
|
||||
plt.rcParams["font.sans-serif"] = ["SimHei"] # 显示中文
|
||||
plt.rcParams["axes.unicode_minus"] = False # 正常显示负号
|
||||
|
||||
plt.tight_layout()
|
||||
# plt.savefig(path, transparent=True)
|
||||
plt.savefig(path)
|
||||
plt.close()
|
||||
|
||||
"""
|
||||
地形重力波 c 是否是地形重力波
|
||||
垂直传播方向 a 1上
|
||||
水平传播方向 b degree
|
||||
本征(固有)频率 round(omega_upper, 6) rad/s
|
||||
本征周期 round(2 * np.pi / omega_upper / 3600, 2) h
|
||||
本征频率与固有频率的比值(即长短轴之比) round(w_f, 2)
|
||||
垂直波长 round(λ_z, 2) km
|
||||
水平波长 round(λ_h, 2) km
|
||||
纬向本征相速度 round(c_x, 2) m/s
|
||||
经向本征相速度 round(c_y, 2) m/s
|
||||
垂直本征相速度 round(c_z, 3) m/s
|
||||
波动能 round(Ek, 4) J/kg
|
||||
势能 round(E_p, 4) J/kg
|
||||
纬向动量通量 round(MFu1, 4) mPa
|
||||
经向动量通量 round(MFv1, 4) mPa
|
||||
纬向风扰动振幅 round(params_u, 2) m/s
|
||||
经向风扰动振幅 round(params_v, 2) m/s
|
||||
温度扰动振幅 round(params_T, 2) K
|
||||
"""
|
||||
return [
|
||||
c,
|
||||
a,
|
||||
b,
|
||||
round(omega_upper, 6),
|
||||
round(2 * np.pi / omega_upper / 3600, 2),
|
||||
round(w_f, 2),
|
||||
round(λ_z, 2),
|
||||
round(λ_h, 2),
|
||||
round(c_x, 2),
|
||||
round(c_y, 2),
|
||||
round(c_z, 3),
|
||||
round(Ek, 4),
|
||||
round(E_p, 4),
|
||||
round(MFu1, 4),
|
||||
round(MFv1, 4),
|
||||
round(params_u, 2),
|
||||
round(params_v, 2),
|
||||
round(params_T, 2),
|
||||
]
|
||||
145
balloon/plot_once_backend.py
Normal file
145
balloon/plot_once_backend.py
Normal file
@ -0,0 +1,145 @@
|
||||
from io import BytesIO
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
|
||||
from balloon.extract_wave import calculate_wave, is_terrain_wave
|
||||
|
||||
lat = 52.21
|
||||
g = 9.76
|
||||
|
||||
|
||||
def plot_polynomial_fit(ucmp, vcmp, temp, height, poly_ucmp, poly_vcmp, poly_temp):
|
||||
plt.figure(figsize=(16, 5))
|
||||
|
||||
# u 风扰动量
|
||||
plt.subplot(1, 3, 1)
|
||||
plt.plot(ucmp, height, linestyle="-")
|
||||
plt.plot(poly_ucmp(height), height)
|
||||
plt.ylabel("Height(km)")
|
||||
plt.xlabel("Zonal wind (m/s)")
|
||||
plt.grid(True)
|
||||
|
||||
# v 风扰动量
|
||||
plt.subplot(1, 3, 2)
|
||||
plt.plot(vcmp, height, linestyle="-")
|
||||
plt.plot(poly_vcmp(height), height)
|
||||
plt.ylabel("Height(km)")
|
||||
plt.xlabel("Meridional wind (m/s)")
|
||||
plt.grid(True)
|
||||
|
||||
plt.title("观测的二阶多项式拟合", fontsize=16)
|
||||
|
||||
# 温度扰动量
|
||||
plt.subplot(1, 3, 3)
|
||||
plt.plot(temp, height, linestyle="-")
|
||||
plt.plot(poly_temp(height), height)
|
||||
plt.ylabel("Height(km)")
|
||||
plt.xlabel("Temperature(K)")
|
||||
plt.grid(True)
|
||||
|
||||
plt.tight_layout()
|
||||
|
||||
|
||||
def plot_sine_fit(residual_ucmp, residual_vcmp, residual_temp, height, u_fit, v_fit, T_fit):
|
||||
plt.figure(figsize=(16, 5))
|
||||
|
||||
# u 风扰动量
|
||||
plt.subplot(1, 3, 1)
|
||||
plt.plot(residual_ucmp, height, marker="o", linestyle="None")
|
||||
plt.plot(u_fit, height)
|
||||
plt.ylabel("Height(km)")
|
||||
plt.xlabel("Zonal wind (m/s)")
|
||||
plt.grid(True)
|
||||
|
||||
# v 风扰动量
|
||||
plt.subplot(1, 3, 2)
|
||||
plt.plot(residual_vcmp, height, marker="o", linestyle="None")
|
||||
plt.plot(v_fit, height)
|
||||
plt.ylabel("Height(km)")
|
||||
plt.xlabel("Meridional wind (m/s)")
|
||||
plt.grid(True)
|
||||
|
||||
plt.title("扰动分量的正弦波拟合", fontsize=16)
|
||||
|
||||
# 温度扰动量
|
||||
plt.subplot(1, 3, 3)
|
||||
plt.plot(residual_temp, height, marker="o", linestyle="None")
|
||||
plt.plot(T_fit, height)
|
||||
plt.ylabel("Height(km)")
|
||||
plt.xlabel("Temperature(K)")
|
||||
plt.grid(True)
|
||||
|
||||
plt.tight_layout()
|
||||
|
||||
|
||||
def plot_uv_vector(u_fit, v_fit, height, specified_heights, markers):
|
||||
plt.figure(figsize=(8, 6))
|
||||
plt.plot(u_fit, v_fit)
|
||||
for h, marker in zip(specified_heights, markers):
|
||||
index = np.abs(height - h).argmin()
|
||||
plt.scatter(u_fit[index], v_fit[index],
|
||||
marker=marker, s=100, label=f"{h} km")
|
||||
|
||||
plt.xlim(-8, 8)
|
||||
plt.ylim(-4, 4)
|
||||
plt.axvline(0, color="gray", linestyle="--")
|
||||
plt.axhline(0, color="gray", linestyle="--")
|
||||
plt.xlabel("Zonal Wind (m/s)")
|
||||
plt.ylabel("Meridional Wind (m/s)")
|
||||
plt.legend()
|
||||
plt.grid(True)
|
||||
plt.title("径向风-纬向风矢量图")
|
||||
|
||||
|
||||
def plot_temp_horizontal_wind(uh, T_fit, height, specified_heights, markers):
|
||||
plt.figure(figsize=(8, 6))
|
||||
plt.plot(uh, T_fit)
|
||||
for h, marker in zip(specified_heights, markers):
|
||||
index = np.abs(height - h).argmin()
|
||||
plt.scatter(uh[index], T_fit[index],
|
||||
marker=marker, s=100, label=f"{h} km")
|
||||
|
||||
plt.xlim(-4, 4)
|
||||
plt.ylim(-2, 2)
|
||||
plt.axvline(0, color="gray", linestyle="--")
|
||||
plt.axhline(0, color="gray", linestyle="--")
|
||||
plt.xlabel("Horizontal wind (m/s)")
|
||||
plt.ylabel("Temp (K)")
|
||||
plt.legend()
|
||||
plt.grid(True)
|
||||
plt.title("温度-水平风矢量图")
|
||||
|
||||
|
||||
def render_by_mode_single(data, mode):
|
||||
wave = calculate_wave(
|
||||
data[(data["alt"] >= 15) & (data["alt"] <= 25)], lat, g)
|
||||
if len(wave) == 0:
|
||||
return None
|
||||
|
||||
c = is_terrain_wave(data, lat, g)
|
||||
a, b, omega_upper, w_f, λ_z, λ_h, c_x, c_y, c_z, Ek, E_p, MFu1, MFv1, params_u, params_v, params_T = wave[
|
||||
:16]
|
||||
ucmp, vcmp, temp, height, poly_ucmp, poly_vcmp, _, poly_temp, residual_ucmp, residual_vcmp, residual_temp, u_fit, v_fit, T_fit, uh, _ = wave[
|
||||
16:]
|
||||
|
||||
if mode == "观测的二阶多项式拟合":
|
||||
plot_polynomial_fit(ucmp, vcmp, temp, height, poly_ucmp,
|
||||
poly_vcmp, poly_temp)
|
||||
elif mode == "扰动分量的正弦波拟合":
|
||||
plot_sine_fit(residual_ucmp, residual_vcmp, residual_temp,
|
||||
height, u_fit, v_fit, T_fit)
|
||||
elif mode == "径向风-纬向风矢量图":
|
||||
plot_uv_vector(u_fit, v_fit, height, [15, 20, 25], [
|
||||
"o", "s", "D"])
|
||||
elif mode == "温度-水平风矢量图":
|
||||
plot_temp_horizontal_wind(uh, T_fit, height, [15, 20, 25], [
|
||||
"o", "s", "D"])
|
||||
else:
|
||||
raise ValueError("Unknown mode")
|
||||
plt.rcParams["font.sans-serif"] = ["SimHei"] # 显示中文
|
||||
buff = BytesIO()
|
||||
plt.savefig(buff, format="png")
|
||||
plt.close()
|
||||
|
||||
buff.seek(0)
|
||||
return buff
|
||||
359
balloon/plot_year.py
Normal file
359
balloon/plot_year.py
Normal file
@ -0,0 +1,359 @@
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.gridspec as gridspec
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import seaborn as sns
|
||||
from windrose import WindroseAxes
|
||||
|
||||
|
||||
def plot_year(data: pd.DataFrame, path: str, lat: float, g: float):
|
||||
if data.size == 0:
|
||||
return False
|
||||
|
||||
data.loc[:, "date"] = data["file_name"].str[:15]
|
||||
filtered_df = data[["date"] + [col for col in data.columns if col != "file_name" and col != "date"]]
|
||||
filtered_df.reset_index(drop=True, inplace=True)
|
||||
|
||||
filtered_df = filtered_df.drop_duplicates(subset="date", keep="last") # 使用 drop_duplicates 函数,保留每组重复中的最后一个记录
|
||||
filtered_df.reset_index(drop=True, inplace=True)
|
||||
|
||||
filtered_df = filtered_df[filtered_df["w_f"] < 10] # 筛选 w_f 值小于 10 的数据
|
||||
|
||||
# todo:1-删除不合理的数据
|
||||
# 1.先剔除明显异常的值
|
||||
for column in filtered_df.columns[1:]: # 不考虑第一列日期列
|
||||
filtered_df = filtered_df[(filtered_df[column] >= -9999) & (filtered_df[column] <= 9999)] # 460
|
||||
|
||||
# 2.再用四分位数法,适合所有数据集
|
||||
def remove_outliers_iqr(df):
|
||||
for column in df.columns[9:]: # 从第10列开始
|
||||
Q1 = df[column].quantile(0.25)
|
||||
Q3 = df[column].quantile(0.75)
|
||||
IQR = Q3 - Q1
|
||||
lower_bound = Q1 - 5 * IQR
|
||||
upper_bound = Q3 + 5 * IQR
|
||||
|
||||
# 过滤掉异常值
|
||||
df = df[(df[column] >= lower_bound) & (df[column] <= upper_bound)]
|
||||
return df
|
||||
|
||||
filtered_df = remove_outliers_iqr(filtered_df) # 408
|
||||
|
||||
# 画图
|
||||
|
||||
fig = plt.figure(figsize=(36, 20))
|
||||
gs = gridspec.GridSpec(4, 12)
|
||||
|
||||
ax1 = fig.add_subplot(gs[0, 0:3])
|
||||
ax2 = fig.add_subplot(gs[0, 3:6])
|
||||
ax3 = fig.add_subplot(gs[0, 6:9])
|
||||
ax4 = fig.add_subplot(gs[0, 9:12])
|
||||
|
||||
ax5_1 = fig.add_subplot(gs[1, 0:2])
|
||||
ax5_2 = fig.add_subplot(gs[1, 2:4])
|
||||
ax5_3 = fig.add_subplot(gs[1, 4:6])
|
||||
ax6_1 = fig.add_subplot(gs[1, 6:8])
|
||||
ax6_2 = fig.add_subplot(gs[1, 8:10])
|
||||
ax6_3 = fig.add_subplot(gs[1, 10:12])
|
||||
|
||||
ax7_1 = fig.add_subplot(gs[2, 0:2])
|
||||
ax7_2 = fig.add_subplot(gs[2, 2:4])
|
||||
ax8 = fig.add_subplot(gs[2, 4:8])
|
||||
ax9 = fig.add_subplot(gs[2, 8:12])
|
||||
|
||||
ax10 = []
|
||||
for i in range(0, 10, 3):
|
||||
ax10.append(fig.add_subplot(gs[3, i : i + 3], projection="windrose"))
|
||||
|
||||
sns.set_theme(style="whitegrid", font="SimHei") # 设置绘图样式为白色背景和网格线
|
||||
|
||||
# 1、w/f比值
|
||||
# 计算bins的边界
|
||||
min_val = 1
|
||||
max_val = 10
|
||||
bin_width = 0.5
|
||||
# 创建bins的边界
|
||||
bins = np.arange(min_val, max_val + bin_width, bin_width)
|
||||
|
||||
sns.histplot(filtered_df["w_f"], bins=bins, kde=False, edgecolor="black", stat="percent", ax=ax1) # 加上stat='percent'表示算的是频率
|
||||
# 设置x轴范围
|
||||
ax1.set_xlim(min_val, max_val)
|
||||
# 添加标题和标签
|
||||
ax1.set_title("w/f值统计结果", fontsize=24)
|
||||
ax1.set_xlabel("w/f")
|
||||
ax1.set_ylabel("Occurrence(%)")
|
||||
|
||||
# 2、周期
|
||||
# 计算bins的边界
|
||||
min_val = 1
|
||||
max_val = 10
|
||||
bin_width = 0.5
|
||||
# 创建bins的边界
|
||||
bins = np.arange(min_val, max_val + bin_width, bin_width)
|
||||
|
||||
min_val = np.floor(filtered_df["zhou_qi"].min()) # 向下取整
|
||||
max_val = np.ceil(filtered_df["zhou_qi"].max()) # 向上取整
|
||||
bin_width = 1
|
||||
bins = np.arange(min_val, max_val + bin_width, bin_width)
|
||||
|
||||
sns.histplot(filtered_df["zhou_qi"], bins=bins, kde=False, edgecolor="black", stat="percent", ax=ax2) # 加上stat='percent'表示算的是频率
|
||||
# 设置x轴范围
|
||||
ax2.set_xlim(min_val, max_val)
|
||||
# 添加标题和标签
|
||||
ax2.set_title("周期统计结果", fontsize=24)
|
||||
ax2.set_xlabel("h")
|
||||
ax2.set_ylabel("Occurrence(%)")
|
||||
|
||||
# 3、垂直波长
|
||||
# 创建bins的边界
|
||||
min_val = np.floor(filtered_df["ver_wave_len"].min()) # 向下取整
|
||||
max_val = np.ceil(filtered_df["ver_wave_len"].max()) # 向上取整
|
||||
bin_width = 0.5
|
||||
bins = np.arange(min_val, max_val + bin_width, bin_width)
|
||||
|
||||
sns.histplot(filtered_df["ver_wave_len"], bins=bins, kde=False, edgecolor="black", stat="percent", ax=ax3) # 加上stat='percent'表示算的是频率
|
||||
# 设置x轴范围
|
||||
ax3.set_xlim(min_val, max_val)
|
||||
# 添加标题和标签
|
||||
ax3.set_title("垂直波长分布", fontsize=24)
|
||||
ax3.set_xlabel("Vertical wavelength(km)")
|
||||
ax3.set_ylabel("Occurrence(%)")
|
||||
|
||||
# 4、水平波长
|
||||
# 创建bins的边界
|
||||
min_val = np.floor(filtered_df["hori_wave_len"].min()) # 向下取整
|
||||
max_val = np.ceil(filtered_df["hori_wave_len"].max()) # 向上取整
|
||||
bin_width = 100
|
||||
bins = np.arange(min_val, max_val + bin_width, bin_width)
|
||||
|
||||
sns.histplot(filtered_df["hori_wave_len"], bins=bins, kde=False, edgecolor="black", stat="percent", ax=ax4) # 加上stat='percent'表示算的是频率
|
||||
# 设置x轴范围
|
||||
ax4.set_xlim(min_val, max_val)
|
||||
# 添加标题和标签
|
||||
ax4.set_title("水平波长分布", fontsize=24)
|
||||
ax4.set_xlabel("Horizontal wavelength(km)")
|
||||
ax4.set_ylabel("Occurrence(%)")
|
||||
|
||||
# 5、本征相速度
|
||||
|
||||
# 纬向本征相速度
|
||||
# 计算bins的边界
|
||||
min_val = np.floor(filtered_df["c_x"].min()) # 向下取整
|
||||
max_val = np.ceil(filtered_df["c_x"].max()) # 向上取整
|
||||
bin_width = 10
|
||||
bins = np.arange(min_val, max_val + bin_width, bin_width)
|
||||
# 创建纬向直方图
|
||||
sns.histplot(filtered_df["c_x"], bins=bins, kde=False, edgecolor="black", stat="percent", ax=ax5_1)
|
||||
ax5_1.set_xlim(min_val, max_val)
|
||||
ax5_1.set_title("纬向本征相速度", fontsize=24)
|
||||
ax5_1.set_xlabel("Zonal phase speed (m/s)")
|
||||
ax5_1.set_ylabel("Occurrence (%)")
|
||||
|
||||
# 经向本征相速度
|
||||
# 计算bins的边界
|
||||
min_val = np.floor(filtered_df["c_y"].min()) # 向下取整
|
||||
max_val = np.ceil(filtered_df["c_y"].max()) # 向上取整
|
||||
bin_width = 10
|
||||
bins = np.arange(min_val, max_val + bin_width, bin_width)
|
||||
# 创建经向直方图
|
||||
sns.histplot(filtered_df["c_y"], bins=bins, kde=False, edgecolor="black", stat="percent", ax=ax5_2)
|
||||
ax5_2.set_xlim(min_val, max_val)
|
||||
ax5_2.set_title("经向本征相速度", fontsize=24)
|
||||
ax5_2.set_xlabel("Meridional phase speed (m/s)")
|
||||
ax5_2.set_ylabel("Occurrence (%)")
|
||||
|
||||
# 垂直本征相速度
|
||||
# 计算bins的边界
|
||||
min_val = filtered_df["c_z"].min() # -1.148
|
||||
max_val = filtered_df["c_z"].max() # 0.624
|
||||
bin_width = 0.1
|
||||
bins = np.arange(min_val, max_val + bin_width, bin_width)
|
||||
# 创建垂直直方图
|
||||
sns.histplot(filtered_df["c_z"], bins=bins, kde=False, edgecolor="black", stat="percent", ax=ax5_3)
|
||||
ax5_3.set_xlim(min_val, max_val)
|
||||
ax5_3.set_title("垂直本征相速度", fontsize=24)
|
||||
ax5_3.set_xlabel("Vertical phase speed (m/s)")
|
||||
ax5_3.set_ylabel("Occurrence (%)")
|
||||
|
||||
# 6、扰动振幅
|
||||
|
||||
# 纬向扰动振幅
|
||||
# 计算bins的边界
|
||||
min_val = np.floor(filtered_df["u1"].min()) # 向下取整
|
||||
max_val = np.ceil(filtered_df["u1"].max()) # 向上取整
|
||||
bin_width = 0.5
|
||||
bins = np.arange(min_val, max_val + bin_width, bin_width)
|
||||
# 创建纬向直方图
|
||||
sns.histplot(filtered_df["u1"], bins=bins, kde=False, edgecolor="black", stat="percent", ax=ax6_1)
|
||||
ax6_1.set_xlim(min_val, max_val)
|
||||
ax6_1.set_title(" ", fontsize=24)
|
||||
ax6_1.set_xlabel("Zonal wind amplitude (m/s)")
|
||||
ax6_1.set_ylabel("Occurrence (%)")
|
||||
|
||||
# 经向扰动振幅
|
||||
# 计算bins的边界
|
||||
min_val = np.floor(filtered_df["v1"].min()) # 向下取整
|
||||
max_val = np.ceil(filtered_df["v1"].max()) # 向上取整
|
||||
bin_width = 0.5
|
||||
bins = np.arange(min_val, max_val + bin_width, bin_width)
|
||||
# 创建经向直方图
|
||||
sns.histplot(filtered_df["v1"], bins=bins, kde=False, edgecolor="black", stat="percent", ax=ax6_2)
|
||||
ax6_2.set_xlim(min_val, max_val)
|
||||
ax6_2.set_title("扰动振幅统计结果", fontsize=24)
|
||||
ax6_2.set_xlabel("Meridional wind amplitude (m/s)")
|
||||
ax6_2.set_ylabel("Occurrence (%)")
|
||||
|
||||
# 垂直扰动振幅
|
||||
# 计算bins的边界
|
||||
min_val = np.floor(filtered_df["T1"].min()) # 向下取整
|
||||
max_val = np.ceil(filtered_df["T1"].max()) # 向上取整
|
||||
bin_width = 0.5
|
||||
bins = np.arange(min_val, max_val + bin_width, bin_width)
|
||||
# 创建垂直直方图
|
||||
sns.histplot(filtered_df["T1"], bins=bins, kde=False, edgecolor="black", stat="percent", ax=ax6_3)
|
||||
ax6_3.set_xlim(min_val, max_val)
|
||||
ax6_3.set_title(" ", fontsize=24)
|
||||
ax6_3.set_xlabel("Temperature amplitude (K)")
|
||||
ax6_3.set_ylabel("Occurrence (%)")
|
||||
|
||||
# 7、动量通量
|
||||
# 挑选出向上传的重力波
|
||||
filtered_df1 = filtered_df[filtered_df["a"] == 1]
|
||||
|
||||
# 绘制第一个子图
|
||||
# 计算bins的边界
|
||||
min_val = np.floor(filtered_df1["MFu"].min()) # 向下取整
|
||||
max_val = np.ceil(filtered_df1["MFu"].max()) # 向上取整
|
||||
bin_width = 0.1
|
||||
bins = np.arange(min_val, max_val + bin_width, bin_width)
|
||||
|
||||
sns.histplot(filtered_df1["MFu"], bins=bins, kde=False, edgecolor="black", stat="percent", ax=ax7_1)
|
||||
ax7_1.set_xlim(min_val, max_val) # 设置x轴范围
|
||||
ax7_1.set_title("纬向动量通量统计结果", fontsize=24) # 添加标题
|
||||
ax7_1.set_xlabel("Zonal momentum flux(mPa)") # x轴标签
|
||||
ax7_1.set_ylabel("Occurrence(%)") # y轴标签
|
||||
|
||||
# 绘制第二个子图
|
||||
# 计算bins的边界
|
||||
min_val = np.floor(filtered_df1["MFv"].min()) # 向下取整
|
||||
max_val = np.ceil(filtered_df1["MFv"].max()) # 向上取整
|
||||
bin_width = 0.1
|
||||
bins = np.arange(min_val, max_val + bin_width, bin_width)
|
||||
|
||||
sns.histplot(filtered_df1["MFv"], bins=bins, kde=False, edgecolor="black", stat="percent", ax=ax7_2)
|
||||
ax7_2.set_xlim(min_val, max_val) # 设置x轴范围
|
||||
ax7_2.set_title("经向动量通量统计结果", fontsize=24) # 添加标题
|
||||
ax7_2.set_xlabel("Meridional momentum flux(mPa)") # x轴标签
|
||||
ax7_2.set_ylabel("Occurrence(%)") # y轴标签
|
||||
|
||||
# 10、水平传播方向
|
||||
|
||||
filtered_df["date1"] = filtered_df["date"].str.split("T").str[0] # 再加一列,只保留日期部分
|
||||
filtered_df["date1"] = pd.to_datetime(filtered_df["date1"], format="%Y%m%d") # 确保 'date1' 列是日期格式
|
||||
|
||||
# 添加季节列
|
||||
def get_season(date):
|
||||
month = date.month
|
||||
if month in [12, 1, 2]:
|
||||
return "Winter"
|
||||
elif month in [3, 4, 5]:
|
||||
return "Spring"
|
||||
elif month in [6, 7, 8]:
|
||||
return "Summer"
|
||||
else:
|
||||
return "Fall"
|
||||
|
||||
filtered_df["season"] = filtered_df["date1"].apply(get_season) # 添加季节列
|
||||
seasons = ["Winter", "Spring", "Summer", "Fall"]
|
||||
|
||||
for ax, season in zip(ax10, seasons):
|
||||
season_data = filtered_df[filtered_df["season"] == season]
|
||||
windrose = WindroseAxes.from_ax(ax)
|
||||
ax.set_title(season, fontsize=18)
|
||||
windrose.bar(season_data["b"], np.ones_like(season_data["b"]), normed=False) # normed=True表示占每个季节的占比
|
||||
|
||||
# # 添加总标题
|
||||
# fig.suptitle("水平传播方向在各个季节的分布变化", fontsize=16, fontweight="bold")
|
||||
|
||||
# 8、垂直传播方向
|
||||
# 设置 日期'date1' 列为索引
|
||||
filtered_df.set_index("date1", inplace=True)
|
||||
|
||||
# 按月份分组并计算百分比
|
||||
monthly_stats_df = (
|
||||
filtered_df.groupby([filtered_df.index.month, filtered_df.index.year])
|
||||
.apply(lambda x: pd.Series({"Upload (%)": (x["a"] == 1).mean() * 100, "Downward (%)": (x["a"] == -1).mean() * 100}))
|
||||
.reset_index(level=1, drop=True)
|
||||
)
|
||||
|
||||
# 按月份汇总这些年的数据
|
||||
monthly_avg_stats_df = monthly_stats_df.groupby(level=0).mean()
|
||||
# 确保索引是 numpy 数组
|
||||
dates = monthly_avg_stats_df.index.to_numpy()
|
||||
|
||||
# 绘制折线图
|
||||
ax8.plot(dates, monthly_avg_stats_df["Upload (%)"].to_numpy(), marker="o", label="Up (%)")
|
||||
ax8.plot(dates, monthly_avg_stats_df["Downward (%)"].to_numpy(), marker="o", label="Down (%)")
|
||||
|
||||
# 设置月份标签
|
||||
ax8.set_xticks(
|
||||
ticks=np.arange(1, 13), labels=["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], rotation=0
|
||||
) # 不倾斜,rotation=0表示倾斜45°
|
||||
# 添加图例、标题和坐标轴标签
|
||||
ax8.legend()
|
||||
ax8.set_title("每月上传/下传重力波占比", fontsize=24)
|
||||
ax8.set_xlabel("Month")
|
||||
ax8.set_ylabel("Percentage (%)")
|
||||
|
||||
# 9、动能和势能
|
||||
filtered_df.reset_index(inplace=True) # 取消索引并恢复为默认整数索引
|
||||
# 提取年月
|
||||
filtered_df["year_month"] = filtered_df["date1"].dt.to_period("M")
|
||||
# 计算每个月的动能和势能的平均值
|
||||
monthly_avg = filtered_df.groupby("year_month")[["Ek", "E_p"]].mean()
|
||||
|
||||
# 创建完整的月份范围(因为有的月份没数据)
|
||||
full_range = pd.period_range(start=monthly_avg.index.min(), end=monthly_avg.index.max(), freq="M")
|
||||
|
||||
# 创建一个新的 DataFrame 使用完整的年份月份范围
|
||||
full_monthly_avg = pd.DataFrame(index=full_range)
|
||||
# 将原始数据合并到新的 DataFrame 中
|
||||
full_monthly_avg = full_monthly_avg.join(monthly_avg)
|
||||
|
||||
# 确保 'Ek' 和 'E_p' 列为数值型
|
||||
full_monthly_avg["Ek"] = pd.to_numeric(full_monthly_avg["Ek"], errors="coerce")
|
||||
full_monthly_avg["E_p"] = pd.to_numeric(full_monthly_avg["E_p"], errors="coerce")
|
||||
|
||||
# 只显示每年6月、12月,简化图形
|
||||
|
||||
# 绘制 Ek、E_p
|
||||
ax9.plot(full_monthly_avg.index.values.astype(str), full_monthly_avg["Ek"].values, marker="o", linestyle="-", color="r", label="动能月平均值")
|
||||
ax9.plot(full_monthly_avg.index.values.astype(str), full_monthly_avg["E_p"].values, marker="o", linestyle="-", color="b", label="势能月平均值")
|
||||
|
||||
# 添加标题和标签
|
||||
ax9.set_title("动能和势能分布情况", fontsize=24)
|
||||
ax9.set_xlabel("Month", fontsize=14)
|
||||
ax9.set_ylabel("Wave energy (J/kg)", fontsize=14)
|
||||
|
||||
# 设定横轴标签
|
||||
months = full_monthly_avg.index.values.astype(str)
|
||||
# 获取所有年份的6月和12月的索引
|
||||
june_indices = [i for i, date in enumerate(months) if date.endswith("-06")]
|
||||
december_indices = [i for i, date in enumerate(months) if date.endswith("-12")]
|
||||
selected_indices = june_indices + december_indices
|
||||
|
||||
# 只显示选定的标签
|
||||
ax9.set_xticks(ticks=selected_indices, labels=[months[i] for i in selected_indices], rotation=45)
|
||||
|
||||
# 添加网格和图例
|
||||
# plt.grid()
|
||||
ax9.legend() # 显示图例
|
||||
|
||||
plt.rcParams["font.sans-serif"] = ["SimHei"] # 显示中文
|
||||
plt.rcParams["axes.unicode_minus"] = False # 正常显示负号
|
||||
|
||||
plt.tight_layout()
|
||||
plt.savefig(path)
|
||||
plt.close()
|
||||
|
||||
return True
|
||||
286
balloon/plot_year_backend.py
Normal file
286
balloon/plot_year_backend.py
Normal file
@ -0,0 +1,286 @@
|
||||
from io import BytesIO
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import seaborn as sns
|
||||
from windrose import WindroseAxes
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.gridspec as gridspec
|
||||
|
||||
|
||||
def plot_w_f(filtered_df, ax):
|
||||
min_val = 1
|
||||
max_val = 10
|
||||
bin_width = 0.5
|
||||
bins = np.arange(min_val, max_val + bin_width, bin_width)
|
||||
sns.histplot(filtered_df["w_f"], bins=bins, kde=False,
|
||||
edgecolor="black", stat="percent", ax=ax)
|
||||
ax.set_xlim(min_val, max_val)
|
||||
ax.set_title("w/f值统计结果", fontsize=24)
|
||||
ax.set_xlabel("w/f")
|
||||
ax.set_ylabel("Occurrence(%)")
|
||||
|
||||
|
||||
def plot_zhou_qi(filtered_df, ax):
|
||||
min_val = np.floor(filtered_df["zhou_qi"].min())
|
||||
max_val = np.ceil(filtered_df["zhou_qi"].max())
|
||||
bin_width = 1
|
||||
bins = np.arange(min_val, max_val + bin_width, bin_width)
|
||||
sns.histplot(filtered_df["zhou_qi"], bins=bins,
|
||||
kde=False, edgecolor="black", stat="percent", ax=ax)
|
||||
ax.set_xlim(min_val, max_val)
|
||||
ax.set_title("周期统计结果", fontsize=24)
|
||||
ax.set_xlabel("h")
|
||||
ax.set_ylabel("Occurrence(%)")
|
||||
|
||||
|
||||
def plot_ver_wave_len(filtered_df, ax):
|
||||
min_val = np.floor(filtered_df["ver_wave_len"].min())
|
||||
max_val = np.ceil(filtered_df["ver_wave_len"].max())
|
||||
bin_width = 0.5
|
||||
bins = np.arange(min_val, max_val + bin_width, bin_width)
|
||||
sns.histplot(filtered_df["ver_wave_len"], bins=bins,
|
||||
kde=False, edgecolor="black", stat="percent", ax=ax)
|
||||
ax.set_xlim(min_val, max_val)
|
||||
ax.set_title("垂直波长分布", fontsize=24)
|
||||
ax.set_xlabel("Vertical wavelength(km)")
|
||||
ax.set_ylabel("Occurrence(%)")
|
||||
|
||||
|
||||
def plot_hori_wave_len(filtered_df, ax):
|
||||
min_val = np.floor(filtered_df["hori_wave_len"].min())
|
||||
max_val = np.ceil(filtered_df["hori_wave_len"].max())
|
||||
bin_width = 100
|
||||
bins = np.arange(min_val, max_val + bin_width, bin_width)
|
||||
sns.histplot(filtered_df["hori_wave_len"], bins=bins,
|
||||
kde=False, edgecolor="black", stat="percent", ax=ax)
|
||||
ax.set_xlim(min_val, max_val)
|
||||
ax.set_title("水平波长分布", fontsize=24)
|
||||
ax.set_xlabel("Horizontal wavelength(km)")
|
||||
ax.set_ylabel("Occurrence(%)")
|
||||
|
||||
|
||||
def plot_c_x(filtered_df, ax):
|
||||
min_val = np.floor(filtered_df["c_x"].min())
|
||||
max_val = np.ceil(filtered_df["c_x"].max())
|
||||
bin_width = 10
|
||||
bins = np.arange(min_val, max_val + bin_width, bin_width)
|
||||
sns.histplot(filtered_df["c_x"], bins=bins, kde=False,
|
||||
edgecolor="black", stat="percent", ax=ax)
|
||||
ax.set_xlim(min_val, max_val)
|
||||
ax.set_title("纬向本征相速度", fontsize=24)
|
||||
ax.set_xlabel("Zonal phase speed (m/s)")
|
||||
ax.set_ylabel("Occurrence (%)")
|
||||
|
||||
|
||||
def plot_c_y(filtered_df, ax):
|
||||
min_val = np.floor(filtered_df["c_y"].min())
|
||||
max_val = np.ceil(filtered_df["c_y"].max())
|
||||
bin_width = 10
|
||||
bins = np.arange(min_val, max_val + bin_width, bin_width)
|
||||
sns.histplot(filtered_df["c_y"], bins=bins, kde=False,
|
||||
edgecolor="black", stat="percent", ax=ax)
|
||||
ax.set_xlim(min_val, max_val)
|
||||
ax.set_title("经向本征相速度", fontsize=24)
|
||||
ax.set_xlabel("Meridional phase speed (m/s)")
|
||||
ax.set_ylabel("Occurrence (%)")
|
||||
|
||||
|
||||
def plot_c_z(filtered_df, ax):
|
||||
min_val = filtered_df["c_z"].min()
|
||||
max_val = filtered_df["c_z"].max()
|
||||
bin_width = 0.1
|
||||
bins = np.arange(min_val, max_val + bin_width, bin_width)
|
||||
sns.histplot(filtered_df["c_z"], bins=bins, kde=False,
|
||||
edgecolor="black", stat="percent", ax=ax)
|
||||
ax.set_xlim(min_val, max_val)
|
||||
ax.set_title("垂直本征相速度", fontsize=24)
|
||||
ax.set_xlabel("Vertical phase speed (m/s)")
|
||||
ax.set_ylabel("Occurrence (%)")
|
||||
|
||||
|
||||
def plot_u1(filtered_df, ax):
|
||||
min_val = np.floor(filtered_df["u1"].min())
|
||||
max_val = np.ceil(filtered_df["u1"].max())
|
||||
bin_width = 0.5
|
||||
bins = np.arange(min_val, max_val + bin_width, bin_width)
|
||||
sns.histplot(filtered_df["u1"], bins=bins, kde=False,
|
||||
edgecolor="black", stat="percent", ax=ax)
|
||||
ax.set_xlim(min_val, max_val)
|
||||
ax.set_title(" ", fontsize=24)
|
||||
ax.set_xlabel("Zonal wind amplitude (m/s)")
|
||||
ax.set_ylabel("Occurrence (%)")
|
||||
|
||||
|
||||
def plot_v1(filtered_df, ax):
|
||||
min_val = np.floor(filtered_df["v1"].min())
|
||||
max_val = np.ceil(filtered_df["v1"].max())
|
||||
bin_width = 0.5
|
||||
bins = np.arange(min_val, max_val + bin_width, bin_width)
|
||||
sns.histplot(filtered_df["v1"], bins=bins, kde=False,
|
||||
edgecolor="black", stat="percent", ax=ax)
|
||||
ax.set_xlim(min_val, max_val)
|
||||
ax.set_title("扰动振幅统计结果", fontsize=24)
|
||||
ax.set_xlabel("Meridional wind amplitude (m/s)")
|
||||
ax.set_ylabel("Occurrence (%)")
|
||||
|
||||
|
||||
def plot_T1(filtered_df, ax):
|
||||
min_val = np.floor(filtered_df["T1"].min())
|
||||
max_val = np.ceil(filtered_df["T1"].max())
|
||||
bin_width = 0.5
|
||||
bins = np.arange(min_val, max_val + bin_width, bin_width)
|
||||
sns.histplot(filtered_df["T1"], bins=bins, kde=False,
|
||||
edgecolor="black", stat="percent", ax=ax)
|
||||
ax.set_xlim(min_val, max_val)
|
||||
ax.set_title(" ", fontsize=24)
|
||||
ax.set_xlabel("Temperature amplitude (K)")
|
||||
ax.set_ylabel("Occurrence (%)")
|
||||
|
||||
|
||||
def plot_MFu(filtered_df1, ax):
|
||||
min_val = np.floor(filtered_df1["MFu"].min())
|
||||
max_val = np.ceil(filtered_df1["MFu"].max())
|
||||
bin_width = 0.1
|
||||
bins = np.arange(min_val, max_val + bin_width, bin_width)
|
||||
sns.histplot(filtered_df1["MFu"], bins=bins, kde=False,
|
||||
edgecolor="black", stat="percent", ax=ax)
|
||||
ax.set_xlim(min_val, max_val)
|
||||
ax.set_title("纬向动量通量统计结果", fontsize=24)
|
||||
ax.set_xlabel("Zonal momentum flux(mPa)")
|
||||
ax.set_ylabel("Occurrence(%)")
|
||||
|
||||
|
||||
def plot_MFv(filtered_df1, ax):
|
||||
min_val = np.floor(filtered_df1["MFv"].min())
|
||||
max_val = np.ceil(filtered_df1["MFv"].max())
|
||||
bin_width = 0.1
|
||||
bins = np.arange(min_val, max_val + bin_width, bin_width)
|
||||
sns.histplot(filtered_df1["MFv"], bins=bins, kde=False,
|
||||
edgecolor="black", stat="percent", ax=ax)
|
||||
ax.set_xlim(min_val, max_val)
|
||||
ax.set_title("经向动量通量统计结果", fontsize=24)
|
||||
ax.set_xlabel("Meridional momentum flux(mPa)")
|
||||
ax.set_ylabel("Occurrence(%)")
|
||||
|
||||
|
||||
def plot_horizontal_propagation(filtered_df, ax, season):
|
||||
season_data = filtered_df[filtered_df["season"] == season]
|
||||
windrose = WindroseAxes.from_ax(ax)
|
||||
ax.set_title(season, fontsize=18)
|
||||
windrose.bar(season_data["b"], np.ones_like(
|
||||
season_data["b"]), normed=False)
|
||||
|
||||
|
||||
def plot_vertical_propagation(filtered_df, ax):
|
||||
filtered_df.set_index("date1", inplace=True)
|
||||
monthly_stats_df = (
|
||||
filtered_df.groupby([filtered_df.index.month, filtered_df.index.year])
|
||||
.apply(lambda x: pd.Series({"Upload (%)": (x["a"] == 1).mean() * 100, "Downward (%)": (x["a"] == -1).mean() * 100}))
|
||||
.reset_index(level=1, drop=True)
|
||||
)
|
||||
monthly_avg_stats_df = monthly_stats_df.groupby(level=0).mean()
|
||||
dates = monthly_avg_stats_df.index.to_numpy()
|
||||
ax.plot(dates, monthly_avg_stats_df["Upload (%)"].to_numpy(
|
||||
), marker="o", label="Up (%)")
|
||||
ax.plot(dates, monthly_avg_stats_df["Downward (%)"].to_numpy(
|
||||
), marker="o", label="Down (%)")
|
||||
ax.set_xticks(
|
||||
ticks=np.arange(1, 13), labels=["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], rotation=0
|
||||
)
|
||||
ax.legend()
|
||||
ax.set_title("每月上传/下传重力波占比", fontsize=24)
|
||||
ax.set_xlabel("Month")
|
||||
ax.set_ylabel("Percentage (%)")
|
||||
|
||||
|
||||
def plot_energy_distribution(filtered_df, ax):
|
||||
filtered_df.reset_index(inplace=True)
|
||||
filtered_df["year_month"] = filtered_df["date1"].dt.to_period("M")
|
||||
monthly_avg = filtered_df.groupby("year_month")[["Ek", "E_p"]].mean()
|
||||
full_range = pd.period_range(
|
||||
start=monthly_avg.index.min(), end=monthly_avg.index.max(), freq="M")
|
||||
full_monthly_avg = pd.DataFrame(index=full_range).join(monthly_avg)
|
||||
full_monthly_avg["Ek"] = pd.to_numeric(
|
||||
full_monthly_avg["Ek"], errors="coerce")
|
||||
full_monthly_avg["E_p"] = pd.to_numeric(
|
||||
full_monthly_avg["E_p"], errors="coerce")
|
||||
ax.plot(full_monthly_avg.index.values.astype(
|
||||
str), full_monthly_avg["Ek"].values, marker="o", linestyle="-", color="r", label="动能月平均值")
|
||||
ax.plot(full_monthly_avg.index.values.astype(
|
||||
str), full_monthly_avg["E_p"].values, marker="o", linestyle="-", color="b", label="势能月平均值")
|
||||
ax.set_title("动能和势能分布情况", fontsize=24)
|
||||
ax.set_xlabel("Month", fontsize=14)
|
||||
ax.set_ylabel("Wave energy (J/kg)", fontsize=14)
|
||||
months = full_monthly_avg.index.values.astype(str)
|
||||
june_indices = [i for i, date in enumerate(months) if date.endswith("-06")]
|
||||
december_indices = [i for i, date in enumerate(
|
||||
months) if date.endswith("-12")]
|
||||
selected_indices = june_indices + december_indices
|
||||
ax.set_xticks(ticks=selected_indices, labels=[
|
||||
months[i] for i in selected_indices], rotation=45)
|
||||
ax.legend()
|
||||
|
||||
|
||||
# def render_based_on_mode(df, mode, seaon = None):
|
||||
def render_based_on_mode(df, mode, season=None):
|
||||
buf = BytesIO()
|
||||
fig, ax = plt.subplots(figsize=(10, 6))
|
||||
if mode == "w/f值统计结果":
|
||||
plot_w_f(df, ax)
|
||||
elif mode == "周期统计结果":
|
||||
plot_zhou_qi(df, ax)
|
||||
elif mode == "垂直波长分布":
|
||||
plot_ver_wave_len(df, ax)
|
||||
elif mode == "水平波长分布":
|
||||
plot_hori_wave_len(df, ax)
|
||||
elif mode == "纬向本征相速度":
|
||||
plot_c_x(df, ax)
|
||||
elif mode == "经向本征相速度":
|
||||
plot_c_y(df, ax)
|
||||
elif mode == "垂直本征相速度":
|
||||
plot_c_z(df, ax)
|
||||
elif mode == "Zonal wind amplitude (m/s)":
|
||||
plot_u1(df, ax)
|
||||
elif mode == "扰动振幅统计结果":
|
||||
plot_v1(df, ax)
|
||||
elif mode == "Temperature amplitude (K)":
|
||||
plot_T1(df, ax)
|
||||
elif mode == "纬向动量通量统计结果":
|
||||
plot_MFu(df, ax)
|
||||
elif mode == "经向动量通量统计结果":
|
||||
plot_MFv(df, ax)
|
||||
elif mode == "horizontal propagation":
|
||||
plot_horizontal_propagation(df, ax, season)
|
||||
elif mode == "每月上传/下传重力波占比":
|
||||
plot_vertical_propagation(df, ax)
|
||||
elif mode == "动能和势能分布情况":
|
||||
plot_energy_distribution(df, ax)
|
||||
else:
|
||||
raise ValueError("Invalid mode")
|
||||
plt.rcParams["font.sans-serif"] = ["SimHei"] # 显示中文
|
||||
fig.savefig(buf)
|
||||
buf.seek(0)
|
||||
plt.close()
|
||||
return buf
|
||||
# if mode == ""
|
||||
|
||||
|
||||
def get_all_modes():
|
||||
return [
|
||||
"w/f值统计结果",
|
||||
"周期统计结果",
|
||||
"垂直波长分布",
|
||||
"水平波长分布",
|
||||
"纬向本征相速度",
|
||||
"经向本征相速度",
|
||||
"垂直本征相速度",
|
||||
"Zonal wind amplitude (m/s)",
|
||||
"扰动振幅统计结果",
|
||||
"Temperature amplitude (K)",
|
||||
"纬向动量通量统计结果",
|
||||
"经向动量通量统计结果",
|
||||
"horizontal propagation",
|
||||
"每月上传/下传重力波占比",
|
||||
"动能和势能分布情况",
|
||||
]
|
||||
41
balloon/read_data.py
Normal file
41
balloon/read_data.py
Normal file
@ -0,0 +1,41 @@
|
||||
import xarray as xr
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
from scipy.interpolate import interp1d
|
||||
|
||||
|
||||
# 定义四舍五入函数
|
||||
def round_to_nearest_multiple(value, multiple):
|
||||
return round(value / multiple) * multiple
|
||||
|
||||
|
||||
def read_data(path):
|
||||
# 读取数据
|
||||
with open(path, "rb") as f:
|
||||
df = xr.open_dataset(f).to_dataframe().dropna(how="any")
|
||||
|
||||
# 提取指定列
|
||||
columns_to_extract = ["alt", "press", "temp", "rh", "u", "v", "wspeed"]
|
||||
extracted_df = df[columns_to_extract].copy()
|
||||
|
||||
# 进行单位转换
|
||||
extracted_df["alt"] = extracted_df["alt"] / 1000 # km
|
||||
extracted_df["rh"] = extracted_df["rh"] * 100 # %
|
||||
|
||||
# 移除重复的高度值
|
||||
extracted_df = extracted_df.drop_duplicates(subset=["alt"])
|
||||
new_height = np.arange(extracted_df["alt"].min(), extracted_df["alt"].max() + 0.05, 0.05)
|
||||
|
||||
# 将每个高度值转换为最接近0.05的整数倍,并转化为数组
|
||||
rounded_heights = [round_to_nearest_multiple(height, 0.05) for height in new_height]
|
||||
rounded_heights_np = np.array(rounded_heights)
|
||||
|
||||
# 初始化一个新的 DataFrame 用于存储插值结果
|
||||
interpolated_data = pd.DataFrame({"alt": rounded_heights_np})
|
||||
|
||||
# 对每个因变量进行线性插值
|
||||
for col in ["press", "temp", "rh", "u", "v", "wspeed"]:
|
||||
interp_func = interp1d(extracted_df["alt"], extracted_df[col], kind="linear", fill_value="extrapolate")
|
||||
interpolated_data[col] = interp_func(rounded_heights_np)
|
||||
|
||||
return interpolated_data
|
||||
538
cosmic/multiple.py
Normal file
538
cosmic/multiple.py
Normal file
@ -0,0 +1,538 @@
|
||||
import os
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from scipy.interpolate import interp1d
|
||||
from scipy.optimize import curve_fit
|
||||
import netCDF4 as nc
|
||||
import matplotlib.pyplot as plt
|
||||
import seaborn as sns
|
||||
# 设置支持中文的字体
|
||||
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei'] # 设置字体为微软雅黑
|
||||
plt.rcParams['axes.unicode_minus'] = False # 正常显示负号
|
||||
|
||||
# 定义处理单个文件的函数
|
||||
|
||||
|
||||
def process_single_file(base_folder_path, i):
|
||||
# 构建当前文件夹的路径
|
||||
folder_name = f"atmPrf_repro2021_2018_00{i}" if i < 10 else f"atmPrf_repro2021_2008_0{i}"
|
||||
folder_path = os.path.join(base_folder_path, folder_name)
|
||||
# 检查文件夹是否存在
|
||||
if os.path.exists(folder_path):
|
||||
dfs = []
|
||||
# 遍历文件夹中的文件
|
||||
for file_name in os.listdir(folder_path):
|
||||
if file_name.endswith('.0390_nc'):
|
||||
finfo = os.path.join(folder_path, file_name)
|
||||
print(f"正在处理文件: {finfo}")
|
||||
try:
|
||||
dataset = nc.Dataset(finfo, 'r')
|
||||
# 提取变量数据
|
||||
temp = dataset.variables['Temp'][:]
|
||||
altitude = dataset.variables['MSL_alt'][:]
|
||||
lat = dataset.variables['Lat'][:]
|
||||
lon = dataset.variables['Lon'][:]
|
||||
# 创建DataFrame
|
||||
df = pd.DataFrame({
|
||||
'Longitude': lon,
|
||||
'Latitude': lat,
|
||||
'Altitude': altitude,
|
||||
'Temperature': temp
|
||||
})
|
||||
dataset.close()
|
||||
# 剔除高度大于60的行
|
||||
df = df[df['Altitude'] <= 60]
|
||||
# 对每个文件的数据进行插值
|
||||
alt_interp = np.linspace(
|
||||
df['Altitude'].min(), df['Altitude'].max(), 3000)
|
||||
f_alt = interp1d(
|
||||
df['Altitude'], df['Altitude'], kind='linear', fill_value="extrapolate")
|
||||
f_lon = interp1d(
|
||||
df['Altitude'], df['Longitude'], kind='linear', fill_value="extrapolate")
|
||||
f_lat = interp1d(
|
||||
df['Altitude'], df['Latitude'], kind='linear', fill_value="extrapolate")
|
||||
f_temp = interp1d(
|
||||
df['Altitude'], df['Temperature'], kind='linear', fill_value="extrapolate")
|
||||
# 计算插值结果
|
||||
interpolated_alt = f_alt(alt_interp)
|
||||
interpolated_lon = f_lon(alt_interp)
|
||||
interpolated_lat = f_lat(alt_interp)
|
||||
interpolated_temp = f_temp(alt_interp)
|
||||
# 创建插值后的DataFrame
|
||||
interpolated_df = pd.DataFrame({
|
||||
'Altitude': interpolated_alt,
|
||||
'Longitude': interpolated_lon,
|
||||
'Latitude': interpolated_lat,
|
||||
'Temperature': interpolated_temp
|
||||
})
|
||||
# 将插值后的DataFrame添加到列表中
|
||||
dfs.append(interpolated_df)
|
||||
except Exception as e:
|
||||
print(f"处理文件 {finfo} 时出错: {e}")
|
||||
# 按行拼接所有插值后的DataFrame
|
||||
final_df = pd.concat(dfs, axis=0, ignore_index=True)
|
||||
# 获取 DataFrame 的长度
|
||||
num_rows = len(final_df)
|
||||
# 生成一个每3000个数从0到2999的序列并重复
|
||||
altitude_values = np.tile(
|
||||
np.arange(3000), num_rows // 3000 + 1)[:num_rows]
|
||||
# 将生成的值赋给 DataFrame 的 'Altitude' 列
|
||||
final_df['Altitude'] = altitude_values
|
||||
# 摄氏度换算开尔文
|
||||
final_df['Temperature'] = final_df['Temperature'] + 273.15
|
||||
|
||||
# 筛选出纬度在30到40度之间的数据
|
||||
latitude_filtered_df = final_df[(
|
||||
final_df['Latitude'] >= 30) & (final_df['Latitude'] <= 40)]
|
||||
|
||||
# 划分经度网格,20°的网格
|
||||
lon_min, lon_max = latitude_filtered_df['Longitude'].min(
|
||||
), latitude_filtered_df['Longitude'].max()
|
||||
lon_bins = np.arange(lon_min, lon_max + 20, 20) # 创建经度网格边界
|
||||
# 将数据分配到网格中
|
||||
latitude_filtered_df['Longitude_Grid'] = np.digitize(
|
||||
latitude_filtered_df['Longitude'], lon_bins) - 1
|
||||
|
||||
# 对相同高度的温度取均值,忽略NaN
|
||||
altitude_temperature_mean = latitude_filtered_df.groupby(
|
||||
'Altitude')['Temperature'].mean().reset_index()
|
||||
# 重命名列,使其更具可读性
|
||||
altitude_temperature_mean.columns = ['Altitude', 'Mean_Temperature']
|
||||
|
||||
# 定义高度的范围(这里从0到最短段)
|
||||
altitude_range = range(0, 3000)
|
||||
all_heights_mean_temperature = [] # 用于存储所有高度下的温度均值结果
|
||||
for altitude in altitude_range:
|
||||
# 筛选出当前高度的所有数据
|
||||
altitude_df = latitude_filtered_df[latitude_filtered_df['Altitude'] == altitude]
|
||||
# 对Longitude_Grid同一区间的温度取均值
|
||||
temperature_mean_by_grid = altitude_df.groupby(
|
||||
'Longitude_Grid')['Temperature'].mean().reset_index()
|
||||
# 重命名列,使其更具可读性
|
||||
temperature_mean_by_grid.columns = [
|
||||
'Longitude_Grid', 'Mean_Temperature']
|
||||
# 添加高度信息列,方便后续区分不同高度的结果
|
||||
temperature_mean_by_grid['Altitude'] = altitude
|
||||
# 将当前高度的结果添加到列表中
|
||||
all_heights_mean_temperature.append(temperature_mean_by_grid)
|
||||
# 将所有高度的结果合并为一个DataFrame
|
||||
combined_mean_temperature_df = pd.concat(
|
||||
all_heights_mean_temperature, ignore_index=True)
|
||||
|
||||
# 基于Altitude列合并两个DataFrame,只保留能匹配上的行
|
||||
merged_df = pd.merge(combined_mean_temperature_df,
|
||||
altitude_temperature_mean, on='Altitude', how='inner')
|
||||
# 计算差值(减去wn0的扰动)
|
||||
merged_df['Temperature_Difference'] = merged_df['Mean_Temperature_x'] - \
|
||||
merged_df['Mean_Temperature_y']
|
||||
|
||||
# 按Altitude分组
|
||||
grouped = merged_df.groupby('Altitude')
|
||||
|
||||
def single_harmonic(x, A, phi):
|
||||
return A * np.sin(2 * np.pi / (18 / k) * x + phi)
|
||||
# 初始化存储每个高度的最佳拟合参数、拟合曲线、残差值以及背景温度的字典
|
||||
fit_results = {}
|
||||
fitted_curves = {}
|
||||
residuals = {}
|
||||
background_temperatures = {}
|
||||
for altitude, group in grouped:
|
||||
y_data = group['Temperature_Difference'].values
|
||||
x_data = np.arange(len(y_data))
|
||||
wn0_data = group['Mean_Temperature_y'].values # 获取同一高度下的wn0数据
|
||||
# 检查Temperature_Difference列是否全部为NaN
|
||||
if np.all(np.isnan(y_data)):
|
||||
fit_results[altitude] = {
|
||||
'A': [np.nan] * 5, 'phi': [np.nan] * 5}
|
||||
fitted_curves[altitude] = [np.nan * x_data] * 5
|
||||
residuals[altitude] = np.nan * x_data
|
||||
background_temperatures[altitude] = np.nan * x_data
|
||||
else:
|
||||
# 替换NaN值为非NaN值的均值
|
||||
y_data = np.where(np.isnan(y_data), np.nanmean(y_data), y_data)
|
||||
# 初始化存储WN参数和曲线的列表
|
||||
wn_params = []
|
||||
wn_curves = []
|
||||
# 计算wn0(使用Mean_Temperature_y列数据)
|
||||
wn0 = wn0_data
|
||||
|
||||
# 对WN1至WN5进行拟合
|
||||
for k in range(1, 6):
|
||||
# 更新单谐波函数中的k值
|
||||
def harmonic_func(
|
||||
x, A, phi): return single_harmonic(x, A, phi)
|
||||
# 使用curve_fit进行拟合
|
||||
popt, pcov = curve_fit(harmonic_func, x_data, y_data, p0=[
|
||||
np.nanmax(y_data) - np.nanmin(y_data), 0])
|
||||
A_fit, phi_fit = popt
|
||||
# 存储拟合结果
|
||||
wn_params.append({'A': A_fit, 'phi': phi_fit})
|
||||
# 使用拟合参数生成拟合曲线
|
||||
WN = harmonic_func(x_data, A_fit, phi_fit)
|
||||
wn_curves.append(WN)
|
||||
# 计算残差值
|
||||
y_data = y_data - WN # 使用残差值作为下一次拟合的y_data
|
||||
# 存储结果
|
||||
fit_results[altitude] = wn_params
|
||||
fitted_curves[altitude] = wn_curves
|
||||
residuals[altitude] = y_data
|
||||
# 计算同一高度下的背景温度(wn0 + wn1 + wn2 + wn3 + wn4 + wn5)
|
||||
wn_sum = np.sum([wn0] + wn_curves, axis=0)
|
||||
background_temperatures[altitude] = wn_sum
|
||||
|
||||
# 将每个字典转换成一个 DataFrame
|
||||
df = pd.DataFrame(residuals)
|
||||
# 使用前向填充(用上一个有效值填充 NaN)
|
||||
df.ffill(axis=1, inplace=True)
|
||||
# 初始化一个新的字典来保存处理结果
|
||||
result = {}
|
||||
# 定义滤波范围
|
||||
lambda_low = 2 # 2 km
|
||||
lambda_high = 15 # 15 km
|
||||
f_low = 2 * np.pi / lambda_high
|
||||
f_high = 2 * np.pi / lambda_low
|
||||
# 循环处理df的每一行(每个高度)
|
||||
for idx, residuals_array in df.iterrows():
|
||||
# 提取有效值
|
||||
valid_values = np.ma.masked_array(
|
||||
residuals_array, np.isnan(residuals_array))
|
||||
compressed_values = valid_values.compressed() # 去除NaN值后的数组
|
||||
N = len(compressed_values) # 有效值的数量
|
||||
# 如果有效值为空(即所有值都是NaN),则将结果设置为NaN
|
||||
if N == 0:
|
||||
result[idx] = np.full_like(residuals_array, np.nan)
|
||||
else:
|
||||
# 时间序列和频率
|
||||
dt = 0.02 # 假设的时间间隔
|
||||
n = np.arange(N)
|
||||
f = n / (N * dt)
|
||||
# 傅里叶变换
|
||||
y = np.fft.fft(compressed_values)
|
||||
# 频率滤波
|
||||
yy = y.copy()
|
||||
freq_filter = (f >= f_low) & (f <= f_high)
|
||||
yy[~freq_filter] = 0
|
||||
# 逆傅里叶变换
|
||||
perturbation_after = np.real(np.fft.ifft(yy))
|
||||
# 将处理结果插回到result字典中
|
||||
result[idx] = perturbation_after
|
||||
|
||||
# 处理背景温度和扰动温度数据格式
|
||||
heights = list(background_temperatures.keys())
|
||||
data_length = len(next(iter(background_temperatures.values())))
|
||||
background_matrix = np.zeros((data_length, len(heights)))
|
||||
for idx, height in enumerate(heights):
|
||||
background_matrix[:, idx] = background_temperatures[height]
|
||||
|
||||
heights = list(result.keys())
|
||||
data_length = len(next(iter(result.values())))
|
||||
perturbation_matrix = np.zeros((data_length, len(heights)))
|
||||
for idx, height in enumerate(heights):
|
||||
perturbation_matrix[:, idx] = result[height]
|
||||
perturbation_matrix = perturbation_matrix.T
|
||||
|
||||
# 计算 Brunt-Väisälä 频率和势能
|
||||
heights_for_calc = np.linspace(0, 60, 3000) * 1000
|
||||
|
||||
def brunt_vaisala_frequency(g, BT_z, c_p, heights):
|
||||
# 计算位温随高度的变化率
|
||||
dBT_z_dz = np.gradient(BT_z, heights)
|
||||
# 计算 Brunt-Väisälä 频率,根号内取绝对值
|
||||
frequency_squared = (g / BT_z) * ((g / c_p) + dBT_z_dz)
|
||||
frequency = np.sqrt(np.abs(frequency_squared))
|
||||
return frequency
|
||||
|
||||
# 计算势能
|
||||
def calculate_gravitational_potential_energy(g, BT_z, N_z, PT_z):
|
||||
# 计算势能
|
||||
return 0.5 * ((g / N_z) ** 2) * ((PT_z / BT_z) ** 2)
|
||||
g = 9.81 # 重力加速度
|
||||
c_p = 1004.5 # 比热容
|
||||
N_z_matrix = []
|
||||
PT_z_matrix = []
|
||||
for i in range(background_matrix.shape[0]):
|
||||
BT_z = np.array(background_matrix[i])
|
||||
PT_z = np.array(perturbation_matrix[i])
|
||||
N_z = brunt_vaisala_frequency(g, BT_z, c_p, heights_for_calc)
|
||||
PW = calculate_gravitational_potential_energy(g, BT_z, N_z, PT_z)
|
||||
N_z_matrix.append(N_z)
|
||||
PT_z_matrix.append(PW)
|
||||
ktemp_Nz = np.vstack(N_z_matrix)
|
||||
ktemp_Ptz = np.vstack(PT_z_matrix)
|
||||
mean_ktemp_Nz = np.mean(ktemp_Nz, axis=0)
|
||||
mean_ktemp_Ptz = np.mean(ktemp_Ptz, axis=0)
|
||||
|
||||
return mean_ktemp_Nz, mean_ktemp_Ptz
|
||||
else:
|
||||
print(f"文件夹 {folder_path} 不存在。")
|
||||
return None, None
|
||||
|
||||
|
||||
# 主循环,处理1到3个文件
|
||||
base_folder_path = r"./cosmic/data/2018"
|
||||
all_mean_ktemp_Nz = []
|
||||
all_mean_ktemp_Ptz = []
|
||||
for file_index in range(1, 365):
|
||||
mean_ktemp_Nz, mean_ktemp_Ptz = process_single_file(
|
||||
base_folder_path, file_index)
|
||||
if mean_ktemp_Nz is not None and mean_ktemp_Ptz is not None:
|
||||
all_mean_ktemp_Nz.append(mean_ktemp_Nz)
|
||||
all_mean_ktemp_Ptz.append(mean_ktemp_Ptz)
|
||||
# 转换每个数组为二维形状
|
||||
final_mean_ktemp_Nz = np.vstack([arr.reshape(1, -1)
|
||||
for arr in all_mean_ktemp_Nz])
|
||||
final_mean_ktemp_Ptz = np.vstack(
|
||||
[arr.reshape(1, -1) for arr in all_mean_ktemp_Ptz])
|
||||
# 使用条件索引替换大于50的值为NaN
|
||||
final_mean_ktemp_Ptz[final_mean_ktemp_Ptz > 50] = np.nan
|
||||
# heights 为每个高度的值
|
||||
heights = np.linspace(0, 60, 3000)
|
||||
df_final_mean_ktemp_Ptz = pd.DataFrame(final_mean_ktemp_Ptz)
|
||||
df_final_mean_ktemp_Nz = pd.DataFrame(final_mean_ktemp_Nz)
|
||||
# -------------------------------------------------绘制年统计图------------------------------------
|
||||
# -----------绘制浮力频率年统计图-----------------------
|
||||
data = df_final_mean_ktemp_Nz.T
|
||||
# 对每个元素进行平方(计算N2)
|
||||
data = data ** 2
|
||||
data = data*10000 # (绘图好看)
|
||||
# 将大于 10 的值替换为 NaN(个别异常值)
|
||||
data[data > 10] = np.nan
|
||||
# 绘制热力图的函数
|
||||
|
||||
|
||||
def plot_heatmap(data, heights, title):
|
||||
plt.figure(figsize=(10, 8))
|
||||
# 绘制热力图,数据中的行代表高度,列代表天数
|
||||
sns.heatmap(data, cmap='coolwarm', xticklabels=1,
|
||||
yticklabels=50, cbar_kws={'label': 'Value'})
|
||||
plt.xlabel('Day')
|
||||
plt.ylabel('Height (km)')
|
||||
plt.title(title)
|
||||
# 设置x轴的刻度,使其每30天显示一个标签
|
||||
num_days = data.shape[1]
|
||||
x_tick_positions = np.arange(0, num_days, 30)
|
||||
x_tick_labels = np.arange(0, num_days, 30)
|
||||
plt.xticks(x_tick_positions, x_tick_labels)
|
||||
# 设置y轴的刻度,使其显示对应的高度
|
||||
plt.yticks(np.linspace(
|
||||
0, data.shape[0] - 1, 6), np.round(np.linspace(heights[0], heights[-1], 6), 2))
|
||||
# 反转 y 轴,使 0 在底部
|
||||
plt.gca().invert_yaxis()
|
||||
plt.show()
|
||||
|
||||
|
||||
# 调用函数绘制热力图
|
||||
plot_heatmap(data, heights, 'Heatmap of final_mean_ktemp_Nz(10^(-4))')
|
||||
# -----------------------------------------------------------------------------
|
||||
# -------------绘制重力势能年统计图------------------------------------------------
|
||||
data1 = df_final_mean_ktemp_Ptz.T
|
||||
# 绘制热力图的函数
|
||||
|
||||
|
||||
def plot_heatmap(data, heights, title):
|
||||
plt.figure(figsize=(10, 8))
|
||||
# 绘制热力图,数据中的行代表高度,列代表天数
|
||||
sns.heatmap(data, cmap='coolwarm', xticklabels=1,
|
||||
yticklabels=50, cbar_kws={'label': 'Value'})
|
||||
plt.xlabel('Day')
|
||||
plt.ylabel('Height (km)')
|
||||
plt.title(title)
|
||||
# 设置x轴的刻度,使其每30天显示一个标签
|
||||
num_days = data.shape[1]
|
||||
x_tick_positions = np.arange(0, num_days, 30)
|
||||
x_tick_labels = np.arange(0, num_days, 30)
|
||||
plt.xticks(x_tick_positions, x_tick_labels)
|
||||
# 设置y轴的刻度,使其显示对应的高度
|
||||
plt.yticks(np.linspace(
|
||||
0, data.shape[0] - 1, 6), np.round(np.linspace(heights[0], heights[-1], 6), 2))
|
||||
# 反转 y 轴,使 0 在底部
|
||||
plt.gca().invert_yaxis()
|
||||
plt.show()
|
||||
|
||||
|
||||
# 调用函数绘制热力图
|
||||
plot_heatmap(data1, heights, 'Heatmap of final_mean_ktemp_Ptz(J/kg)')
|
||||
# ------------------------绘制月统计图---------------------------------------------------------------------------------
|
||||
# ----------绘制浮力频率月统计图-------------------------------------------------
|
||||
# 获取总列数
|
||||
num_columns = data.shape[1]
|
||||
# 按30列分组计算均值
|
||||
averaged_df = []
|
||||
# 逐步处理每30列
|
||||
for i in range(0, num_columns, 30):
|
||||
# 获取当前范围内的列,并计算均值
|
||||
subset = data.iloc[:, i:i+30] # 获取第i到i+29列
|
||||
mean_values = subset.mean(axis=1) # 对每行计算均值
|
||||
averaged_df.append(mean_values) # 将均值添加到列表
|
||||
# 将结果转化为一个新的 DataFrame
|
||||
averaged_df = pd.DataFrame(averaged_df).T
|
||||
# 1. 每3000行取一个均值
|
||||
# 获取总行数
|
||||
num_rows = averaged_df.shape[0]
|
||||
# 创建一个新的列表来存储每3000行的均值
|
||||
averaged_by_rows_df = []
|
||||
# 逐步处理每3000行
|
||||
for i in range(0, num_rows, 3000):
|
||||
# 获取当前范围内的行
|
||||
subset = averaged_df.iloc[i:i+3000, :] # 获取第i到i+99行
|
||||
mean_values = subset.mean(axis=0) # 对每列计算均值
|
||||
averaged_by_rows_df.append(mean_values) # 将均值添加到列表
|
||||
# 将结果转化为一个新的 DataFrame
|
||||
averaged_by_rows_df = pd.DataFrame(averaged_by_rows_df)
|
||||
# 绘制折线图
|
||||
plt.figure(figsize=(10, 6)) # 设置图形的大小
|
||||
plt.plot(averaged_by_rows_df.columns, averaged_by_rows_df.mean(
|
||||
axis=0), marker='o', color='b', label='平均值')
|
||||
# 添加标题和标签
|
||||
plt.title('每月平均N^2的折线图')
|
||||
plt.xlabel('月份')
|
||||
plt.ylabel('N^2(10^-4)')
|
||||
plt.legend()
|
||||
# 显示图形
|
||||
plt.grid(True)
|
||||
plt.xticks(rotation=45) # 让x轴标签(月份)倾斜,以便更清晰显示
|
||||
plt.tight_layout()
|
||||
plt.show()
|
||||
# ------------重力势能的月统计-----------------------------------
|
||||
# 获取总列数
|
||||
num_columns = data1.shape[1]
|
||||
# 按30列分组计算均值
|
||||
averaged_df = []
|
||||
# 逐步处理每30列
|
||||
for i in range(0, num_columns, 30):
|
||||
# 获取当前范围内的列,并计算均值
|
||||
subset = data1.iloc[:, i:i+30] # 获取第i到i+29列
|
||||
mean_values = subset.mean(axis=1) # 对每行计算均值
|
||||
averaged_df.append(mean_values) # 将均值添加到列表
|
||||
# 将结果转化为一个新的 DataFrame
|
||||
averaged_df = pd.DataFrame(averaged_df).T
|
||||
# 1. 每3000行取一个均值
|
||||
# 获取总行数
|
||||
num_rows = averaged_df.shape[0]
|
||||
# 创建一个新的列表来存储每3000行的均值
|
||||
averaged_by_rows_df = []
|
||||
# 逐步处理每3000行
|
||||
for i in range(0, num_rows, 3000):
|
||||
# 获取当前范围内的行
|
||||
subset = averaged_df.iloc[i:i+3000, :] # 获取第i到i+99行
|
||||
mean_values = subset.mean(axis=0) # 对每列计算均值
|
||||
averaged_by_rows_df.append(mean_values) # 将均值添加到列表
|
||||
# 将结果转化为一个新的 DataFrame
|
||||
averaged_by_rows_df = pd.DataFrame(averaged_by_rows_df)
|
||||
# 绘制折线图
|
||||
plt.figure(figsize=(10, 6)) # 设置图形的大小
|
||||
plt.plot(averaged_by_rows_df.columns, averaged_by_rows_df.mean(
|
||||
axis=0), marker='o', color='b', label='平均值')
|
||||
# 添加标题和标签
|
||||
plt.title('每月平均重力势能的折线图')
|
||||
plt.xlabel('月份')
|
||||
plt.ylabel('重力势能(J/Kg)')
|
||||
plt.legend()
|
||||
# 显示图形
|
||||
plt.grid(True)
|
||||
plt.xticks(rotation=45) # 让x轴标签(月份)倾斜,以便更清晰显示
|
||||
plt.tight_layout()
|
||||
plt.show()
|
||||
|
||||
|
||||
# 获取总列数
|
||||
total_columns = data.shape[1]
|
||||
# 用于存储每一组30列计算得到的均值列数据(最终会构成新的DataFrame)
|
||||
mean_columns = []
|
||||
# 分组序号,用于生成列名时区分不同的均值列,从1开始
|
||||
group_index = 1
|
||||
# 按照每30列一组进行划分(不滑动)
|
||||
for start_col in range(0, total_columns, 30):
|
||||
end_col = start_col + 30
|
||||
if end_col > total_columns:
|
||||
end_col = total_columns
|
||||
# 选取当前组的30列(如果不足30列,按实际剩余列数选取)
|
||||
group_data = data.iloc[:, start_col:end_col]
|
||||
# 按行对当前组的列数据求和
|
||||
sum_per_row = group_data.sum(axis=1)
|
||||
# 计算平均(每一组的平均,每行都有一个平均结果)
|
||||
mean_per_row = sum_per_row / (end_col - start_col)
|
||||
# 生成新的列名,格式为'Mean_分组序号',例如'Mean_1'、'Mean_2'等
|
||||
new_column_name = f'Mean_{group_index}'
|
||||
group_index += 1
|
||||
# 将当前组计算得到的均值列添加到列表中
|
||||
mean_columns.append(mean_per_row)
|
||||
# 将所有的均值列合并为一个新的DataFrame(列方向合并)
|
||||
new_mean_df = pd.concat(mean_columns, axis=1)
|
||||
# 按行对new_mean_df所有列的数据进行求和,得到一个Series,索引与new_mean_df的索引一致,每个元素是每行的总和
|
||||
row_sums = new_mean_df.sum(axis=1)
|
||||
# 计算所有行总和的均值
|
||||
mean_value = row_sums.mean()
|
||||
# 设置中文字体为黑体,解决中文显示问题(Windows系统下),如果是其他系统或者有其他字体需求可适当调整
|
||||
plt.rcParams['font.sans-serif'] = ['SimHei']
|
||||
# 解决负号显示问题
|
||||
plt.rcParams['axes.unicode_minus'] = False
|
||||
# 提取月份作为x轴标签(假设mean_value的索引就是月份信息)
|
||||
months = mean_value.index.tolist()
|
||||
# 提取均值数据作为y轴数据
|
||||
energy_values = mean_value.tolist()
|
||||
# 创建图形和坐标轴对象
|
||||
fig, ax = plt.subplots(figsize=(10, 6))
|
||||
# 绘制折线图
|
||||
ax.plot(months, energy_values, marker='o', linestyle='-', color='b')
|
||||
# 设置坐标轴标签和标题
|
||||
ax.set_xlabel('月份')
|
||||
ax.set_ylabel('平均浮力频率')
|
||||
ax.set_title('每月浮力频率变化趋势')
|
||||
# 设置x轴刻度,让其旋转一定角度以便更好地显示所有月份标签,避免重叠
|
||||
plt.xticks(rotation=45)
|
||||
# 显示网格线,增强图表可读性
|
||||
ax.grid(True)
|
||||
# 显示图形
|
||||
plt.show()
|
||||
# --------------------------------绘制重力势能月统计图------------------------------
|
||||
# 获取总列数
|
||||
total_columns = data1.shape[1]
|
||||
# 用于存储每一组30列计算得到的均值列数据(最终会构成新的DataFrame)
|
||||
mean_columns = []
|
||||
# 分组序号,用于生成列名时区分不同的均值列,从1开始
|
||||
group_index = 1
|
||||
# 按照每30列一组进行划分(不滑动)
|
||||
for start_col in range(0, total_columns, 30):
|
||||
end_col = start_col + 30
|
||||
if end_col > total_columns:
|
||||
end_col = total_columns
|
||||
# 选取当前组的30列(如果不足30列,按实际剩余列数选取)
|
||||
group_data = data1.iloc[:, start_col:end_col]
|
||||
# 按行对当前组的列数据求和
|
||||
sum_per_row = group_data.sum(axis=1)
|
||||
# 计算平均(每一组的平均,每行都有一个平均结果)
|
||||
mean_per_row = sum_per_row / (end_col - start_col)
|
||||
# 生成新的列名,格式为'Mean_分组序号',例如'Mean_1'、'Mean_2'等
|
||||
new_column_name = f'Mean_{group_index}'
|
||||
group_index += 1
|
||||
# 将当前组计算得到的均值列添加到列表中
|
||||
mean_columns.append(mean_per_row)
|
||||
# 将所有的均值列合并为一个新的DataFrame(列方向合并)
|
||||
new_mean_df = pd.concat(mean_columns, axis=1)
|
||||
# 按行对new_mean_df所有列的数据进行求和,得到一个Series,索引与new_mean_df的索引一致,每个元素是每行的总和
|
||||
row_sums = new_mean_df.sum(axis=1)
|
||||
# 计算所有行总和的均值
|
||||
mean_value = row_sums.mean()
|
||||
# 设置中文字体为黑体,解决中文显示问题(Windows系统下),如果是其他系统或者有其他字体需求可适当调整
|
||||
plt.rcParams['font.sans-serif'] = ['SimHei']
|
||||
# 解决负号显示问题
|
||||
plt.rcParams['axes.unicode_minus'] = False
|
||||
# 提取月份作为x轴标签(假设mean_value的索引就是月份信息)
|
||||
months = mean_value.index.tolist()
|
||||
# 提取均值数据作为y轴数据
|
||||
energy_values = mean_value.tolist()
|
||||
# 创建图形和坐标轴对象
|
||||
fig, ax = plt.subplots(figsize=(10, 6))
|
||||
# 绘制折线图
|
||||
ax.plot(months, energy_values, marker='o', linestyle='-', color='b')
|
||||
# 设置坐标轴标签和标题
|
||||
ax.set_xlabel('月份')
|
||||
ax.set_ylabel('平均浮力频率')
|
||||
ax.set_title('每月浮力频率变化趋势')
|
||||
# 设置x轴刻度,让其旋转一定角度以便更好地显示所有月份标签,避免重叠
|
||||
plt.xticks(rotation=45)
|
||||
# 显示网格线,增强图表可读性
|
||||
ax.grid(True)
|
||||
# 显示图形
|
||||
plt.show()
|
||||
393
cosmic/single.py
Normal file
393
cosmic/single.py
Normal file
@ -0,0 +1,393 @@
|
||||
import os
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from scipy.interpolate import interp1d
|
||||
from scipy.optimize import curve_fit
|
||||
import netCDF4 as nc
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
# 模块1: 数据读取与预处理
|
||||
|
||||
|
||||
def read_and_preprocess_data(base_folder_path, day_num):
|
||||
"""
|
||||
读取指定路径下指定天数的相关数据文件,进行插值等预处理操作
|
||||
|
||||
参数:
|
||||
base_folder_path (str): 基础文件夹路径
|
||||
day_num (int): 表示天数的序号(范围按代码中原本逻辑处理)
|
||||
|
||||
返回:
|
||||
final_df (pd.DataFrame): 预处理后的最终数据框
|
||||
"""
|
||||
dfs = []
|
||||
# 构建当前文件夹的路径
|
||||
folder_name = f"atmPrf_repro2021_2018_00{day_num}" if day_num < 10 else f"atmPrf_repro2021_2018_0{day_num}"
|
||||
folder_path = os.path.join(base_folder_path, folder_name)
|
||||
# 检查文件夹是否存在
|
||||
if os.path.exists(folder_path):
|
||||
# 遍历文件夹中的文件
|
||||
for file_name in os.listdir(folder_path):
|
||||
if file_name.endswith('.0390_nc'):
|
||||
finfo = os.path.join(folder_path, file_name)
|
||||
print(f"正在处理文件: {finfo}")
|
||||
try:
|
||||
dataset = nc.Dataset(finfo, 'r')
|
||||
# 提取变量数据
|
||||
temp = dataset.variables['Temp'][:]
|
||||
altitude = dataset.variables['MSL_alt'][:]
|
||||
lat = dataset.variables['Lat'][:]
|
||||
lon = dataset.variables['Lon'][:]
|
||||
# 创建DataFrame
|
||||
df = pd.DataFrame({
|
||||
'Longitude': lon,
|
||||
'Latitude': lat,
|
||||
'Altitude': altitude,
|
||||
'Temperature': temp
|
||||
})
|
||||
dataset.close()
|
||||
# 剔除高度大于60的行
|
||||
df = df[df['Altitude'] <= 60]
|
||||
|
||||
# 对每个文件的数据进行插值
|
||||
alt_interp = np.linspace(
|
||||
df['Altitude'].min(), df['Altitude'].max(), 3000)
|
||||
f_alt = interp1d(
|
||||
df['Altitude'], df['Altitude'], kind='linear', fill_value="extrapolate")
|
||||
f_lon = interp1d(
|
||||
df['Altitude'], df['Longitude'], kind='linear', fill_value="extrapolate")
|
||||
f_lat = interp1d(
|
||||
df['Altitude'], df['Latitude'], kind='linear', fill_value="extrapolate")
|
||||
f_temp = interp1d(
|
||||
df['Altitude'], df['Temperature'], kind='linear', fill_value="extrapolate")
|
||||
|
||||
# 计算插值结果
|
||||
interpolated_alt = f_alt(alt_interp)
|
||||
interpolated_lon = f_lon(alt_interp)
|
||||
interpolated_lat = f_lat(alt_interp)
|
||||
interpolated_temp = f_temp(alt_interp)
|
||||
|
||||
# 创建插值后的DataFrame
|
||||
interpolated_df = pd.DataFrame({
|
||||
'Altitude': interpolated_alt,
|
||||
'Longitude': interpolated_lon,
|
||||
'Latitude': interpolated_lat,
|
||||
'Temperature': interpolated_temp
|
||||
})
|
||||
|
||||
# 将插值后的DataFrame添加到列表中
|
||||
dfs.append(interpolated_df)
|
||||
except Exception as e:
|
||||
print(f"处理文件 {finfo} 时出错: {e}")
|
||||
else:
|
||||
print(f"文件夹 {folder_path} 不存在。")
|
||||
# 按行拼接所有插值后的DataFrame
|
||||
final_df = pd.concat(dfs, axis=0, ignore_index=True)
|
||||
# 获取 DataFrame 的长度
|
||||
num_rows = len(final_df)
|
||||
# 生成一个每3000个数从0到2999的序列并重复
|
||||
altitude_values = np.tile(np.arange(3000), num_rows // 3000 + 1)[:num_rows]
|
||||
# 将生成的值赋给 DataFrame 的 'Altitude' 列
|
||||
final_df['Altitude'] = altitude_values
|
||||
# 摄氏度换算开尔文
|
||||
final_df['Temperature'] = final_df['Temperature'] + 273.15
|
||||
return final_df
|
||||
|
||||
|
||||
# 模块2: 数据筛选与网格划分
|
||||
def filter_and_grid_data(final_df):
|
||||
"""
|
||||
对输入的数据框进行纬度筛选以及经度网格划分等操作
|
||||
|
||||
参数:
|
||||
final_df (pd.DataFrame): 预处理后的完整数据框
|
||||
|
||||
返回:
|
||||
latitude_filtered_df (pd.DataFrame): 经过纬度筛选和经度网格划分后的数据框
|
||||
altitude_temperature_mean (pd.DataFrame): 按高度分组求平均温度后的数据框
|
||||
"""
|
||||
# 筛选出纬度在30到40度之间的数据
|
||||
latitude_filtered_df = final_df[(
|
||||
final_df['Latitude'] >= 30) & (final_df['Latitude'] <= 40)]
|
||||
# 划分经度网格,20°的网格
|
||||
lon_min, lon_max = latitude_filtered_df['Longitude'].min(
|
||||
), latitude_filtered_df['Longitude'].max()
|
||||
lon_bins = np.arange(lon_min, lon_max + 20, 20) # 创建经度网格边界
|
||||
# 将数据分配到网格中
|
||||
latitude_filtered_df['Longitude_Grid'] = np.digitize(
|
||||
latitude_filtered_df['Longitude'], lon_bins) - 1
|
||||
# 对相同高度的温度取均值,忽略NaN
|
||||
altitude_temperature_mean = latitude_filtered_df.groupby(
|
||||
'Altitude')['Temperature'].mean().reset_index()
|
||||
# 重命名列,使其更具可读性
|
||||
altitude_temperature_mean.columns = ['Altitude', 'Mean_Temperature']
|
||||
return latitude_filtered_df, altitude_temperature_mean
|
||||
|
||||
|
||||
# 模块3: 计算wn0和初始扰动相关操作
|
||||
def calculate_wn0_and_perturbation(latitude_filtered_df, altitude_temperature_mean):
|
||||
"""
|
||||
计算wn0以及相关的温度差值等操作
|
||||
|
||||
参数:
|
||||
latitude_filtered_df (pd.DataFrame): 经过纬度筛选和经度网格划分后的数据框
|
||||
altitude_temperature_mean (pd.DataFrame): 按高度分组求平均温度后的数据框
|
||||
|
||||
返回:
|
||||
merged_df (pd.DataFrame): 合并相关数据后的结果数据框,包含温度差值等列
|
||||
"""
|
||||
altitude_range = range(0, 3000)
|
||||
all_heights_mean_temperature = [] # 用于存储所有高度下的温度均值结果
|
||||
for altitude in altitude_range:
|
||||
# 筛选出当前高度的所有数据
|
||||
altitude_df = latitude_filtered_df[latitude_filtered_df['Altitude'] == altitude]
|
||||
# 对Longitude_Grid同一区间的温度取均值
|
||||
temperature_mean_by_grid = altitude_df.groupby(
|
||||
'Longitude_Grid')['Temperature'].mean().reset_index()
|
||||
# 重命名列,使其更具可读性
|
||||
temperature_mean_by_grid.columns = [
|
||||
'Longitude_Grid', 'Mean_Temperature']
|
||||
# 添加高度信息列,方便后续区分不同高度的结果
|
||||
temperature_mean_by_grid['Altitude'] = altitude
|
||||
# 将当前高度的结果添加到列表中
|
||||
all_heights_mean_temperature.append(temperature_mean_by_grid)
|
||||
# 将所有高度的结果合并为一个DataFrame
|
||||
combined_mean_temperature_df = pd.concat(
|
||||
all_heights_mean_temperature, ignore_index=True)
|
||||
# 基于Altitude列合并两个DataFrame,只保留能匹配上的行
|
||||
merged_df = pd.merge(combined_mean_temperature_df,
|
||||
altitude_temperature_mean, on='Altitude', how='inner')
|
||||
# 计算差值(减去wn0的扰动)
|
||||
merged_df['Temperature_Difference'] = merged_df['Mean_Temperature_x'] - \
|
||||
merged_df['Mean_Temperature_y']
|
||||
return merged_df
|
||||
|
||||
|
||||
# 模块4: 高度相同下不同区间网格数据的波拟合和滤波处理
|
||||
def wave_fitting_and_filtering(merged_df):
|
||||
"""
|
||||
对合并后的数据框按高度进行波拟合以及滤波处理
|
||||
|
||||
参数:
|
||||
merged_df (pd.DataFrame): 合并相关数据后的结果数据框,包含温度差值等列
|
||||
|
||||
返回:
|
||||
fit_results (dict): 存储每个高度的最佳拟合参数的字典
|
||||
residuals (dict): 存储每个高度的残差值的字典
|
||||
background_temperatures (dict): 存储每个高度的背景温度的字典
|
||||
"""
|
||||
def single_harmonic(x, A, phi):
|
||||
return A * np.sin(2 * np.pi / (18 / k) * x + phi)
|
||||
|
||||
fit_results = {}
|
||||
fitted_curves = {}
|
||||
residuals = {}
|
||||
background_temperatures = {}
|
||||
grouped = merged_df.groupby('Altitude')
|
||||
for altitude, group in grouped:
|
||||
y_data = group['Temperature_Difference'].values
|
||||
x_data = np.arange(len(y_data))
|
||||
wn0_data = group['Mean_Temperature_y'].values # 获取同一高度下的wn0数据
|
||||
# 检查Temperature_Difference列是否全部为NaN
|
||||
if np.all(np.isnan(y_data)):
|
||||
fit_results[altitude] = {'A': [np.nan] * 5, 'phi': [np.nan] * 5}
|
||||
fitted_curves[altitude] = [np.nan * x_data] * 5
|
||||
residuals[altitude] = np.nan * x_data
|
||||
background_temperatures[altitude] = np.nan * \
|
||||
x_data # 对应高度全部为NaN时,背景温度也设为NaN
|
||||
else:
|
||||
# 替换NaN值为非NaN值的均值
|
||||
y_data = np.where(np.isnan(y_data), np.nanmean(y_data), y_data)
|
||||
# 初始化存储WN参数和曲线的列表
|
||||
wn_params = []
|
||||
wn_curves = []
|
||||
# 计算wn0(使用Mean_Temperature_y列数据)
|
||||
wn0 = wn0_data
|
||||
# 对WN1至WN5进行拟合
|
||||
for k in range(1, 6):
|
||||
# 更新单谐波函数中的k值
|
||||
def harmonic_func(x, A, phi): return single_harmonic(x, A, phi)
|
||||
# 使用curve_fit进行拟合
|
||||
popt, pcov = curve_fit(harmonic_func, x_data, y_data, p0=[
|
||||
np.nanmax(y_data) - np.nanmin(y_data), 0])
|
||||
A_fit, phi_fit = popt
|
||||
# 存储拟合结果
|
||||
wn_params.append({'A': A_fit, 'phi': phi_fit})
|
||||
# 使用拟合参数生成拟合曲线
|
||||
WN = harmonic_func(x_data, A_fit, phi_fit)
|
||||
wn_curves.append(WN)
|
||||
# 计算残差值
|
||||
y_data = y_data - WN # 使用残差值作为下一次拟合的y_data
|
||||
# 存储结果
|
||||
fit_results[altitude] = wn_params
|
||||
fitted_curves[altitude] = wn_curves
|
||||
residuals[altitude] = y_data # 最终残差值
|
||||
# 计算同一高度下的背景温度(wn0 + wn1 + wn2 + wn3 + wn4 + wn5)
|
||||
wn_sum = np.sum([wn0] + wn_curves, axis=0)
|
||||
background_temperatures[altitude] = wn_sum
|
||||
return fit_results, residuals, background_temperatures
|
||||
|
||||
|
||||
# 模块5: 带通滤波处理
|
||||
def bandpass_filtering(residuals):
|
||||
"""
|
||||
对残差数据进行带通滤波处理
|
||||
|
||||
参数:
|
||||
residuals (dict): 存储每个高度的残差值的字典
|
||||
|
||||
返回:
|
||||
result (dict): 存储滤波后每个高度结果的字典
|
||||
"""
|
||||
df = pd.DataFrame(residuals)
|
||||
# 使用前向填充(用上一个有效值填充 NaN)
|
||||
df.ffill(axis=1, inplace=True)
|
||||
result = {}
|
||||
lambda_low = 2 # 2 km
|
||||
lambda_high = 15 # 15 km
|
||||
f_low = 2 * np.pi / lambda_high
|
||||
f_high = 2 * np.pi / lambda_low
|
||||
for idx, residuals_array in df.iterrows():
|
||||
# 提取有效值
|
||||
valid_values = np.ma.masked_array(
|
||||
residuals_array, np.isnan(residuals_array))
|
||||
compressed_values = valid_values.compressed() # 去除NaN值后的数组
|
||||
N = len(compressed_values) # 有效值的数量
|
||||
# 如果有效值为空(即所有值都是NaN),则将结果设置为NaN
|
||||
if N == 0:
|
||||
result[idx] = np.full_like(residuals_array, np.nan)
|
||||
else:
|
||||
# 时间序列和频率
|
||||
dt = 0.02 # 假设的时间间隔
|
||||
n = np.arange(N)
|
||||
f = n / (N * dt)
|
||||
# 傅里叶变换
|
||||
y = np.fft.fft(compressed_values) # 使用去除NaN后的数组进行FFT
|
||||
# 频率滤波
|
||||
yy = y.copy()
|
||||
freq_filter = (f >= f_low) & (f <= f_high) # 保留指定频段
|
||||
yy[~freq_filter] = 0 # 过滤掉指定频段外的值
|
||||
# 逆傅里叶变换
|
||||
perturbation_after = np.real(np.fft.ifft(yy))
|
||||
# 将处理结果插回到result字典中
|
||||
result[idx] = perturbation_after
|
||||
return result
|
||||
|
||||
|
||||
# 模块6: 计算势能相关操作
|
||||
def calculate_potential_energy(background_temperatures, result, heights):
|
||||
"""
|
||||
基于背景温度、滤波后的扰动以及高度数据计算浮力频率和势能,并求平均
|
||||
|
||||
参数:
|
||||
background_temperatures (dict): 存储每个高度的背景温度的字典
|
||||
result (dict): 存储滤波后每个高度结果的字典
|
||||
heights (np.ndarray): 高度数据数组
|
||||
|
||||
返回:
|
||||
mean_ktemp_Nz (np.ndarray): 平均浮力频率数据
|
||||
mean_ktemp_Ptz (np.ndarray): 平均势能数据
|
||||
"""
|
||||
# 处理背景温度和扰动温度数据格式
|
||||
heights1 = list(background_temperatures.keys())
|
||||
data_length1 = len(next(iter(background_temperatures.values())))
|
||||
background_matrix = np.zeros((data_length1, len(heights1)))
|
||||
for idx, height in enumerate(heights1):
|
||||
background_matrix[:, idx] = background_temperatures[height]
|
||||
heights2 = list(result.keys())
|
||||
data_length2 = len(next(iter(result.values())))
|
||||
perturbation_matrix = np.zeros((data_length2, len(heights2)))
|
||||
for idx, height in enumerate(heights2):
|
||||
perturbation_matrix[:, idx] = result[height]
|
||||
perturbation_matrix = perturbation_matrix.T
|
||||
|
||||
def brunt_vaisala_frequency(g, BT_z, c_p, heights):
|
||||
# 计算位温随高度的变化率
|
||||
dBT_z_dz = np.gradient(BT_z, heights)
|
||||
# 计算 Brunt-Väisälä 频率,根号内取绝对值
|
||||
frequency_squared = (g / BT_z) * ((g / c_p) + dBT_z_dz)
|
||||
frequency = np.sqrt(np.abs(frequency_squared))
|
||||
return frequency
|
||||
|
||||
def calculate_gravitational_potential_energy(g, BT_z, N_z, PT_z):
|
||||
# 计算势能
|
||||
return 0.5 * ((g / N_z) ** 2) * ((PT_z / BT_z) ** 2)
|
||||
|
||||
g = 9.81 # 重力加速度
|
||||
c_p = 1004.5 # 比热容
|
||||
N_z_matrix = []
|
||||
PT_z_matrix = []
|
||||
for i in range(background_matrix.shape[0]):
|
||||
BT_z = np.array(background_matrix[i])
|
||||
PT_z = np.array(perturbation_matrix[i]) # 滤波后的扰动
|
||||
|
||||
# 调用Brunt-Väisälä频率函数
|
||||
N_z = brunt_vaisala_frequency(g, BT_z, c_p, heights)
|
||||
|
||||
# 调用势能函数
|
||||
PW = calculate_gravitational_potential_energy(g, BT_z, N_z, PT_z)
|
||||
|
||||
# 将结果添加到矩阵中
|
||||
N_z_matrix.append(N_z)
|
||||
PT_z_matrix.append(PW)
|
||||
ktemp_Nz = np.vstack(N_z_matrix)
|
||||
ktemp_Ptz = np.vstack(PT_z_matrix)
|
||||
mean_ktemp_Nz = np.mean(ktemp_Nz, axis=0)
|
||||
mean_ktemp_Ptz = np.mean(ktemp_Ptz, axis=0)
|
||||
# 对mean_ktemp_Ptz中的值做处理,如果超过40,除以10
|
||||
mean_ktemp_Ptz[mean_ktemp_Ptz > 40] /= 10
|
||||
return mean_ktemp_Nz, mean_ktemp_Ptz
|
||||
# 模块7 绘制重力波势能和浮力频率日图
|
||||
|
||||
|
||||
def plot_results(mean_ktemp_Nz, mean_ktemp_Ptz, heights):
|
||||
"""
|
||||
绘制平均浮力频率和平均势能随高度变化的图像
|
||||
|
||||
参数:
|
||||
mean_ktemp_Nz (np.ndarray): 平均浮力频率数据
|
||||
mean_ktemp_Ptz (np.ndarray): 平均势能数据
|
||||
heights (np.ndarray): 高度数据数组
|
||||
"""
|
||||
# 绘制平均浮力频率(mean_ktemp_Nz)随高度变化的图像
|
||||
plt.figure(figsize=(10, 6))
|
||||
plt.plot(mean_ktemp_Nz, heights / 1000) # 高度单位换算为km,方便展示
|
||||
plt.xlabel('Average (N_z)')
|
||||
plt.ylabel('H(km)')
|
||||
# plt.gca().invert_yaxis() # 使高度坐标轴从上到下递增,符合常规习惯
|
||||
plt.show()
|
||||
|
||||
# 绘制平均势能(mean_ktemp_Ptz)随高度变化的图像
|
||||
plt.figure(figsize=(10, 6))
|
||||
plt.plot(mean_ktemp_Ptz, heights / 1000) # 高度单位换算为km,方便展示
|
||||
plt.xlabel('Average (PT_z)')
|
||||
plt.ylabel('H (km)')
|
||||
# plt.gca().invert_yaxis() # 使高度坐标轴从上到下递增,符合常规习惯
|
||||
plt.show()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
base_folder_path = r"./cosmic/data/2018"
|
||||
day_num = 1
|
||||
# 模块1调用
|
||||
final_df = read_and_preprocess_data(base_folder_path, day_num)
|
||||
# 模块2调用
|
||||
latitude_filtered_df, altitude_temperature_mean = filter_and_grid_data(
|
||||
final_df)
|
||||
# 模块3调用
|
||||
merged_df = calculate_wn0_and_perturbation(
|
||||
latitude_filtered_df, altitude_temperature_mean)
|
||||
# 模块4调用
|
||||
fit_results, residuals, background_temperatures = wave_fitting_and_filtering(
|
||||
merged_df)
|
||||
# 模块5调用
|
||||
result = bandpass_filtering(residuals)
|
||||
# 创建从1到80km的高度列表(按照代码中原本逻辑转换为对应单位的数组)
|
||||
heights = np.linspace(0, 60000, 3000)
|
||||
# 模块6调用,计算势能相关数据并获取平均浮力频率和平均势能
|
||||
mean_ktemp_Nz, mean_ktemp_Ptz = calculate_potential_energy(
|
||||
background_temperatures, result, heights)
|
||||
print("平均浮力频率(mean_ktemp_Nz):", mean_ktemp_Nz)
|
||||
print("平均势能(mean_ktemp_Ptz):", mean_ktemp_Ptz)
|
||||
# 调用绘图模块函数进行绘图
|
||||
plot_results(mean_ktemp_Nz, mean_ktemp_Ptz, heights)
|
||||
11
file_stuff.py
Normal file
11
file_stuff.py
Normal file
@ -0,0 +1,11 @@
|
||||
import pandas as pd
|
||||
|
||||
|
||||
all_year_data = pd.read_parquet("./cache/ballon_data_lin.parquet")
|
||||
|
||||
|
||||
def get_dataframe_between_year(start_year, end_year):
|
||||
res = all_year_data
|
||||
filtered_res = res[(res['file_name'].str.extract(r'LIN-(\d{4})')[0].astype(int) >= start_year) &
|
||||
(res['file_name'].str.extract(r'LIN-(\d{4})')[0].astype(int) <= end_year)]
|
||||
return filtered_res
|
||||
506
main.py
Normal file
506
main.py
Normal file
@ -0,0 +1,506 @@
|
||||
from PySide6.QtWidgets import QApplication, QMainWindow, QTableWidget, QTableWidgetItem, QHeaderView
|
||||
from PySide6.QtGui import QPixmap
|
||||
from PySide6.QtCore import Qt
|
||||
from qt import Ui_MainWindow
|
||||
from qt_material import apply_stylesheet
|
||||
import pandas as pd
|
||||
import sys
|
||||
import os
|
||||
import re
|
||||
import balloon
|
||||
|
||||
|
||||
lat = 52.21
|
||||
g = 9.76
|
||||
|
||||
tag_balloon_once = [
|
||||
[
|
||||
"地形重力波",
|
||||
"垂直传播方向",
|
||||
"水平传播方向",
|
||||
"本征(固有)频率",
|
||||
"本征周期",
|
||||
"本征频率与固有频率的比值(即长短轴之比)",
|
||||
],
|
||||
[
|
||||
"垂直波长",
|
||||
"水平波长",
|
||||
"纬向本征相速度",
|
||||
"经向本征相速度",
|
||||
"垂直本征相速度",
|
||||
"波动能",
|
||||
],
|
||||
[
|
||||
"势能",
|
||||
"纬向动量通量",
|
||||
"经向动量通量",
|
||||
"纬向风扰动振幅",
|
||||
"经向风扰动振幅",
|
||||
"温度扰动振幅",
|
||||
],
|
||||
]
|
||||
|
||||
|
||||
def set_table_balloon_once(table: QTableWidget, data: list):
|
||||
for i in range(3):
|
||||
for j in range(6):
|
||||
table.setItem(i, j * 2, QTableWidgetItem(tag_balloon_once[i][j]))
|
||||
if data[0] == True:
|
||||
data[0] = "是"
|
||||
else:
|
||||
data[0] = "否"
|
||||
if data[1] == 1:
|
||||
data[1] = "上"
|
||||
else:
|
||||
data[1] = "下"
|
||||
for i in range(2, len(data)):
|
||||
data[i] = f"{data[i]} "
|
||||
data[2] += "°"
|
||||
data[3] += "rad/s"
|
||||
data[4] += "h"
|
||||
data[6] += "km"
|
||||
data[7] += "km"
|
||||
for i in [8, 9, 10, 15, 16]:
|
||||
data[i] += "m/s"
|
||||
data[11] += "J/kg"
|
||||
data[12] += "J/kg"
|
||||
data[13] += "mPa"
|
||||
data[14] += "mPa"
|
||||
data[17] += "K"
|
||||
for i in range(18):
|
||||
table.setItem(i // 6, (i % 6) * 2 + 1, QTableWidgetItem(data[i]))
|
||||
|
||||
|
||||
class MainWindow(QMainWindow, Ui_MainWindow):
|
||||
comboType = [
|
||||
"探空气球",
|
||||
"流星雷达",
|
||||
"Saber",
|
||||
"TIDI",
|
||||
"COSMIC",
|
||||
]
|
||||
|
||||
comboMode = [
|
||||
["重力波单次", "重力波统计"],
|
||||
["重力波月统计", "潮汐波单次", "潮汐波月统计"],
|
||||
["行星波月统计", "重力波单次", "重力波月统计"],
|
||||
["行星波月统计"],
|
||||
["行星波月统计"],
|
||||
]
|
||||
|
||||
comboDate = [
|
||||
[["年", "时间"], ["起始年", "终止年"]],
|
||||
[["年", "月"], ["年", "日期"], ["年", "月"]],
|
||||
[["起始月", "-"], ["月", "日"], ["月", "-"]],
|
||||
[["起始月", "-"]],
|
||||
[["起始月", "-"]],
|
||||
]
|
||||
|
||||
comboMap = [
|
||||
[1, 2],
|
||||
[3, 4, 5],
|
||||
[6, 7, 8],
|
||||
[9],
|
||||
[10],
|
||||
]
|
||||
|
||||
def get_date0_items(self):
|
||||
match self.dataType:
|
||||
case 0:
|
||||
try:
|
||||
self.currentDate0 = os.listdir(
|
||||
f"./data/{self.comboType[self.dataType]}/{self.dataStation}")
|
||||
except FileNotFoundError:
|
||||
return []
|
||||
return [i[-4:] for i in self.currentDate0]
|
||||
case 1:
|
||||
self.currentDate0 = ["2024"]
|
||||
return self.currentDate0
|
||||
case 2:
|
||||
try:
|
||||
self.currentDate0 = os.listdir(
|
||||
f"./data/{self.comboType[self.dataType]}/{self.dataStation}")
|
||||
except FileNotFoundError:
|
||||
return []
|
||||
return [re.search(r"_([A-z]+)\d{4}_", i).group(1) for i in self.currentDate0]
|
||||
case _:
|
||||
self.currentDate0 = ["Feb"]
|
||||
return self.currentDate0
|
||||
|
||||
def get_date1_items(self):
|
||||
if self.dataType == 0 and self.dataMode == 0:
|
||||
try:
|
||||
self.currentDate1 = os.listdir(
|
||||
f"./data/{self.comboType[self.dataType]}/{self.dataStation}/{self.currentDate0[self.dataDate0]}")
|
||||
except FileNotFoundError:
|
||||
return []
|
||||
return [re.search(r"_(\d{8}T\d{6}_\d+)", i).group(1) for i in self.currentDate1]
|
||||
elif self.dataType == 0 and self.dataMode == 1:
|
||||
return [i[-4:] for i in [end for j, end in enumerate(self.currentDate0) if j >= self.dataDate0]]
|
||||
elif self.dataType == 1 and self.dataMode == 0:
|
||||
self.currentDate1 = ["01"]
|
||||
return self.currentDate1
|
||||
elif self.dataType == 1 and self.dataMode == 1:
|
||||
self.currentDate1 = ["20240317T000000"]
|
||||
return self.currentDate1
|
||||
elif self.dataType == 1 and self.dataMode == 2:
|
||||
if self.dataStation == "黑龙江漠河":
|
||||
self.currentDate1 = ["01"]
|
||||
else:
|
||||
self.currentDate1 = ["03"]
|
||||
return self.currentDate1
|
||||
elif self.dataType == 2 and self.dataMode == 1:
|
||||
return [f"{i:02d}" for i in range(1, 31)]
|
||||
else:
|
||||
self.currentDate1 = [f"{i:02d}" for i in range(1, 13)]
|
||||
return self.currentDate1
|
||||
|
||||
def __init__(self):
|
||||
super(MainWindow, self).__init__()
|
||||
self.setupUi(self)
|
||||
|
||||
self.stackedWidget.setCurrentIndex(0)
|
||||
self.pixmap_logo = QPixmap("./data/logo.png")
|
||||
|
||||
self.dataType = -1
|
||||
self.dataMode = -1
|
||||
self.currentDate0 = []
|
||||
self.currentDate1 = []
|
||||
|
||||
self.funcMap = [
|
||||
[self.on_balloon_once_change, self.on_balloon_year_change],
|
||||
[self.on_meteor_g_change, self.on_meteor_once_change,
|
||||
self.on_meteor_month_change],
|
||||
[self.on_Saber_twice_change, self.on_Saber_g_once_change,
|
||||
self.on_Saber_g_month_change],
|
||||
[self.on_TIDI_twice_change],
|
||||
[self.on_COSMIC_twice_change],
|
||||
]
|
||||
|
||||
self.clear_combo(0)
|
||||
self.combo_type.addItems(self.comboType)
|
||||
|
||||
self.combo_type.currentTextChanged.connect(self.on_change_type)
|
||||
self.combo_mode.currentTextChanged.connect(self.on_change_mode)
|
||||
self.combo_station.currentTextChanged.connect(self.on_change_station)
|
||||
self.combo_date0.currentTextChanged.connect(self.on_change_date0)
|
||||
self.combo_date1.currentTextChanged.connect(self.on_change_date1)
|
||||
|
||||
s = self.size()
|
||||
self.resize(s.width() + 1, s.height() + 1)
|
||||
self.resize(s)
|
||||
|
||||
def resizeEvent(self, event):
|
||||
self.on_resize()
|
||||
super().resizeEvent(event)
|
||||
|
||||
def on_resize(self):
|
||||
tmp = [
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
self.label_meteor_g,
|
||||
self.label_meteor_once,
|
||||
self.label_meteor_month,
|
||||
self.label_Saber_twice,
|
||||
self.label_Saber_g_once,
|
||||
self.label_Saber_g_month,
|
||||
self.label_TIDI_twice,
|
||||
self.label_COSMIC_twice,
|
||||
]
|
||||
match self.stackedWidget.currentIndex():
|
||||
case 0:
|
||||
l = self.label_idle_logo
|
||||
p = self.pixmap_logo
|
||||
l.setPixmap(p.scaled(l.size(), Qt.KeepAspectRatio,
|
||||
Qt.SmoothTransformation))
|
||||
case 1:
|
||||
l = self.label_balloon_once
|
||||
p = self.pixmap_balloon_once
|
||||
if p.isNull():
|
||||
self.clear_pixmap()
|
||||
l.setText("无重力波")
|
||||
else:
|
||||
l.setPixmap(
|
||||
p.scaled(l.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation))
|
||||
case 2:
|
||||
l = self.label_balloon_year
|
||||
p = self.pixmap_balloon_year
|
||||
if p.isNull():
|
||||
self.clear_pixmap()
|
||||
l.setText("无数据")
|
||||
else:
|
||||
l.setPixmap(
|
||||
p.scaled(l.size(), Qt.KeepAspectRatio, Qt.SmoothTransformation))
|
||||
case 5:
|
||||
i = self.stackedWidget.currentIndex()
|
||||
l = tmp[i]
|
||||
|
||||
if self.dataStation == "黑龙江漠河":
|
||||
p = QPixmap("./output/tmp_5_01.png")
|
||||
else:
|
||||
p = QPixmap("./output/tmp_5_03.png")
|
||||
|
||||
l.setPixmap(p.scaled(l.size(), Qt.KeepAspectRatio,
|
||||
Qt.SmoothTransformation))
|
||||
l.setCursor(Qt.CursorShape.PointingHandCursor)
|
||||
case _:
|
||||
i = self.stackedWidget.currentIndex()
|
||||
l = tmp[i]
|
||||
|
||||
p = QPixmap(f"./output/tmp_{i}.png")
|
||||
l.setPixmap(p.scaled(l.size(), Qt.KeepAspectRatio,
|
||||
Qt.SmoothTransformation))
|
||||
l.setCursor(Qt.CursorShape.PointingHandCursor)
|
||||
|
||||
def clear_pixmap(self):
|
||||
self.pixmap_balloon_once = QPixmap()
|
||||
self.pixmap_balloon_year = QPixmap()
|
||||
self.pixmap_meteor_g = QPixmap()
|
||||
self.pixmap_meteor_once = QPixmap()
|
||||
self.pixmap_meteor_month = QPixmap()
|
||||
self.pixmap_Saber_twice = QPixmap()
|
||||
self.pixmap_Saber_g_once = QPixmap()
|
||||
self.pixmap_Saber_g_month = QPixmap()
|
||||
self.pixmap_TIDI_twice = QPixmap()
|
||||
self.pixmap_COSMIC_twice = QPixmap()
|
||||
|
||||
self.table_balloon_once.setVisible(False)
|
||||
|
||||
for l in [
|
||||
self.label_balloon_once,
|
||||
self.label_balloon_year,
|
||||
self.label_meteor_g,
|
||||
self.label_meteor_once,
|
||||
self.label_meteor_month,
|
||||
self.label_Saber_twice,
|
||||
self.label_Saber_g_once,
|
||||
self.label_Saber_g_month,
|
||||
self.label_TIDI_twice,
|
||||
self.label_COSMIC_twice,
|
||||
]:
|
||||
l.setCursor(Qt.CursorShape.ArrowCursor)
|
||||
|
||||
def clear_combo(self, level: int):
|
||||
self.clear_pixmap()
|
||||
|
||||
self.on_resize()
|
||||
self.stackedWidget.setCurrentIndex(0)
|
||||
|
||||
for i, combo in enumerate(
|
||||
[
|
||||
self.combo_type,
|
||||
self.combo_mode,
|
||||
self.combo_station,
|
||||
self.combo_date0,
|
||||
self.combo_date1,
|
||||
]
|
||||
):
|
||||
if i >= level:
|
||||
combo.clear()
|
||||
combo.setEnabled(i <= level)
|
||||
|
||||
def on_change_type(self):
|
||||
if self.combo_type.currentIndex() < 0:
|
||||
return
|
||||
self.clear_combo(1)
|
||||
self.dataType = self.combo_type.currentIndex()
|
||||
self.combo_mode.addItems(self.comboMode[self.dataType])
|
||||
|
||||
self.combo_date0.setPlaceholderText("-")
|
||||
self.combo_date1.setPlaceholderText("-")
|
||||
|
||||
def on_change_mode(self):
|
||||
if self.combo_mode.currentIndex() < 0:
|
||||
return
|
||||
self.clear_combo(2)
|
||||
self.dataMode = self.combo_mode.currentIndex()
|
||||
|
||||
if self.dataType < 2:
|
||||
self.combo_station.setPlaceholderText("台站")
|
||||
else:
|
||||
self.combo_station.setPlaceholderText("年")
|
||||
list_station = []
|
||||
try:
|
||||
# list_station = [i for i in os.listdir(f"./data/{self.comboType[self.dataType]}") if i.encode("utf-8").isupper()]
|
||||
list_station = [i for i in os.listdir(
|
||||
f"./data/{self.comboType[self.dataType]}")]
|
||||
except FileNotFoundError:
|
||||
return
|
||||
self.combo_station.addItems(list_station)
|
||||
|
||||
date0, date1 = self.comboDate[self.dataType][self.dataMode]
|
||||
self.combo_date0.setPlaceholderText(date0)
|
||||
self.combo_date1.setPlaceholderText(date1)
|
||||
|
||||
def on_change_station(self):
|
||||
if self.combo_station.currentIndex() < 0:
|
||||
return
|
||||
self.clear_combo(3)
|
||||
self.dataStation = self.combo_station.currentText()
|
||||
self.combo_date0.addItems(self.get_date0_items())
|
||||
|
||||
def on_change_date0(self):
|
||||
if self.combo_date0.currentIndex() < 0:
|
||||
return
|
||||
self.clear_combo(4)
|
||||
self.dataDate0 = self.combo_date0.currentIndex()
|
||||
if self.dataType < 2 or (self.dataType == 2 and self.dataMode == 1):
|
||||
self.combo_date1.addItems(self.get_date1_items())
|
||||
return
|
||||
self.combo_date1.setEnabled(False)
|
||||
self.stackedWidget.setCurrentIndex(
|
||||
self.comboMap[self.dataType][self.dataMode])
|
||||
if f := self.funcMap[self.dataType][self.dataMode]:
|
||||
f()
|
||||
|
||||
def on_change_date1(self):
|
||||
if self.combo_date1.currentIndex() < 0:
|
||||
return
|
||||
self.dataDate1 = self.combo_date1.currentIndex()
|
||||
self.stackedWidget.setCurrentIndex(
|
||||
self.comboMap[self.dataType][self.dataMode])
|
||||
if f := self.funcMap[self.dataType][self.dataMode]:
|
||||
f()
|
||||
|
||||
def on_balloon_once_change(self):
|
||||
path = os.path.join("./data/探空气球", self.dataStation,
|
||||
self.currentDate0[self.dataDate0], self.currentDate1[self.dataDate1])
|
||||
|
||||
try:
|
||||
data = balloon.read_data(path)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return
|
||||
|
||||
path = "./output" + path[6:-2] + "png"
|
||||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||
result = balloon.plot_once(data, path, lat, g)
|
||||
|
||||
if len(result) == 0:
|
||||
self.pixmap_balloon_once = QPixmap()
|
||||
self.on_resize()
|
||||
return
|
||||
|
||||
self.pixmap_balloon_once = QPixmap(path)
|
||||
self.label_balloon_once.setCursor(Qt.CursorShape.PointingHandCursor)
|
||||
|
||||
t = self.table_balloon_once
|
||||
t.clear()
|
||||
t.setRowCount(3)
|
||||
t.setColumnCount(12)
|
||||
t.setVisible(True)
|
||||
t.verticalHeader().setHidden(True)
|
||||
t.horizontalHeader().setHidden(True)
|
||||
t.verticalScrollBar().setHidden(True)
|
||||
t.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
|
||||
set_table_balloon_once(t, result)
|
||||
t.setMaximumHeight(t.rowHeight(0) * 3)
|
||||
|
||||
s = self.size()
|
||||
self.resize(s.width() + 1, s.height() + 1)
|
||||
self.resize(s)
|
||||
|
||||
def on_balloon_year_change(self):
|
||||
columns = [
|
||||
"file_name",
|
||||
"c",
|
||||
"a",
|
||||
"b",
|
||||
"omega_upper",
|
||||
"w_f",
|
||||
"ver_wave_len",
|
||||
"hori_wave_len",
|
||||
"c_x",
|
||||
"c_y",
|
||||
"c_z",
|
||||
"Ek",
|
||||
"E_p",
|
||||
"MFu",
|
||||
"MFv",
|
||||
"u1",
|
||||
"v1",
|
||||
"T1",
|
||||
"zhou_qi",
|
||||
]
|
||||
combined_df = pd.DataFrame()
|
||||
for i in range(self.dataDate1 + 1):
|
||||
path = f"./output/探空气球/{self.dataStation}/{self.currentDate0[self.dataDate0 + i]}.csv"
|
||||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||||
if os.path.exists(path):
|
||||
year_df = pd.read_csv(path)
|
||||
combined_df = pd.concat([combined_df, year_df])
|
||||
continue
|
||||
folder = "./data" + path[8:-4]
|
||||
year_df = pd.DataFrame()
|
||||
for file in os.listdir(folder):
|
||||
print(file)
|
||||
data = balloon.read_data(os.path.join(folder, file))
|
||||
try:
|
||||
wave = balloon.extract_wave(data, lat, g)
|
||||
except Exception:
|
||||
wave = []
|
||||
if len(wave) == 0:
|
||||
continue
|
||||
c = balloon.is_terrain_wave(data, lat, g)
|
||||
wave.insert(0, c)
|
||||
wave.insert(0, file)
|
||||
line = pd.DataFrame([wave], columns=columns)
|
||||
year_df = pd.concat([year_df, line])
|
||||
combined_df = pd.concat([combined_df, year_df])
|
||||
year_df.to_csv(path, index=False)
|
||||
path = f"./output/探空气球/{self.dataStation}/{self.currentDate0[self.dataDate0]}_{self.dataDate1}.png"
|
||||
if not balloon.plot_year(combined_df, path, lat, g):
|
||||
self.pixmap_balloon_year = QPixmap()
|
||||
self.on_resize()
|
||||
return
|
||||
|
||||
self.pixmap_balloon_year = QPixmap(path)
|
||||
self.label_balloon_year.setCursor(Qt.CursorShape.PointingHandCursor)
|
||||
|
||||
s = self.size()
|
||||
self.resize(s.width() + 1, s.height() + 1)
|
||||
self.resize(s)
|
||||
|
||||
def on_meteor_g_change(self):
|
||||
self.on_resize()
|
||||
|
||||
def on_meteor_once_change(self):
|
||||
self.on_resize()
|
||||
|
||||
def on_meteor_month_change(self):
|
||||
self.on_resize()
|
||||
|
||||
def on_Saber_twice_change(self):
|
||||
self.spin_Saber_twice_lat.setValue(0)
|
||||
self.spin_Saber_twice_alt.setValue(90)
|
||||
self.on_resize()
|
||||
|
||||
def on_Saber_g_once_change(self):
|
||||
self.spin_Saber_g_once_lat.setValue(0)
|
||||
self.spin_Saber_g_once_lon.setValue(0)
|
||||
self.spin_Saber_g_once_alt.setValue(90)
|
||||
self.on_resize()
|
||||
|
||||
def on_Saber_g_month_change(self):
|
||||
self.spin_Saber_g_month_lat.setValue(0)
|
||||
self.spin_Saber_g_month_alt.setValue(90)
|
||||
self.on_resize()
|
||||
|
||||
def on_TIDI_twice_change(self):
|
||||
self.spin_TIDI_twice_lat.setValue(0)
|
||||
self.spin_TIDI_twice_alt.setValue(90)
|
||||
self.on_resize()
|
||||
|
||||
def on_COSMIC_twice_change(self):
|
||||
self.spin_COSMIC_twice_lat.setValue(0)
|
||||
self.spin_COSMIC_twice_alt.setValue(40)
|
||||
self.on_resize()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = QApplication(sys.argv)
|
||||
window = MainWindow()
|
||||
apply_stylesheet(app, theme="light_blue.xml", invert_secondary=True)
|
||||
window.show()
|
||||
sys.exit(app.exec())
|
||||
616
qt.py
Normal file
616
qt.py
Normal file
@ -0,0 +1,616 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
################################################################################
|
||||
# Form generated from reading UI file 'qt.ui'
|
||||
##
|
||||
# Created by: Qt User Interface Compiler version 6.8.0
|
||||
##
|
||||
# WARNING! All changes made in this file will be lost when recompiling UI file!
|
||||
################################################################################
|
||||
|
||||
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
|
||||
QMetaObject, QObject, QPoint, QRect,
|
||||
QSize, QTime, QUrl, Qt)
|
||||
from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
|
||||
QFont, QFontDatabase, QGradient, QIcon,
|
||||
QImage, QKeySequence, QLinearGradient, QPainter,
|
||||
QPalette, QPixmap, QRadialGradient, QTransform)
|
||||
from PySide6.QtWidgets import (QApplication, QComboBox, QHBoxLayout, QHeaderView,
|
||||
QLabel, QMainWindow, QPushButton, QSizePolicy,
|
||||
QSpinBox, QStackedWidget, QStatusBar, QTableWidget,
|
||||
QTableWidgetItem, QVBoxLayout, QWidget)
|
||||
|
||||
|
||||
class Ui_MainWindow(object):
|
||||
def setupUi(self, MainWindow):
|
||||
if not MainWindow.objectName():
|
||||
MainWindow.setObjectName(u"MainWindow")
|
||||
MainWindow.resize(800, 600)
|
||||
MainWindow.setMinimumSize(QSize(800, 600))
|
||||
self.centralwidget = QWidget(MainWindow)
|
||||
self.centralwidget.setObjectName(u"centralwidget")
|
||||
self.verticalLayout = QVBoxLayout(self.centralwidget)
|
||||
self.verticalLayout.setObjectName(u"verticalLayout")
|
||||
self.horizontalLayout = QHBoxLayout()
|
||||
self.horizontalLayout.setObjectName(u"horizontalLayout")
|
||||
self.combo_type = QComboBox(self.centralwidget)
|
||||
self.combo_type.setObjectName(u"combo_type")
|
||||
sizePolicy = QSizePolicy(
|
||||
QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Maximum)
|
||||
sizePolicy.setHorizontalStretch(0)
|
||||
sizePolicy.setVerticalStretch(0)
|
||||
sizePolicy.setHeightForWidth(
|
||||
self.combo_type.sizePolicy().hasHeightForWidth())
|
||||
self.combo_type.setSizePolicy(sizePolicy)
|
||||
|
||||
self.horizontalLayout.addWidget(self.combo_type)
|
||||
|
||||
self.combo_mode = QComboBox(self.centralwidget)
|
||||
self.combo_mode.setObjectName(u"combo_mode")
|
||||
sizePolicy.setHeightForWidth(
|
||||
self.combo_mode.sizePolicy().hasHeightForWidth())
|
||||
self.combo_mode.setSizePolicy(sizePolicy)
|
||||
|
||||
self.horizontalLayout.addWidget(self.combo_mode)
|
||||
|
||||
self.combo_station = QComboBox(self.centralwidget)
|
||||
self.combo_station.setObjectName(u"combo_station")
|
||||
sizePolicy.setHeightForWidth(
|
||||
self.combo_station.sizePolicy().hasHeightForWidth())
|
||||
self.combo_station.setSizePolicy(sizePolicy)
|
||||
|
||||
self.horizontalLayout.addWidget(self.combo_station)
|
||||
|
||||
self.combo_date0 = QComboBox(self.centralwidget)
|
||||
self.combo_date0.setObjectName(u"combo_date0")
|
||||
sizePolicy.setHeightForWidth(
|
||||
self.combo_date0.sizePolicy().hasHeightForWidth())
|
||||
self.combo_date0.setSizePolicy(sizePolicy)
|
||||
|
||||
self.horizontalLayout.addWidget(self.combo_date0)
|
||||
|
||||
self.combo_date1 = QComboBox(self.centralwidget)
|
||||
self.combo_date1.setObjectName(u"combo_date1")
|
||||
sizePolicy.setHeightForWidth(
|
||||
self.combo_date1.sizePolicy().hasHeightForWidth())
|
||||
self.combo_date1.setSizePolicy(sizePolicy)
|
||||
|
||||
self.horizontalLayout.addWidget(self.combo_date1)
|
||||
|
||||
self.verticalLayout.addLayout(self.horizontalLayout)
|
||||
|
||||
self.stackedWidget = QStackedWidget(self.centralwidget)
|
||||
self.stackedWidget.setObjectName(u"stackedWidget")
|
||||
self.page_idle = QWidget()
|
||||
self.page_idle.setObjectName(u"page_idle")
|
||||
self.verticalLayout_0 = QVBoxLayout(self.page_idle)
|
||||
self.verticalLayout_0.setObjectName(u"verticalLayout_0")
|
||||
self.label_idle_logo = QLabel(self.page_idle)
|
||||
self.label_idle_logo.setObjectName(u"label_idle_logo")
|
||||
sizePolicy1 = QSizePolicy(
|
||||
QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding)
|
||||
sizePolicy1.setHorizontalStretch(0)
|
||||
sizePolicy1.setVerticalStretch(0)
|
||||
sizePolicy1.setHeightForWidth(
|
||||
self.label_idle_logo.sizePolicy().hasHeightForWidth())
|
||||
self.label_idle_logo.setSizePolicy(sizePolicy1)
|
||||
font = QFont()
|
||||
font.setPointSize(40)
|
||||
self.label_idle_logo.setFont(font)
|
||||
self.label_idle_logo.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
|
||||
self.verticalLayout_0.addWidget(self.label_idle_logo)
|
||||
|
||||
self.stackedWidget.addWidget(self.page_idle)
|
||||
self.page_balloon_once = QWidget()
|
||||
self.page_balloon_once.setObjectName(u"page_balloon_once")
|
||||
self.verticalLayout_1 = QVBoxLayout(self.page_balloon_once)
|
||||
self.verticalLayout_1.setObjectName(u"verticalLayout_1")
|
||||
self.table_balloon_once = QTableWidget(self.page_balloon_once)
|
||||
self.table_balloon_once.setObjectName(u"table_balloon_once")
|
||||
sizePolicy2 = QSizePolicy(
|
||||
QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Minimum)
|
||||
sizePolicy2.setHorizontalStretch(0)
|
||||
sizePolicy2.setVerticalStretch(0)
|
||||
sizePolicy2.setHeightForWidth(
|
||||
self.table_balloon_once.sizePolicy().hasHeightForWidth())
|
||||
self.table_balloon_once.setSizePolicy(sizePolicy2)
|
||||
|
||||
self.verticalLayout_1.addWidget(self.table_balloon_once)
|
||||
|
||||
self.label_balloon_once = QLabel(self.page_balloon_once)
|
||||
self.label_balloon_once.setObjectName(u"label_balloon_once")
|
||||
sizePolicy1.setHeightForWidth(
|
||||
self.label_balloon_once.sizePolicy().hasHeightForWidth())
|
||||
self.label_balloon_once.setSizePolicy(sizePolicy1)
|
||||
self.label_balloon_once.setMinimumSize(QSize(0, 0))
|
||||
self.label_balloon_once.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
|
||||
self.verticalLayout_1.addWidget(self.label_balloon_once)
|
||||
|
||||
self.stackedWidget.addWidget(self.page_balloon_once)
|
||||
self.page_balloon_year = QWidget()
|
||||
self.page_balloon_year.setObjectName(u"page_balloon_year")
|
||||
self.verticalLayout_2 = QVBoxLayout(self.page_balloon_year)
|
||||
self.verticalLayout_2.setObjectName(u"verticalLayout_2")
|
||||
self.label_balloon_year = QLabel(self.page_balloon_year)
|
||||
self.label_balloon_year.setObjectName(u"label_balloon_year")
|
||||
sizePolicy1.setHeightForWidth(
|
||||
self.label_balloon_year.sizePolicy().hasHeightForWidth())
|
||||
self.label_balloon_year.setSizePolicy(sizePolicy1)
|
||||
self.label_balloon_year.setMinimumSize(QSize(0, 0))
|
||||
self.label_balloon_year.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
|
||||
self.verticalLayout_2.addWidget(self.label_balloon_year)
|
||||
|
||||
self.stackedWidget.addWidget(self.page_balloon_year)
|
||||
self.page_meteor_g = QWidget()
|
||||
self.page_meteor_g.setObjectName(u"page_meteor_g")
|
||||
self.verticalLayout_3 = QVBoxLayout(self.page_meteor_g)
|
||||
self.verticalLayout_3.setObjectName(u"verticalLayout_3")
|
||||
self.label_meteor_g = QLabel(self.page_meteor_g)
|
||||
self.label_meteor_g.setObjectName(u"label_meteor_g")
|
||||
sizePolicy1.setHeightForWidth(
|
||||
self.label_meteor_g.sizePolicy().hasHeightForWidth())
|
||||
self.label_meteor_g.setSizePolicy(sizePolicy1)
|
||||
self.label_meteor_g.setMinimumSize(QSize(0, 0))
|
||||
self.label_meteor_g.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
|
||||
self.verticalLayout_3.addWidget(self.label_meteor_g)
|
||||
|
||||
self.stackedWidget.addWidget(self.page_meteor_g)
|
||||
self.page_meteor_once = QWidget()
|
||||
self.page_meteor_once.setObjectName(u"page_meteor_once")
|
||||
self.verticalLayout_4 = QVBoxLayout(self.page_meteor_once)
|
||||
self.verticalLayout_4.setObjectName(u"verticalLayout_4")
|
||||
self.label_meteor_once = QLabel(self.page_meteor_once)
|
||||
self.label_meteor_once.setObjectName(u"label_meteor_once")
|
||||
sizePolicy1.setHeightForWidth(
|
||||
self.label_meteor_once.sizePolicy().hasHeightForWidth())
|
||||
self.label_meteor_once.setSizePolicy(sizePolicy1)
|
||||
self.label_meteor_once.setMinimumSize(QSize(0, 0))
|
||||
self.label_meteor_once.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
|
||||
self.verticalLayout_4.addWidget(self.label_meteor_once)
|
||||
|
||||
self.stackedWidget.addWidget(self.page_meteor_once)
|
||||
self.page_meteor_month = QWidget()
|
||||
self.page_meteor_month.setObjectName(u"page_meteor_month")
|
||||
self.verticalLayout_5 = QVBoxLayout(self.page_meteor_month)
|
||||
self.verticalLayout_5.setObjectName(u"verticalLayout_5")
|
||||
self.label_meteor_month = QLabel(self.page_meteor_month)
|
||||
self.label_meteor_month.setObjectName(u"label_meteor_month")
|
||||
sizePolicy1.setHeightForWidth(
|
||||
self.label_meteor_month.sizePolicy().hasHeightForWidth())
|
||||
self.label_meteor_month.setSizePolicy(sizePolicy1)
|
||||
self.label_meteor_month.setMinimumSize(QSize(0, 0))
|
||||
self.label_meteor_month.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
|
||||
self.verticalLayout_5.addWidget(self.label_meteor_month)
|
||||
|
||||
self.stackedWidget.addWidget(self.page_meteor_month)
|
||||
self.page_Saber_twice = QWidget()
|
||||
self.page_Saber_twice.setObjectName(u"page_Saber_twice")
|
||||
self.verticalLayout_6 = QVBoxLayout(self.page_Saber_twice)
|
||||
self.verticalLayout_6.setObjectName(u"verticalLayout_6")
|
||||
self.horizontalLayout_6 = QHBoxLayout()
|
||||
self.horizontalLayout_6.setObjectName(u"horizontalLayout_6")
|
||||
self.label_Saber_twice_lat = QLabel(self.page_Saber_twice)
|
||||
self.label_Saber_twice_lat.setObjectName(u"label_Saber_twice_lat")
|
||||
sizePolicy3 = QSizePolicy(
|
||||
QSizePolicy.Policy.Maximum, QSizePolicy.Policy.Preferred)
|
||||
sizePolicy3.setHorizontalStretch(0)
|
||||
sizePolicy3.setVerticalStretch(0)
|
||||
sizePolicy3.setHeightForWidth(
|
||||
self.label_Saber_twice_lat.sizePolicy().hasHeightForWidth())
|
||||
self.label_Saber_twice_lat.setSizePolicy(sizePolicy3)
|
||||
|
||||
self.horizontalLayout_6.addWidget(self.label_Saber_twice_lat)
|
||||
|
||||
self.spin_Saber_twice_lat = QSpinBox(self.page_Saber_twice)
|
||||
self.spin_Saber_twice_lat.setObjectName(u"spin_Saber_twice_lat")
|
||||
self.spin_Saber_twice_lat.setMaximum(80)
|
||||
self.spin_Saber_twice_lat.setMinimum(-80)
|
||||
self.spin_Saber_twice_lat.setSingleStep(5)
|
||||
|
||||
self.horizontalLayout_6.addWidget(self.spin_Saber_twice_lat)
|
||||
|
||||
self.label_Saber_twice_alt = QLabel(self.page_Saber_twice)
|
||||
self.label_Saber_twice_alt.setObjectName(u"label_Saber_twice_alt")
|
||||
sizePolicy3.setHeightForWidth(
|
||||
self.label_Saber_twice_alt.sizePolicy().hasHeightForWidth())
|
||||
self.label_Saber_twice_alt.setSizePolicy(sizePolicy3)
|
||||
|
||||
self.horizontalLayout_6.addWidget(self.label_Saber_twice_alt)
|
||||
|
||||
self.spin_Saber_twice_alt = QSpinBox(self.page_Saber_twice)
|
||||
self.spin_Saber_twice_alt.setObjectName(u"spin_Saber_twice_alt")
|
||||
self.spin_Saber_twice_alt.setMaximum(100)
|
||||
self.spin_Saber_twice_alt.setMinimum(25)
|
||||
self.spin_Saber_twice_alt.setSingleStep(5)
|
||||
|
||||
self.horizontalLayout_6.addWidget(self.spin_Saber_twice_alt)
|
||||
|
||||
self.button_Saber_twice = QPushButton(self.page_Saber_twice)
|
||||
self.button_Saber_twice.setObjectName(u"button_Saber_twice")
|
||||
sizePolicy4 = QSizePolicy(
|
||||
QSizePolicy.Policy.Maximum, QSizePolicy.Policy.Fixed)
|
||||
sizePolicy4.setHorizontalStretch(0)
|
||||
sizePolicy4.setVerticalStretch(0)
|
||||
sizePolicy4.setHeightForWidth(
|
||||
self.button_Saber_twice.sizePolicy().hasHeightForWidth())
|
||||
self.button_Saber_twice.setSizePolicy(sizePolicy4)
|
||||
|
||||
self.horizontalLayout_6.addWidget(self.button_Saber_twice)
|
||||
|
||||
self.verticalLayout_6.addLayout(self.horizontalLayout_6)
|
||||
|
||||
self.label_Saber_twice = QLabel(self.page_Saber_twice)
|
||||
self.label_Saber_twice.setObjectName(u"label_Saber_twice")
|
||||
sizePolicy1.setHeightForWidth(
|
||||
self.label_Saber_twice.sizePolicy().hasHeightForWidth())
|
||||
self.label_Saber_twice.setSizePolicy(sizePolicy1)
|
||||
self.label_Saber_twice.setMinimumSize(QSize(0, 0))
|
||||
self.label_Saber_twice.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
|
||||
self.verticalLayout_6.addWidget(self.label_Saber_twice)
|
||||
|
||||
self.stackedWidget.addWidget(self.page_Saber_twice)
|
||||
self.page_Saber_g_once = QWidget()
|
||||
self.page_Saber_g_once.setObjectName(u"page_Saber_g_once")
|
||||
self.verticalLayout_7 = QVBoxLayout(self.page_Saber_g_once)
|
||||
self.verticalLayout_7.setObjectName(u"verticalLayout_7")
|
||||
self.horizontalLayout_7 = QHBoxLayout()
|
||||
self.horizontalLayout_7.setObjectName(u"horizontalLayout_7")
|
||||
self.label_Saber_g_once_lat = QLabel(self.page_Saber_g_once)
|
||||
self.label_Saber_g_once_lat.setObjectName(u"label_Saber_g_once_lat")
|
||||
sizePolicy3.setHeightForWidth(
|
||||
self.label_Saber_g_once_lat.sizePolicy().hasHeightForWidth())
|
||||
self.label_Saber_g_once_lat.setSizePolicy(sizePolicy3)
|
||||
|
||||
self.horizontalLayout_7.addWidget(self.label_Saber_g_once_lat)
|
||||
|
||||
self.spin_Saber_g_once_lat = QSpinBox(self.page_Saber_g_once)
|
||||
self.spin_Saber_g_once_lat.setObjectName(u"spin_Saber_g_once_lat")
|
||||
self.spin_Saber_g_once_lat.setMaximum(80)
|
||||
self.spin_Saber_g_once_lat.setMinimum(-80)
|
||||
self.spin_Saber_g_once_lat.setSingleStep(5)
|
||||
|
||||
self.horizontalLayout_7.addWidget(self.spin_Saber_g_once_lat)
|
||||
|
||||
self.label_Saber_g_once_lon = QLabel(self.page_Saber_g_once)
|
||||
self.label_Saber_g_once_lon.setObjectName(u"label_Saber_g_once_lon")
|
||||
sizePolicy3.setHeightForWidth(
|
||||
self.label_Saber_g_once_lon.sizePolicy().hasHeightForWidth())
|
||||
self.label_Saber_g_once_lon.setSizePolicy(sizePolicy3)
|
||||
|
||||
self.horizontalLayout_7.addWidget(self.label_Saber_g_once_lon)
|
||||
|
||||
self.spin_Saber_g_once_lon = QSpinBox(self.page_Saber_g_once)
|
||||
self.spin_Saber_g_once_lon.setObjectName(u"spin_Saber_g_once_lon")
|
||||
self.spin_Saber_g_once_lon.setMaximum(180)
|
||||
self.spin_Saber_g_once_lon.setMinimum(-180)
|
||||
self.spin_Saber_g_once_lon.setSingleStep(25)
|
||||
|
||||
self.horizontalLayout_7.addWidget(self.spin_Saber_g_once_lon)
|
||||
|
||||
self.label_Saber_g_once_alt = QLabel(self.page_Saber_g_once)
|
||||
self.label_Saber_g_once_alt.setObjectName(u"label_Saber_g_once_alt")
|
||||
sizePolicy3.setHeightForWidth(
|
||||
self.label_Saber_g_once_alt.sizePolicy().hasHeightForWidth())
|
||||
self.label_Saber_g_once_alt.setSizePolicy(sizePolicy3)
|
||||
|
||||
self.horizontalLayout_7.addWidget(self.label_Saber_g_once_alt)
|
||||
|
||||
self.spin_Saber_g_once_alt = QSpinBox(self.page_Saber_g_once)
|
||||
self.spin_Saber_g_once_alt.setObjectName(u"spin_Saber_g_once_alt")
|
||||
self.spin_Saber_g_once_alt.setMaximum(100)
|
||||
self.spin_Saber_g_once_alt.setMinimum(25)
|
||||
self.spin_Saber_g_once_alt.setSingleStep(5)
|
||||
|
||||
self.horizontalLayout_7.addWidget(self.spin_Saber_g_once_alt)
|
||||
|
||||
self.button_Saber_g_once = QPushButton(self.page_Saber_g_once)
|
||||
self.button_Saber_g_once.setObjectName(u"button_Saber_g_once")
|
||||
sizePolicy4.setHeightForWidth(
|
||||
self.button_Saber_g_once.sizePolicy().hasHeightForWidth())
|
||||
self.button_Saber_g_once.setSizePolicy(sizePolicy4)
|
||||
|
||||
self.horizontalLayout_7.addWidget(self.button_Saber_g_once)
|
||||
|
||||
self.verticalLayout_7.addLayout(self.horizontalLayout_7)
|
||||
|
||||
self.label_Saber_g_once = QLabel(self.page_Saber_g_once)
|
||||
self.label_Saber_g_once.setObjectName(u"label_Saber_g_once")
|
||||
sizePolicy1.setHeightForWidth(
|
||||
self.label_Saber_g_once.sizePolicy().hasHeightForWidth())
|
||||
self.label_Saber_g_once.setSizePolicy(sizePolicy1)
|
||||
self.label_Saber_g_once.setMinimumSize(QSize(0, 0))
|
||||
self.label_Saber_g_once.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
|
||||
self.verticalLayout_7.addWidget(self.label_Saber_g_once)
|
||||
|
||||
self.stackedWidget.addWidget(self.page_Saber_g_once)
|
||||
self.page_Saber_g_month = QWidget()
|
||||
self.page_Saber_g_month.setObjectName(u"page_Saber_g_month")
|
||||
self.verticalLayout_8 = QVBoxLayout(self.page_Saber_g_month)
|
||||
self.verticalLayout_8.setObjectName(u"verticalLayout_8")
|
||||
self.horizontalLayout_8 = QHBoxLayout()
|
||||
self.horizontalLayout_8.setObjectName(u"horizontalLayout_8")
|
||||
self.label_Saber_g_month_lat = QLabel(self.page_Saber_g_month)
|
||||
self.label_Saber_g_month_lat.setObjectName(u"label_Saber_g_month_lat")
|
||||
sizePolicy3.setHeightForWidth(
|
||||
self.label_Saber_g_month_lat.sizePolicy().hasHeightForWidth())
|
||||
self.label_Saber_g_month_lat.setSizePolicy(sizePolicy3)
|
||||
|
||||
self.horizontalLayout_8.addWidget(self.label_Saber_g_month_lat)
|
||||
|
||||
self.spin_Saber_g_month_lat = QSpinBox(self.page_Saber_g_month)
|
||||
self.spin_Saber_g_month_lat.setObjectName(u"spin_Saber_g_month_lat")
|
||||
self.spin_Saber_g_month_lat.setMaximum(80)
|
||||
self.spin_Saber_g_month_lat.setMinimum(-80)
|
||||
self.spin_Saber_g_month_lat.setSingleStep(5)
|
||||
|
||||
self.horizontalLayout_8.addWidget(self.spin_Saber_g_month_lat)
|
||||
|
||||
self.label_Saber_g_month_alt = QLabel(self.page_Saber_g_month)
|
||||
self.label_Saber_g_month_alt.setObjectName(u"label_Saber_g_month_alt")
|
||||
sizePolicy3.setHeightForWidth(
|
||||
self.label_Saber_g_month_alt.sizePolicy().hasHeightForWidth())
|
||||
self.label_Saber_g_month_alt.setSizePolicy(sizePolicy3)
|
||||
|
||||
self.horizontalLayout_8.addWidget(self.label_Saber_g_month_alt)
|
||||
|
||||
self.spin_Saber_g_month_alt = QSpinBox(self.page_Saber_g_month)
|
||||
self.spin_Saber_g_month_alt.setObjectName(u"spin_Saber_g_month_alt")
|
||||
self.spin_Saber_g_month_alt.setMaximum(100)
|
||||
self.spin_Saber_g_month_alt.setMinimum(25)
|
||||
self.spin_Saber_g_month_alt.setSingleStep(5)
|
||||
|
||||
self.horizontalLayout_8.addWidget(self.spin_Saber_g_month_alt)
|
||||
|
||||
self.button_Saber_g_month = QPushButton(self.page_Saber_g_month)
|
||||
self.button_Saber_g_month.setObjectName(u"button_Saber_g_month")
|
||||
sizePolicy4.setHeightForWidth(
|
||||
self.button_Saber_g_month.sizePolicy().hasHeightForWidth())
|
||||
self.button_Saber_g_month.setSizePolicy(sizePolicy4)
|
||||
|
||||
self.horizontalLayout_8.addWidget(self.button_Saber_g_month)
|
||||
|
||||
self.verticalLayout_8.addLayout(self.horizontalLayout_8)
|
||||
|
||||
self.label_Saber_g_month = QLabel(self.page_Saber_g_month)
|
||||
self.label_Saber_g_month.setObjectName(u"label_Saber_g_month")
|
||||
sizePolicy1.setHeightForWidth(
|
||||
self.label_Saber_g_month.sizePolicy().hasHeightForWidth())
|
||||
self.label_Saber_g_month.setSizePolicy(sizePolicy1)
|
||||
self.label_Saber_g_month.setMinimumSize(QSize(0, 0))
|
||||
self.label_Saber_g_month.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
|
||||
self.verticalLayout_8.addWidget(self.label_Saber_g_month)
|
||||
|
||||
self.stackedWidget.addWidget(self.page_Saber_g_month)
|
||||
self.page_TIDI_twice = QWidget()
|
||||
self.page_TIDI_twice.setObjectName(u"page_TIDI_twice")
|
||||
self.verticalLayout_9 = QVBoxLayout(self.page_TIDI_twice)
|
||||
self.verticalLayout_9.setObjectName(u"verticalLayout_9")
|
||||
self.horizontalLayout_9 = QHBoxLayout()
|
||||
self.horizontalLayout_9.setObjectName(u"horizontalLayout_9")
|
||||
self.label_TIDI_twice_lat = QLabel(self.page_TIDI_twice)
|
||||
self.label_TIDI_twice_lat.setObjectName(u"label_TIDI_twice_lat")
|
||||
sizePolicy3.setHeightForWidth(
|
||||
self.label_TIDI_twice_lat.sizePolicy().hasHeightForWidth())
|
||||
self.label_TIDI_twice_lat.setSizePolicy(sizePolicy3)
|
||||
|
||||
self.horizontalLayout_9.addWidget(self.label_TIDI_twice_lat)
|
||||
|
||||
self.spin_TIDI_twice_lat = QSpinBox(self.page_TIDI_twice)
|
||||
self.spin_TIDI_twice_lat.setObjectName(u"spin_TIDI_twice_lat")
|
||||
self.spin_TIDI_twice_lat.setMaximum(80)
|
||||
self.spin_TIDI_twice_lat.setMinimum(-80)
|
||||
self.spin_TIDI_twice_lat.setSingleStep(5)
|
||||
|
||||
self.horizontalLayout_9.addWidget(self.spin_TIDI_twice_lat)
|
||||
|
||||
self.label_TIDI_twice_alt = QLabel(self.page_TIDI_twice)
|
||||
self.label_TIDI_twice_alt.setObjectName(u"label_TIDI_twice_alt")
|
||||
sizePolicy3.setHeightForWidth(
|
||||
self.label_TIDI_twice_alt.sizePolicy().hasHeightForWidth())
|
||||
self.label_TIDI_twice_alt.setSizePolicy(sizePolicy3)
|
||||
|
||||
self.horizontalLayout_9.addWidget(self.label_TIDI_twice_alt)
|
||||
|
||||
self.spin_TIDI_twice_alt = QSpinBox(self.page_TIDI_twice)
|
||||
self.spin_TIDI_twice_alt.setObjectName(u"spin_TIDI_twice_alt")
|
||||
self.spin_TIDI_twice_alt.setMaximum(100)
|
||||
self.spin_TIDI_twice_alt.setMinimum(25)
|
||||
self.spin_TIDI_twice_alt.setSingleStep(5)
|
||||
|
||||
self.horizontalLayout_9.addWidget(self.spin_TIDI_twice_alt)
|
||||
|
||||
self.button_TIDI_twice = QPushButton(self.page_TIDI_twice)
|
||||
self.button_TIDI_twice.setObjectName(u"button_TIDI_twice")
|
||||
sizePolicy4.setHeightForWidth(
|
||||
self.button_TIDI_twice.sizePolicy().hasHeightForWidth())
|
||||
self.button_TIDI_twice.setSizePolicy(sizePolicy4)
|
||||
|
||||
self.horizontalLayout_9.addWidget(self.button_TIDI_twice)
|
||||
|
||||
self.verticalLayout_9.addLayout(self.horizontalLayout_9)
|
||||
|
||||
self.label_TIDI_twice = QLabel(self.page_TIDI_twice)
|
||||
self.label_TIDI_twice.setObjectName(u"label_TIDI_twice")
|
||||
sizePolicy1.setHeightForWidth(
|
||||
self.label_TIDI_twice.sizePolicy().hasHeightForWidth())
|
||||
self.label_TIDI_twice.setSizePolicy(sizePolicy1)
|
||||
self.label_TIDI_twice.setMinimumSize(QSize(0, 0))
|
||||
self.label_TIDI_twice.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
|
||||
self.verticalLayout_9.addWidget(self.label_TIDI_twice)
|
||||
|
||||
self.stackedWidget.addWidget(self.page_TIDI_twice)
|
||||
self.page_COSMIC_twice = QWidget()
|
||||
self.page_COSMIC_twice.setObjectName(u"page_COSMIC_twice")
|
||||
self.verticalLayout_10 = QVBoxLayout(self.page_COSMIC_twice)
|
||||
self.verticalLayout_10.setObjectName(u"verticalLayout_10")
|
||||
self.horizontalLayout_10 = QHBoxLayout()
|
||||
self.horizontalLayout_10.setObjectName(u"horizontalLayout_10")
|
||||
self.label_COSMIC_twice_lat = QLabel(self.page_COSMIC_twice)
|
||||
self.label_COSMIC_twice_lat.setObjectName(u"label_COSMIC_twice_lat")
|
||||
sizePolicy3.setHeightForWidth(
|
||||
self.label_COSMIC_twice_lat.sizePolicy().hasHeightForWidth())
|
||||
self.label_COSMIC_twice_lat.setSizePolicy(sizePolicy3)
|
||||
|
||||
self.horizontalLayout_10.addWidget(self.label_COSMIC_twice_lat)
|
||||
|
||||
self.spin_COSMIC_twice_lat = QSpinBox(self.page_COSMIC_twice)
|
||||
self.spin_COSMIC_twice_lat.setObjectName(u"spin_COSMIC_twice_lat")
|
||||
self.spin_COSMIC_twice_lat.setMaximum(60)
|
||||
self.spin_COSMIC_twice_lat.setMinimum(-60)
|
||||
self.spin_COSMIC_twice_lat.setSingleStep(5)
|
||||
|
||||
self.horizontalLayout_10.addWidget(self.spin_COSMIC_twice_lat)
|
||||
|
||||
self.label_COSMIC_twice_alt = QLabel(self.page_COSMIC_twice)
|
||||
self.label_COSMIC_twice_alt.setObjectName(u"label_COSMIC_twice_alt")
|
||||
sizePolicy3.setHeightForWidth(
|
||||
self.label_COSMIC_twice_alt.sizePolicy().hasHeightForWidth())
|
||||
self.label_COSMIC_twice_alt.setSizePolicy(sizePolicy3)
|
||||
|
||||
self.horizontalLayout_10.addWidget(self.label_COSMIC_twice_alt)
|
||||
|
||||
self.spin_COSMIC_twice_alt = QSpinBox(self.page_COSMIC_twice)
|
||||
self.spin_COSMIC_twice_alt.setObjectName(u"spin_COSMIC_twice_alt")
|
||||
self.spin_COSMIC_twice_alt.setMaximum(100)
|
||||
self.spin_COSMIC_twice_alt.setMinimum(75)
|
||||
self.spin_COSMIC_twice_alt.setSingleStep(5)
|
||||
|
||||
self.horizontalLayout_10.addWidget(self.spin_COSMIC_twice_alt)
|
||||
|
||||
self.button_COSMIC_twice = QPushButton(self.page_COSMIC_twice)
|
||||
self.button_COSMIC_twice.setObjectName(u"button_COSMIC_twice")
|
||||
sizePolicy4.setHeightForWidth(
|
||||
self.button_COSMIC_twice.sizePolicy().hasHeightForWidth())
|
||||
self.button_COSMIC_twice.setSizePolicy(sizePolicy4)
|
||||
|
||||
self.horizontalLayout_10.addWidget(self.button_COSMIC_twice)
|
||||
|
||||
self.verticalLayout_10.addLayout(self.horizontalLayout_10)
|
||||
|
||||
self.label_COSMIC_twice = QLabel(self.page_COSMIC_twice)
|
||||
self.label_COSMIC_twice.setObjectName(u"label_COSMIC_twice")
|
||||
sizePolicy1.setHeightForWidth(
|
||||
self.label_COSMIC_twice.sizePolicy().hasHeightForWidth())
|
||||
self.label_COSMIC_twice.setSizePolicy(sizePolicy1)
|
||||
self.label_COSMIC_twice.setMinimumSize(QSize(0, 0))
|
||||
self.label_COSMIC_twice.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
||||
|
||||
self.verticalLayout_10.addWidget(self.label_COSMIC_twice)
|
||||
|
||||
self.stackedWidget.addWidget(self.page_COSMIC_twice)
|
||||
|
||||
self.verticalLayout.addWidget(self.stackedWidget)
|
||||
|
||||
MainWindow.setCentralWidget(self.centralwidget)
|
||||
self.statusbar = QStatusBar(MainWindow)
|
||||
self.statusbar.setObjectName(u"statusbar")
|
||||
MainWindow.setStatusBar(self.statusbar)
|
||||
|
||||
self.retranslateUi(MainWindow)
|
||||
|
||||
self.stackedWidget.setCurrentIndex(6)
|
||||
|
||||
QMetaObject.connectSlotsByName(MainWindow)
|
||||
# setupUi
|
||||
|
||||
def retranslateUi(self, MainWindow):
|
||||
MainWindow.setWindowTitle(QCoreApplication.translate(
|
||||
"MainWindow", u"\u4e2d\u9ad8\u5c42\u5927\u6c14\u6ce2\u52a8\u89e3\u6790\u8bc6\u522b\u6280\u672f\u7cfb\u7edf", None))
|
||||
self.combo_type.setCurrentText("")
|
||||
self.combo_type.setPlaceholderText(QCoreApplication.translate(
|
||||
"MainWindow", u"\u89c2\u6d4b\u8bbe\u5907", None))
|
||||
self.combo_mode.setPlaceholderText(QCoreApplication.translate(
|
||||
"MainWindow", u"\u5904\u7406\u6a21\u5f0f", None))
|
||||
self.combo_station.setPlaceholderText(
|
||||
QCoreApplication.translate("MainWindow", u"\u53f0\u7ad9", None))
|
||||
self.combo_date0.setPlaceholderText(
|
||||
QCoreApplication.translate("MainWindow", u"-", None))
|
||||
self.combo_date1.setPlaceholderText(
|
||||
QCoreApplication.translate("MainWindow", u"-", None))
|
||||
self.label_idle_logo.setText(
|
||||
QCoreApplication.translate("MainWindow", u"logo", None))
|
||||
self.label_balloon_once.setText(QCoreApplication.translate(
|
||||
"MainWindow", u"\u65e0\u6570\u636e", None))
|
||||
self.label_balloon_year.setText(QCoreApplication.translate(
|
||||
"MainWindow", u"\u65e0\u6570\u636e", None))
|
||||
self.label_meteor_g.setText(QCoreApplication.translate(
|
||||
"MainWindow", u"\u65e0\u6570\u636e", None))
|
||||
self.label_meteor_once.setText(QCoreApplication.translate(
|
||||
"MainWindow", u"\u65e0\u6570\u636e", None))
|
||||
self.label_meteor_month.setText(QCoreApplication.translate(
|
||||
"MainWindow", u"\u65e0\u6570\u636e", None))
|
||||
self.label_Saber_twice_lat.setText(QCoreApplication.translate(
|
||||
"MainWindow", u"\u7eac\u5ea6\uff1a", None))
|
||||
self.spin_Saber_twice_lat.setSuffix(
|
||||
QCoreApplication.translate("MainWindow", u" \u00b0", None))
|
||||
self.label_Saber_twice_alt.setText(QCoreApplication.translate(
|
||||
"MainWindow", u"\u9ad8\u5ea6\uff1a", None))
|
||||
self.spin_Saber_twice_alt.setSuffix(
|
||||
QCoreApplication.translate("MainWindow", u" km", None))
|
||||
self.button_Saber_twice.setText(
|
||||
QCoreApplication.translate("MainWindow", u"\u66f4\u65b0", None))
|
||||
self.label_Saber_twice.setText(QCoreApplication.translate(
|
||||
"MainWindow", u"\u65e0\u6570\u636e", None))
|
||||
self.label_Saber_g_once_lat.setText(QCoreApplication.translate(
|
||||
"MainWindow", u"\u7eac\u5ea6\u5e26\u8d77\u70b9\uff1a", None))
|
||||
self.spin_Saber_g_once_lat.setSuffix(
|
||||
QCoreApplication.translate("MainWindow", u" \u00b0", None))
|
||||
self.label_Saber_g_once_lon.setText(QCoreApplication.translate(
|
||||
"MainWindow", u"\u7ecf\u5ea6\u5e26\u8d77\u70b9\uff1a", None))
|
||||
self.spin_Saber_g_once_lon.setSuffix(
|
||||
QCoreApplication.translate("MainWindow", u" \u00b0", None))
|
||||
self.label_Saber_g_once_alt.setText(QCoreApplication.translate(
|
||||
"MainWindow", u"\u9ad8\u5ea6\uff1a", None))
|
||||
self.spin_Saber_g_once_alt.setSuffix(
|
||||
QCoreApplication.translate("MainWindow", u" km", None))
|
||||
self.button_Saber_g_once.setText(
|
||||
QCoreApplication.translate("MainWindow", u"\u66f4\u65b0", None))
|
||||
self.label_Saber_g_once.setText(QCoreApplication.translate(
|
||||
"MainWindow", u"\u65e0\u6570\u636e", None))
|
||||
self.label_Saber_g_month_lat.setText(
|
||||
QCoreApplication.translate("MainWindow", u"\u7eac\u5ea6\uff1a", None))
|
||||
self.spin_Saber_g_month_lat.setSuffix(
|
||||
QCoreApplication.translate("MainWindow", u" \u00b0", None))
|
||||
self.label_Saber_g_month_alt.setText(
|
||||
QCoreApplication.translate("MainWindow", u"\u9ad8\u5ea6\uff1a", None))
|
||||
self.spin_Saber_g_month_alt.setSuffix(
|
||||
QCoreApplication.translate("MainWindow", u" km", None))
|
||||
self.button_Saber_g_month.setText(
|
||||
QCoreApplication.translate("MainWindow", u"\u66f4\u65b0", None))
|
||||
self.label_Saber_g_month.setText(QCoreApplication.translate(
|
||||
"MainWindow", u"\u65e0\u6570\u636e", None))
|
||||
self.label_TIDI_twice_lat.setText(QCoreApplication.translate(
|
||||
"MainWindow", u"\u7eac\u5ea6\uff1a", None))
|
||||
self.spin_TIDI_twice_lat.setSuffix(
|
||||
QCoreApplication.translate("MainWindow", u" \u00b0", None))
|
||||
self.label_TIDI_twice_alt.setText(QCoreApplication.translate(
|
||||
"MainWindow", u"\u9ad8\u5ea6\uff1a", None))
|
||||
self.spin_TIDI_twice_alt.setSuffix(
|
||||
QCoreApplication.translate("MainWindow", u" km", None))
|
||||
self.button_TIDI_twice.setText(
|
||||
QCoreApplication.translate("MainWindow", u"\u66f4\u65b0", None))
|
||||
self.label_TIDI_twice.setText(QCoreApplication.translate(
|
||||
"MainWindow", u"\u65e0\u6570\u636e", None))
|
||||
self.label_COSMIC_twice_lat.setText(QCoreApplication.translate(
|
||||
"MainWindow", u"\u7eac\u5ea6\uff1a", None))
|
||||
self.spin_COSMIC_twice_lat.setSuffix(
|
||||
QCoreApplication.translate("MainWindow", u" \u00b0", None))
|
||||
self.label_COSMIC_twice_alt.setText(QCoreApplication.translate(
|
||||
"MainWindow", u"\u9ad8\u5ea6\uff1a", None))
|
||||
self.spin_COSMIC_twice_alt.setSuffix(
|
||||
QCoreApplication.translate("MainWindow", u" km", None))
|
||||
self.button_COSMIC_twice.setText(
|
||||
QCoreApplication.translate("MainWindow", u"\u66f4\u65b0", None))
|
||||
self.label_COSMIC_twice.setText(QCoreApplication.translate(
|
||||
"MainWindow", u"\u65e0\u6570\u636e", None))
|
||||
# retranslateUi
|
||||
61
radar/__init__.py
Normal file
61
radar/__init__.py
Normal file
@ -0,0 +1,61 @@
|
||||
import asyncio
|
||||
import glob
|
||||
from flask import Blueprint, request, send_file
|
||||
from radar.plot_original import final_plot_v1, ALL_MODEL_NAMES
|
||||
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)
|
||||
|
||||
|
||||
radar_module = Blueprint("Radar", __name__)
|
||||
|
||||
|
||||
@radar_module.route("/metadata")
|
||||
def get_all_files():
|
||||
return globed_all_files
|
||||
|
||||
|
||||
@radar_module.route("/metadata/models")
|
||||
def get_all_models():
|
||||
return all_model_names
|
||||
|
||||
|
||||
@radar_module.route('/render/v1')
|
||||
def render_v1():
|
||||
"""
|
||||
wind_type: Any,
|
||||
year: Any,
|
||||
H: Any,
|
||||
model_name: Any
|
||||
"""
|
||||
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)
|
||||
return send_file(buf, mimetype='image/png')
|
||||
|
||||
|
||||
@radar_module.route('/render/v2')
|
||||
def render_v2():
|
||||
"""
|
||||
year: Any,
|
||||
station: Any,
|
||||
model_name: Any
|
||||
"""
|
||||
year = request.args.get('year')
|
||||
station = request.args.get('station')
|
||||
model_name = request.args.get('model_name')
|
||||
# async_plot_v2 = asyncio.coroutine(plot_v2)
|
||||
|
||||
buffer = plot_v2(int(year), station, model_name)
|
||||
|
||||
return send_file(buffer, mimetype='image/png')
|
||||
998
radar/plot2.py
Normal file
998
radar/plot2.py
Normal file
@ -0,0 +1,998 @@
|
||||
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))
|
||||
|
||||
|
||||
|
||||
262
radar/plot_original.py
Normal file
262
radar/plot_original.py
Normal file
@ -0,0 +1,262 @@
|
||||
from io import BytesIO
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
import os
|
||||
from scipy.optimize import curve_fit
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
# 解决绘图中中文不能显示的问题
|
||||
import matplotlib
|
||||
# 设置中文显示和负号正常显示
|
||||
matplotlib.rcParams['font.sans-serif'] = ['SimHei'] # 显示中文
|
||||
matplotlib.rcParams['axes.unicode_minus'] = False # 正常显示负号
|
||||
|
||||
# 定义所有模型
|
||||
|
||||
|
||||
def tidal_model(t, v0, a1, b1, a2, b2, a3, b3, a4, b4):
|
||||
"""潮汐波模型"""
|
||||
T1, T2, T3, T4 = 6, 8, 12, 24 # 周期以小时为单位
|
||||
return (v0 + a1 * np.sin((2 * np.pi / T1) * t + b1)
|
||||
+ a2 * np.sin((2 * np.pi / T2) * t + b2)
|
||||
+ a3 * np.sin((2 * np.pi / T3) * t + b3)
|
||||
+ a4 * np.sin((2 * np.pi / T4) * t + b4))
|
||||
|
||||
|
||||
def planetary_model_2day(t, v0, a, b):
|
||||
"""2日行星波模型"""
|
||||
T = 48 # 周期为2天(48小时)
|
||||
return v0 + a * np.sin((2 * np.pi / T) * t + b)
|
||||
|
||||
|
||||
def planetary_model_5day(t, v0, a, b):
|
||||
"""5日行星波模型"""
|
||||
T = 120 # 周期为5天(120小时)
|
||||
return v0 + a * np.sin((2 * np.pi / T) * t + b)
|
||||
|
||||
|
||||
def planetary_model_10day(t, v0, a, b):
|
||||
"""10日行星波模型"""
|
||||
T = 240 # 周期为10天(240小时)
|
||||
return v0 + a * np.sin((2 * np.pi / T) * t + b)
|
||||
|
||||
|
||||
def planetary_model_16day(t, v0, a, b):
|
||||
"""16日行星波模型"""
|
||||
T = 384 # 周期为16天(384小时)
|
||||
return v0 + a * np.sin((2 * np.pi / T) * t + b)
|
||||
|
||||
|
||||
# 统一管理模型及其参数
|
||||
MODELS = {
|
||||
'潮汐波': {
|
||||
'function': tidal_model,
|
||||
'param_names': ['v0', 'a1', 'b1', 'a2', 'b2', 'a3', 'b3', 'a4', 'b4'],
|
||||
'initial_guess': [0, 1, 0, 1, 0, 1, 0, 1, 0],
|
||||
'bounds': ([-np.inf, 0, -np.inf, 0, -np.inf, 0, -np.inf, 0, -np.inf],
|
||||
[np.inf, np.inf, np.inf, np.inf, np.inf, np.inf, np.inf, np.inf, np.inf]),
|
||||
'window': 5 # 窗口为5天
|
||||
},
|
||||
'2日行星波': {
|
||||
'function': planetary_model_2day,
|
||||
'param_names': ['v0', 'a', 'b'],
|
||||
'initial_guess': [0, 1, 0],
|
||||
'bounds': ([-np.inf, 0, -np.inf], [np.inf, np.inf, np.inf]),
|
||||
'window': 5 # 窗口为5天
|
||||
},
|
||||
'5日行星波': {
|
||||
'function': planetary_model_5day,
|
||||
'param_names': ['v0', 'a', 'b'],
|
||||
'initial_guess': [0, 1, 0],
|
||||
'bounds': ([-np.inf, 0, -np.inf], [np.inf, np.inf, np.inf]),
|
||||
'window': 15 # 窗口为15天
|
||||
},
|
||||
'10日行星波': {
|
||||
'function': planetary_model_10day,
|
||||
'param_names': ['v0', 'a', 'b'],
|
||||
'initial_guess': [0, 1, 0],
|
||||
'bounds': ([-np.inf, 0, -np.inf], [np.inf, np.inf, np.inf]),
|
||||
'window': 29 # 窗口为29天
|
||||
},
|
||||
'16日行星波': {
|
||||
'function': planetary_model_16day,
|
||||
'param_names': ['v0', 'a', 'b'],
|
||||
'initial_guess': [0, 1, 0],
|
||||
'bounds': ([-np.inf, 0, -np.inf], [np.inf, np.inf, np.inf]),
|
||||
'window': 49 # 窗口为49天
|
||||
},
|
||||
}
|
||||
|
||||
ALL_MODEL_NAMES = list(MODELS.keys())
|
||||
|
||||
# ----------------------------------------------------------第一大部分————————————————————————————————————————————————————
|
||||
# # 根据纬向风(u)、经向风(v),得到武汉左岭镇站/黑龙江漠河站
|
||||
# 固定年份(2022年)固定高度(90km)大气波动(6、8、12、24小时潮汐波,2日、5日、10日、16日行星波)随时间的变化
|
||||
|
||||
|
||||
# 参数设置,选择高度、时间、站点
|
||||
|
||||
# station = '黑龙江漠河站'
|
||||
def get_df(H, year, station):
|
||||
# 文件路径设置
|
||||
file_dir = rf'./radar/data/{station}\{year}' # 数据文件路径
|
||||
output_dir = rf'./radar/out/{station}\{year}' # 参数txt、图片输出路径
|
||||
os.makedirs(output_dir, exist_ok=True) # 确保输出路径存在
|
||||
|
||||
# 1-提取整个文件夹的数据
|
||||
# 获取所有 txt 文件
|
||||
file_list = [f for f in os.listdir(file_dir) if f.endswith('.txt')]
|
||||
|
||||
# 初始化空的 DataFrame,用于合并所有文件的数据
|
||||
final_df = pd.DataFrame()
|
||||
|
||||
# 批量处理每个 txt 文件
|
||||
for file_name in file_list:
|
||||
file_path = os.path.join(file_dir, file_name) # 拼接文件路径
|
||||
df = pd.read_csv(file_path, sep='\s+') # 读取文件
|
||||
date_part = os.path.splitext(file_name)[0][-8:] # 提取日期部分
|
||||
|
||||
# 处理缺失值,将 1000000 替换为 NaN
|
||||
df.loc[df['uwind'] == 1000000, 'uwind'] = np.nan
|
||||
df.loc[df['vwind'] == 1000000, 'vwind'] = np.nan
|
||||
|
||||
# 筛选出 height 列为 90km 的行
|
||||
filtered_df = df[df['height'] == H]
|
||||
|
||||
# 重命名列,包含日期信息
|
||||
filtered_df = filtered_df.rename(columns={
|
||||
'uwind': f'{date_part}_uwind',
|
||||
'vwind': f'{date_part}_vwind'
|
||||
})
|
||||
|
||||
# 只保留需要的列
|
||||
filtered_df = filtered_df[['height', 'time',
|
||||
f'{date_part}_uwind', f'{date_part}_vwind']]
|
||||
|
||||
# 如果 final_df 为空,则直接赋值
|
||||
if final_df.empty:
|
||||
final_df = filtered_df
|
||||
else:
|
||||
# 按 'height' 和 'time' 列对齐合并
|
||||
final_df = pd.merge(final_df, filtered_df, on=[
|
||||
'height', 'time'], how='outer')
|
||||
|
||||
# 生成完整日期范围的列名,补全缺失的天,使得平年365,闰年366
|
||||
all_dates = pd.date_range(
|
||||
f'{year}-01-01', f'{year}-12-31').strftime('%Y%m%d')
|
||||
expected_columns = [f'{date}_uwind' for date in all_dates] + \
|
||||
[f'{date}_vwind' for date in all_dates]
|
||||
|
||||
# 确保 final_df 包含所有日期的列,缺失列补为 NaN
|
||||
for col in expected_columns:
|
||||
if col not in final_df.columns:
|
||||
final_df[col] = np.nan
|
||||
|
||||
# 按列名排序(确保日期顺序)
|
||||
final_df = final_df[['height', 'time'] + sorted(expected_columns)]
|
||||
return final_df
|
||||
# 此时,final_df已经是90km包含完整天的分析数据---------------------------------------------------------
|
||||
|
||||
|
||||
# 通用函数:处理风速数据
|
||||
def final_plot_v1(wind_type, year, H, model_name, station):
|
||||
|
||||
final_df = get_df(H, year, station)
|
||||
"""
|
||||
处理风速数据并针对多种模型进行拟合和绘图。
|
||||
"""
|
||||
wind_data = final_df[[col for col in final_df.columns if wind_type in col]]
|
||||
wind_data.columns = [col[:8] for col in wind_data.columns]
|
||||
result = wind_data.to_numpy().T.flatten()
|
||||
column_names = np.repeat(wind_data.columns.to_numpy(), 24)
|
||||
combined = pd.DataFrame(np.vstack((column_names, result)))
|
||||
|
||||
dates = combined.iloc[0].values
|
||||
wind_speed = combined.iloc[1].values
|
||||
date_series = pd.to_datetime(dates)
|
||||
data = pd.DataFrame({'date': date_series, 'wind_speed': wind_speed})
|
||||
data.set_index('date', inplace=True)
|
||||
unique_dates = np.unique(data.index.date)
|
||||
|
||||
if model_name not in MODELS:
|
||||
raise ValueError(f"Model {model_name} not found")
|
||||
return
|
||||
|
||||
model_info = MODELS[model_name]
|
||||
|
||||
params = []
|
||||
model_func = model_info['function']
|
||||
param_names = model_info['param_names']
|
||||
initial_guess = model_info['initial_guess']
|
||||
bounds = model_info['bounds']
|
||||
window_days = model_info['window'] # 获取窗口长度
|
||||
|
||||
for date in unique_dates:
|
||||
# 根据模型窗口调整时间范围
|
||||
start_date = pd.to_datetime(
|
||||
date) - pd.Timedelta(days=(window_days - 1) / 2)
|
||||
end_date = pd.to_datetime(
|
||||
date) + pd.Timedelta(days=(window_days - 1) / 2)
|
||||
window_data = data[start_date:end_date]
|
||||
t = np.arange(len(window_data)) + 1
|
||||
y = pd.to_numeric(window_data['wind_speed'], errors='coerce')
|
||||
valid_mask = ~np.isnan(y)
|
||||
t = t[valid_mask]
|
||||
y = y[valid_mask]
|
||||
|
||||
if len(y) < len(initial_guess) * 6:
|
||||
print(
|
||||
f"Not enough valid data after filtering for date: {date}")
|
||||
|
||||
params.append([None] * len(param_names))
|
||||
continue
|
||||
|
||||
try:
|
||||
popt, _ = curve_fit(
|
||||
model_func, t, y, p0=initial_guess, bounds=bounds)
|
||||
params.append(popt)
|
||||
except RuntimeError:
|
||||
print(f"Fitting failed for date: {date}")
|
||||
|
||||
params.append([None] * len(param_names))
|
||||
|
||||
# 保存参数
|
||||
params_df = pd.DataFrame(
|
||||
params, columns=param_names, index=unique_dates)
|
||||
# file_name = f'{year}年_{wind_type}_{H // 1000}km_{model_name}_强度.txt'
|
||||
# params_df.to_csv(os.path.join(output_dir, file_name),
|
||||
# sep='\t', index=True, header=True)
|
||||
|
||||
# 绘图
|
||||
if 'a1' in params_df.columns: # 处理潮汐波多分量
|
||||
plt.figure(figsize=(12, 6))
|
||||
for i, T in enumerate([6, 8, 12, 24], start=1):
|
||||
plt.plot(params_df[f'a{i}'], label=f'{T}h', linewidth=2)
|
||||
else: # 处理行星波单分量
|
||||
plt.figure(figsize=(12, 6))
|
||||
plt.plot(
|
||||
params_df['a'], label=f'{model_name}', color='orange', linewidth=2)
|
||||
|
||||
plt.title(
|
||||
f'{year}年{wind_type}_{H // 1000}km_{model_name}_强度', fontsize=16)
|
||||
plt.xlabel('日期', fontsize=14)
|
||||
plt.ylabel('幅度 (m/s)', fontsize=14)
|
||||
plt.xticks(rotation=45)
|
||||
plt.legend()
|
||||
plt.tick_params(axis='both', labelsize=12) # 调整刻度字体大小
|
||||
plt.tight_layout()
|
||||
# picture_file_name = f'{year}年_{wind_type}_{H // 1000}km_{model_name}_强度.png'
|
||||
# plt.savefig(os.path.join(output_dir, picture_file_name))
|
||||
# plt.show()
|
||||
buffer = BytesIO()
|
||||
plt.savefig(buffer, format='png')
|
||||
buffer.seek(0)
|
||||
plt.close()
|
||||
return buffer
|
||||
|
||||
|
||||
# 处理纬向风和经向风,适配所有模型
|
||||
if __name__ == "__main__":
|
||||
pass
|
||||
# final_plot_v1('uwind', year, H, "")
|
||||
# final_plot_v1('vwind', year, H, "")
|
||||
213
radar/plot_prod.py
Normal file
213
radar/plot_prod.py
Normal file
@ -0,0 +1,213 @@
|
||||
from io import BytesIO
|
||||
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(t, v0, a, b, period):
|
||||
"""行星波模型"""
|
||||
return v0 + a * np.sin((2 * np.pi / period) * t + b)
|
||||
|
||||
# ------------------------------ 数据处理工具函数 ------------------------------
|
||||
|
||||
|
||||
def preprocess_data(file_dir, year):
|
||||
"""读取数据文件并合并成完整的 DataFrame"""
|
||||
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)
|
||||
df = pd.read_csv(file_path, sep='\s+')
|
||||
|
||||
# 替换异常值为 NaN
|
||||
df.loc[df['uwind'] == 1000000, 'uwind'] = np.nan
|
||||
df.loc[df['vwind'] == 1000000, 'vwind'] = np.nan
|
||||
|
||||
# 筛选所需列并重命名
|
||||
date_part = os.path.splitext(file_name)[0][-8:]
|
||||
df = df[['height', 'time', 'uwind', 'vwind']].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')
|
||||
|
||||
# 补全缺失列并排序
|
||||
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]
|
||||
for col in expected_columns:
|
||||
if col not in final_df.columns:
|
||||
final_df[col] = np.nan
|
||||
final_df = final_df[['height', 'time'] + sorted(expected_columns)]
|
||||
|
||||
return final_df
|
||||
|
||||
# ------------------------------ 拟合工具函数 ------------------------------
|
||||
|
||||
|
||||
def fit_wind_speed(data, model_func, initial_guess, bounds, window, num_params):
|
||||
"""对风速数据进行滑动窗口拟合"""
|
||||
# replace "_uwind" and "_vwind" in date column
|
||||
data["date"] = data["date"].str.replace(
|
||||
"_uwind", "").str.replace("_vwind", "")
|
||||
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
|
||||
|
||||
valid_mask = ~np.isnan(y)
|
||||
t = t[valid_mask]
|
||||
y = y[valid_mask]
|
||||
|
||||
if len(y) < num_params * 6:
|
||||
params.append([None] * len(initial_guess))
|
||||
continue
|
||||
|
||||
try:
|
||||
popt, _ = curve_fit(
|
||||
model_func, t, y, p0=initial_guess, bounds=bounds)
|
||||
params.append(popt)
|
||||
except RuntimeError:
|
||||
params.append([None] * len(initial_guess))
|
||||
|
||||
params_df = pd.DataFrame(params, columns=[
|
||||
"v0"] + [f"param_{i}" for i in range(1, len(initial_guess))], index=unique_dates)
|
||||
return params_df
|
||||
|
||||
# ------------------------------ 热力图绘制函数 ------------------------------
|
||||
|
||||
|
||||
def plot_heatmaps(result_dict, param, title, vmax):
|
||||
"""绘制热力图"""
|
||||
fig, axes = plt.subplots(1, 2, figsize=(18, 8))
|
||||
fig.suptitle(title, fontsize=20)
|
||||
|
||||
for i, (wind_type, data) in enumerate(result_dict.items()):
|
||||
heatmap_data = data[param].unstack(level=0)
|
||||
heatmap_data.index = pd.to_datetime(heatmap_data.index)
|
||||
heatmap_data = heatmap_data.iloc[:, ::-1]
|
||||
|
||||
sns.heatmap(
|
||||
heatmap_data.T,
|
||||
cmap="viridis",
|
||||
vmax=vmax,
|
||||
cbar_kws={'label': '振幅 (m/s)'},
|
||||
yticklabels=[
|
||||
f'{height / 1000:.1f}' for height in heatmap_data.columns],
|
||||
ax=axes[i]
|
||||
)
|
||||
axes[i].set_title(f'{wind_type}振幅变化', fontsize=14)
|
||||
axes[i].set_xlabel('日期', fontsize=12)
|
||||
axes[i].set_ylabel('高度 (km)', fontsize=12)
|
||||
|
||||
plt.tight_layout(rect=[0, 0, 1, 0.95])
|
||||
# plt.show()
|
||||
|
||||
|
||||
# ------------------------------ 主逻辑 ------------------------------
|
||||
def final_plot_v2(year, station, model_name):
|
||||
"""
|
||||
主逻辑函数,用于处理数据并绘制选定模型的热力图。
|
||||
|
||||
参数:
|
||||
- year: int,年份
|
||||
- station: str,站点名称
|
||||
- model_name: str,模型名称(如 '潮汐波', '2日行星波', '5日行星波', '10日行星波', '16日行星波')
|
||||
"""
|
||||
# 配置文件路径
|
||||
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)
|
||||
uwind_data = final_df[["height", "time"] +
|
||||
[col for col in final_df.columns if "_uwind" in col]]
|
||||
vwind_data = final_df[["height", "time"] +
|
||||
[col for col in final_df.columns if "_vwind" in col]]
|
||||
heights = uwind_data["height"].unique()
|
||||
|
||||
# 模型配置
|
||||
models = {
|
||||
"潮汐波": {"func": tidal_model, "params": 9, "initial": [0, 1, 0, 1, 0, 1, 0, 1, 0], "bounds": ([-np.inf, 0, -np.inf, 0, -np.inf, 0, -np.inf, 0, -np.inf], [np.inf] * 9), "window": 5},
|
||||
"2日行星波": {"func": lambda t, v0, a, b: planetary_model(t, v0, a, b, 48), "params": 3, "initial": [0, 1, 0], "bounds": ([-np.inf, 0, -np.inf], [np.inf, np.inf, np.inf]), "window": 5},
|
||||
"5日行星波": {"func": lambda t, v0, a, b: planetary_model(t, v0, a, b, 120), "params": 3, "initial": [0, 1, 0], "bounds": ([-np.inf, 0, -np.inf], [np.inf, np.inf, np.inf]), "window": 15},
|
||||
"10日行星波": {"func": lambda t, v0, a, b: planetary_model(t, v0, a, b, 240), "params": 3, "initial": [0, 1, 0], "bounds": ([-np.inf, 0, -np.inf], [np.inf, np.inf, np.inf]), "window": 29},
|
||||
"16日行星波": {"func": lambda t, v0, a, b: planetary_model(t, v0, a, b, 384), "params": 3, "initial": [0, 1, 0], "bounds": ([-np.inf, 0, -np.inf], [np.inf, np.inf, np.inf]), "window": 49}
|
||||
}
|
||||
|
||||
# 检查模型名称是否有效
|
||||
if model_name not in models:
|
||||
raise ValueError(
|
||||
f"无效的模型名称: '{model_name}'。可选模型为: {list(models.keys())}")
|
||||
|
||||
# 获取模型配置
|
||||
config = models[model_name]
|
||||
|
||||
# 拟合纬向风和经向风
|
||||
u_result_dict, v_result_dict = {}, {}
|
||||
for height in heights:
|
||||
# 纬向风数据
|
||||
height_data_u = uwind_data[uwind_data["height"] == height].melt(
|
||||
id_vars=["time", "height"], var_name="date", value_name="wind_speed")
|
||||
u_result_dict[height] = fit_wind_speed(
|
||||
height_data_u, config["func"], config["initial"], config["bounds"], config["window"], config["params"])
|
||||
|
||||
# 经向风数据
|
||||
height_data_v = vwind_data[vwind_data["height"] == height].melt(
|
||||
id_vars=["time", "height"], var_name="date", value_name="wind_speed")
|
||||
v_result_dict[height] = fit_wind_speed(
|
||||
height_data_v, config["func"], config["initial"], config["bounds"], config["window"], config["params"])
|
||||
|
||||
# 汇总结果
|
||||
u_final_result = pd.concat(u_result_dict, names=["height", "date"])
|
||||
v_final_result = pd.concat(v_result_dict, names=["height", "date"])
|
||||
|
||||
# 绘制热力图
|
||||
plot_heatmaps(
|
||||
{"纬向风": u_final_result, "经向风": v_final_result},
|
||||
param="param_1", # 振幅参数
|
||||
title=f'{year}年{model_name}振幅时空变化',
|
||||
vmax=100,
|
||||
)
|
||||
|
||||
buffer = BytesIO()
|
||||
plt.savefig(buffer, format='png')
|
||||
buffer.seek(0)
|
||||
plt.close()
|
||||
return buffer
|
||||
103
saber/__init__.py
Normal file
103
saber/__init__.py
Normal file
@ -0,0 +1,103 @@
|
||||
import glob
|
||||
from io import BytesIO
|
||||
from flask import Blueprint, request, send_file
|
||||
from matplotlib import pyplot as plt
|
||||
from saber.process import DataProcessor
|
||||
from saber.render import Renderer
|
||||
from saber.utils import *
|
||||
|
||||
|
||||
saber_module = Blueprint('saber', __name__)
|
||||
lat_range = (30.0, 40.0)
|
||||
alt_range = (20.0, 105.0)
|
||||
lambda_range = (2, 15)
|
||||
lvboin = True
|
||||
|
||||
processor = DataProcessor(
|
||||
lat_range=lat_range, alt_range=alt_range, lambda_range=lambda_range, lvboin=lvboin)
|
||||
renderer = Renderer()
|
||||
|
||||
|
||||
def extract_payload():
|
||||
buffer = BytesIO()
|
||||
plt.savefig(buffer, format="png")
|
||||
buffer.seek(0)
|
||||
return send_file(buffer, mimetype="image/png")
|
||||
|
||||
|
||||
all_saber_files = glob.glob("./saber/data/**/**.nc", recursive=True)
|
||||
|
||||
|
||||
@saber_module.route("/metadata")
|
||||
def get_files():
|
||||
return all_saber_files
|
||||
|
||||
|
||||
@saber_module.route("/metadata/list_days")
|
||||
def get_days():
|
||||
path = request.args.get("path")
|
||||
|
||||
ncfile = data_nc_load(path)
|
||||
|
||||
return ncfile.date_time.tolist()
|
||||
|
||||
|
||||
@saber_module.route("/render/plot_wave_fitting")
|
||||
def do_plot_wave_day_fitting():
|
||||
path = request.args.get("path")
|
||||
day = request.args.get("day")
|
||||
height = request.args.get("height")
|
||||
|
||||
ncfile = data_nc_load(path)
|
||||
data = processor.process_day(ncfile, int(day))
|
||||
renderer.plot_wave_fitting(data, int(height))
|
||||
return extract_payload()
|
||||
|
||||
|
||||
@saber_module.route("/render/day_fft_ifft_plot")
|
||||
def do_day_fft_ifft_plot():
|
||||
path = request.args.get("path")
|
||||
day = request.args.get("day")
|
||||
cycle_no = request.args.get("cycle_no")
|
||||
|
||||
ncfile = data_nc_load(path)
|
||||
|
||||
data = processor.process_day(ncfile, int(day))
|
||||
renderer.day_fft_ifft_plot(wave_data=data, cycle_no=int(cycle_no))
|
||||
return extract_payload()
|
||||
|
||||
|
||||
@saber_module.route("/render/day_cycle_power_wave_plot")
|
||||
def do_day_cycle_power_wave_plot():
|
||||
path = request.args.get("path")
|
||||
day = request.args.get("day")
|
||||
cycle_no = request.args.get("cycle_no")
|
||||
|
||||
ncfile = data_nc_load(path)
|
||||
|
||||
data = processor.process_day(ncfile, int(day))
|
||||
renderer.day_cycle_power_wave_plot(wave_data=data, cycle_no=int(cycle_no))
|
||||
return extract_payload()
|
||||
|
||||
|
||||
@saber_module.route("/render/month_power_wave_plot")
|
||||
def do_month_power_wave_plot():
|
||||
path = request.args.get("path")
|
||||
month = request.args.get("month")
|
||||
|
||||
ncfile = data_nc_load(path)
|
||||
|
||||
data = processor.process_month(ncfile)
|
||||
renderer.month_power_wave_plot(wave_data=data, date_time=ncfile.date_time)
|
||||
return extract_payload()
|
||||
|
||||
|
||||
@saber_module.route("/render/year_power_wave_plot")
|
||||
def do_year_power_wave_plot():
|
||||
year = request.args.get("year")
|
||||
|
||||
data = processor.process_year([
|
||||
data_nc_load(path) for path in glob.glob(f"./saber/data/{year}/*.nc")
|
||||
])
|
||||
renderer.year_power_wave_plot(year_wave=data)
|
||||
return extract_payload()
|
||||
1077
saber/archive/legacy.py
Normal file
1077
saber/archive/legacy.py
Normal file
File diff suppressed because it is too large
Load Diff
825
saber/archive/saber_render.py
Normal file
825
saber/archive/saber_render.py
Normal file
@ -0,0 +1,825 @@
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.dates as mdates
|
||||
|
||||
from saber.utils import *
|
||||
# from matplotlib.colors import LinearSegmentedColormap
|
||||
# 设置字体为支持中文的字体
|
||||
plt.rcParams['font.family'] = 'SimHei' # 设置为黑体(需要你的环境中有该字体)
|
||||
plt.rcParams['axes.unicode_minus'] = False # 解决负号'-'显示为方块的问题
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
# 5---main程序环节 -
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
# 按日处理资料
|
||||
|
||||
|
||||
def day_process_main(file_path, day_read, latitude_min, latitude_max, altitude_min, altitude_max, lamda_low, lamda_high, lvboin):
|
||||
|
||||
dataset, tplatitude, tplongitude, tpaltitude, ktemp, time, date, date_time = data_nc_load(
|
||||
file_path)
|
||||
# 2018年的第94天 # 4月4日的日期,以年份和年内的第几天表示
|
||||
df = day_data_read(date, day_read, tplatitude)
|
||||
cycles = data_cycle_identify(df, latitude_min, latitude_max)
|
||||
ktemp_cycles, altitude_cycles = data_cycle_generate(
|
||||
cycles, ktemp, tpaltitude, altitude_min, altitude_max)
|
||||
ktemp_wn0 = ktemp_cycles - \
|
||||
np.mean(ktemp_cycles, axis=0) # 观测值-平均温度
|
||||
ktemp_fit_wn1, ktemp_wn1 = fit_wave(ktemp_wn0, 1)
|
||||
ktemp_fit_wn2, ktemp_wn2 = fit_wave(ktemp_wn1, 2)
|
||||
ktemp_fit_wn3, ktemp_wn3 = fit_wave(ktemp_wn2, 3)
|
||||
ktemp_fit_wn4, ktemp_wn4 = fit_wave(ktemp_wn3, 4)
|
||||
ktemp_fit_wn5, ktemp_wn5 = fit_wave(ktemp_wn4, 5)
|
||||
ktemp_fft, ktemp_fft_lvbo, ktemp_ifft = fft_ifft_wave(
|
||||
ktemp_wn5, lamda_low, lamda_high, altitude_min, altitude_max, lvboin)
|
||||
ktemp_Nz, ktemp_Ptz = power_indices(
|
||||
ktemp_cycles, ktemp_wn5, ktemp_ifft, altitude_min, altitude_max)
|
||||
|
||||
return ktemp_cycles, altitude_cycles, ktemp_wn0, ktemp_fit_wn1, ktemp_wn1, ktemp_fit_wn2, ktemp_wn2, ktemp_fit_wn3, ktemp_wn3, ktemp_fit_wn4, ktemp_wn4, ktemp_fit_wn5, ktemp_wn5, ktemp_fft, ktemp_fft_lvbo, ktemp_ifft, ktemp_Nz, ktemp_Ptz
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
# 按文件中单日处理资料
|
||||
|
||||
|
||||
def day_process_maing(dataset, tplatitude, tplongitude, tpaltitude, ktemp, time, date, date_time, day_read,
|
||||
latitude_min, latitude_max, altitude_min, altitude_max, lamda_low, lamda_high, lvboin):
|
||||
df = day_data_read(date, day_read, tplatitude)
|
||||
cycles = data_cycle_identify(df, latitude_min, latitude_max)
|
||||
|
||||
if not cycles: # 如果周期列表为空,返回18个None值
|
||||
return (None,) * 18
|
||||
|
||||
ktemp_cycles, altitude_cycles = data_cycle_generate(
|
||||
cycles, ktemp, tpaltitude, altitude_min, altitude_max)
|
||||
|
||||
if ktemp_cycles is None or altitude_cycles is None: # 再次检查周期数据是否为空
|
||||
return (None,) * 18
|
||||
|
||||
ktemp_wn0 = ktemp_cycles - \
|
||||
np.mean(ktemp_cycles, axis=0) # 按照纬向计算平均温度 wn0_temp
|
||||
ktemp_fit_wn1, ktemp_wn1 = fit_wave(ktemp_wn0, 1)
|
||||
ktemp_fit_wn2, ktemp_wn2 = fit_wave(ktemp_wn1, 2)
|
||||
ktemp_fit_wn3, ktemp_wn3 = fit_wave(ktemp_wn2, 3)
|
||||
ktemp_fit_wn4, ktemp_wn4 = fit_wave(ktemp_wn3, 4)
|
||||
ktemp_fit_wn5, ktemp_wn5 = fit_wave(ktemp_wn4, 5)
|
||||
ktemp_fft, ktemp_fft_lvbo, ktemp_ifft = fft_ifft_wave(ktemp_wn5, lamda_low, lamda_high, altitude_min, altitude_max,
|
||||
lvboin)
|
||||
ktemp_Nz, ktemp_Ptz = power_indices(
|
||||
ktemp_cycles, ktemp_wn5, ktemp_ifft, altitude_min, altitude_max)
|
||||
|
||||
return ktemp_cycles, altitude_cycles, ktemp_wn0, ktemp_fit_wn1, ktemp_wn1, ktemp_fit_wn2, ktemp_wn2, ktemp_fit_wn3, ktemp_wn3, ktemp_fit_wn4, ktemp_wn4, ktemp_fit_wn5, ktemp_wn5, ktemp_fft, ktemp_fft_lvbo, ktemp_ifft, ktemp_Nz, ktemp_Ptz
|
||||
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
# 按月处理资料
|
||||
|
||||
|
||||
def mon_process_main(file_path, latitude_min, latitude_max, altitude_min, altitude_max, lamda_low, lamda_high, lvboin):
|
||||
# 打开文件并读取数据
|
||||
dataset, tplatitude, tplongitude, tpaltitude, ktemp, time, date, date_time = data_nc_load(
|
||||
file_path)
|
||||
|
||||
ktemp_cycles_mon = []
|
||||
altitude_cycles_mon = []
|
||||
ktemp_wn0_mon = []
|
||||
ktemp_fit_wn1_mon = []
|
||||
ktemp_wn1_mon = []
|
||||
ktemp_fit_wn2_mon = []
|
||||
ktemp_wn2_mon = []
|
||||
ktemp_fit_wn3_mon = []
|
||||
ktemp_wn3_mon = []
|
||||
ktemp_fit_wn4_mon = []
|
||||
ktemp_wn4_mon = []
|
||||
ktemp_fit_wn5_mon = []
|
||||
ktemp_wn5_mon = []
|
||||
ktemp_fft_mon = []
|
||||
ktemp_fft_lvbo_mon = []
|
||||
ktemp_ifft_mon = []
|
||||
ktemp_Nz_mon = []
|
||||
ktemp_Ptz_mon = []
|
||||
|
||||
# 遍历每一天,处理数据
|
||||
for day_read in date_time:
|
||||
print(f"读取日期 {day_read}")
|
||||
# 处理单日数据
|
||||
results = day_process_maing(
|
||||
dataset, tplatitude, tplongitude, tpaltitude, ktemp, time, date, date_time,
|
||||
day_read, latitude_min, latitude_max, altitude_min, altitude_max, lamda_low, lamda_high, lvboin
|
||||
)
|
||||
|
||||
# 检查结果是否包含有效数据
|
||||
if results is not None and len(results) == 18:
|
||||
ktemp_cycles0, altitude_cycles0, ktemp_wn00, ktemp_fit_wn10, ktemp_wn10, ktemp_fit_wn20, ktemp_wn20, ktemp_fit_wn30, ktemp_wn30, ktemp_fit_wn40, ktemp_wn40, ktemp_fit_wn50, ktemp_wn50, ktemp_fft0, ktemp_fft_lvbo0, ktemp_ifft0, ktemp_Nz0, ktemp_Ptz0 = results
|
||||
|
||||
# 将有效数据添加到月度列表中
|
||||
ktemp_cycles_mon.append(ktemp_cycles0)
|
||||
altitude_cycles_mon.append(altitude_cycles0)
|
||||
ktemp_wn0_mon.append(ktemp_wn00)
|
||||
ktemp_fit_wn1_mon.append(ktemp_fit_wn10)
|
||||
ktemp_wn1_mon.append(ktemp_wn10)
|
||||
ktemp_fit_wn2_mon.append(ktemp_fit_wn20)
|
||||
ktemp_wn2_mon.append(ktemp_wn20)
|
||||
ktemp_fit_wn3_mon.append(ktemp_fit_wn30)
|
||||
ktemp_wn3_mon.append(ktemp_wn30)
|
||||
ktemp_fit_wn4_mon.append(ktemp_fit_wn40)
|
||||
ktemp_wn4_mon.append(ktemp_wn40)
|
||||
ktemp_fit_wn5_mon.append(ktemp_fit_wn50)
|
||||
ktemp_wn5_mon.append(ktemp_wn50)
|
||||
ktemp_fft_mon.append(ktemp_fft0)
|
||||
ktemp_fft_lvbo_mon.append(ktemp_fft_lvbo0)
|
||||
ktemp_ifft_mon.append(ktemp_ifft0)
|
||||
ktemp_Nz_mon.append(ktemp_Nz0)
|
||||
ktemp_Ptz_mon.append(ktemp_Ptz0)
|
||||
|
||||
# 返回整个月的数据
|
||||
return (date_time, ktemp_cycles_mon, altitude_cycles_mon, ktemp_wn0_mon, ktemp_fit_wn1_mon, ktemp_wn1_mon,
|
||||
ktemp_fit_wn2_mon, ktemp_wn2_mon, ktemp_fit_wn3_mon, ktemp_wn3_mon, ktemp_fit_wn4_mon, ktemp_wn4_mon,
|
||||
ktemp_fit_wn5_mon, ktemp_wn5_mon, ktemp_fft_mon, ktemp_fft_lvbo_mon, ktemp_ifft_mon, ktemp_Nz_mon,
|
||||
ktemp_Ptz_mon)
|
||||
# 滤波后NZ、PTZ重力波势能指标计算
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
# 按年处理资料
|
||||
|
||||
|
||||
def process_yearly_data(year, path, latitude_min, latitude_max, altitude_min, altitude_max, lamda_low, lamda_high, lvboin):
|
||||
# 创建空列表来存储每月的数据
|
||||
date_time_list = []
|
||||
ktemp_cycles_mon_list = []
|
||||
altitude_cycles_mon_list = []
|
||||
ktemp_wn0_mon_list = []
|
||||
ktemp_fit_wn1_mon_list = []
|
||||
ktemp_wn1_mon_list = []
|
||||
ktemp_fit_wn2_mon_list = []
|
||||
ktemp_wn2_mon_list = []
|
||||
ktemp_fit_wn3_mon_list = []
|
||||
ktemp_wn3_mon_list = []
|
||||
ktemp_fit_wn4_mon_list = []
|
||||
ktemp_wn4_mon_list = []
|
||||
ktemp_fit_wn5_mon_list = []
|
||||
ktemp_wn5_mon_list = []
|
||||
ktemp_fft_mon_list = []
|
||||
ktemp_fft_lvbo_mon_list = []
|
||||
ktemp_ifft_mon_list = []
|
||||
ktemp_Nz_mon_list = []
|
||||
ktemp_Ptz_mon_list = []
|
||||
|
||||
# 循环处理每个月的数据
|
||||
for month in range(1, 13):
|
||||
# 获取当前月的文件路径
|
||||
file_path = filename_read(year, month, path)
|
||||
|
||||
try:
|
||||
# 调用 mon_process_main 函数处理文件并获取结果
|
||||
(date_time, ktemp_cycles_mon, altitude_cycles_mon, ktemp_wn0_mon, ktemp_fit_wn1_mon,
|
||||
ktemp_wn1_mon, ktemp_fit_wn2_mon, ktemp_wn2_mon, ktemp_fit_wn3_mon, ktemp_wn3_mon,
|
||||
ktemp_fit_wn4_mon, ktemp_wn4_mon, ktemp_fit_wn5_mon, ktemp_wn5_mon, ktemp_fft_mon,
|
||||
ktemp_fft_lvbo_mon, ktemp_ifft_mon, ktemp_Nz_mon, ktemp_Ptz_mon) = mon_process_main(
|
||||
file_path, latitude_min, latitude_max, altitude_min, altitude_max, lamda_low, lamda_high, lvboin
|
||||
)
|
||||
|
||||
# 将每月的结果添加到相应的列表中
|
||||
date_time_list.extend(date_time)
|
||||
ktemp_cycles_mon_list.extend(ktemp_cycles_mon)
|
||||
altitude_cycles_mon_list.extend(altitude_cycles_mon)
|
||||
ktemp_wn0_mon_list.extend(ktemp_wn0_mon)
|
||||
ktemp_fit_wn1_mon_list.extend(ktemp_fit_wn1_mon)
|
||||
ktemp_wn1_mon_list.extend(ktemp_wn1_mon)
|
||||
ktemp_fit_wn2_mon_list.extend(ktemp_fit_wn2_mon)
|
||||
ktemp_wn2_mon_list.extend(ktemp_wn2_mon)
|
||||
ktemp_fit_wn3_mon_list.extend(ktemp_fit_wn3_mon)
|
||||
ktemp_wn3_mon_list.extend(ktemp_wn3_mon)
|
||||
ktemp_fit_wn4_mon_list.extend(ktemp_fit_wn4_mon)
|
||||
ktemp_wn4_mon_list.extend(ktemp_wn4_mon)
|
||||
ktemp_fit_wn5_mon_list.extend(ktemp_fit_wn5_mon)
|
||||
ktemp_wn5_mon_list.extend(ktemp_wn5_mon)
|
||||
ktemp_fft_mon_list.extend(ktemp_fft_mon)
|
||||
ktemp_fft_lvbo_mon_list.extend(ktemp_fft_lvbo_mon)
|
||||
ktemp_ifft_mon_list.extend(ktemp_ifft_mon)
|
||||
ktemp_Nz_mon_list.extend(ktemp_Nz_mon)
|
||||
ktemp_Ptz_mon_list.extend(ktemp_Ptz_mon)
|
||||
|
||||
except Exception as e:
|
||||
print(f"处理文件 {file_path} 时出错(月份:{month}):{e}")
|
||||
continue # 出现错误时跳过当前月份,继续处理下个月
|
||||
|
||||
# 返回所有月份的处理结果
|
||||
return {
|
||||
"date_time_list": date_time_list,
|
||||
"ktemp_cycles_mon_list": ktemp_cycles_mon_list,
|
||||
"altitude_cycles_mon_list": altitude_cycles_mon_list,
|
||||
"ktemp_wn0_mon_list": ktemp_wn0_mon_list,
|
||||
"ktemp_fit_wn1_mon_list": ktemp_fit_wn1_mon_list,
|
||||
"ktemp_wn1_mon_list": ktemp_wn1_mon_list,
|
||||
"ktemp_fit_wn2_mon_list": ktemp_fit_wn2_mon_list,
|
||||
"ktemp_wn2_mon_list": ktemp_wn2_mon_list,
|
||||
"ktemp_fit_wn3_mon_list": ktemp_fit_wn3_mon_list,
|
||||
"ktemp_wn3_mon_list": ktemp_wn3_mon_list,
|
||||
"ktemp_fit_wn4_mon_list": ktemp_fit_wn4_mon_list,
|
||||
"ktemp_wn4_mon_list": ktemp_wn4_mon_list,
|
||||
"ktemp_fit_wn5_mon_list": ktemp_fit_wn5_mon_list,
|
||||
"ktemp_wn5_mon_list": ktemp_wn5_mon_list,
|
||||
"ktemp_fft_mon_list": ktemp_fft_mon_list,
|
||||
"ktemp_fft_lvbo_mon_list": ktemp_fft_lvbo_mon_list,
|
||||
"ktemp_ifft_mon_list": ktemp_ifft_mon_list,
|
||||
"ktemp_Nz_mon_list": ktemp_Nz_mon_list,
|
||||
"ktemp_Ptz_mon_list": ktemp_Ptz_mon_list
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------------------------
|
||||
# 6-主要统计分析图绘制 -
|
||||
# -----------------------------------------------------------------------------------------------------------------------------
|
||||
# 6-1 示例的逐层滤波效果图---不同波数 曲线图
|
||||
|
||||
|
||||
def day_fit_wave_plot(height_no, ktemp_wn0, ktemp_fit_wn1, ktemp_wn1, ktemp_fit_wn2, ktemp_wn2, ktemp_fit_wn3, ktemp_wn3, ktemp_fit_wn4, ktemp_wn4, ktemp_fit_wn5, ktemp_wn5):
|
||||
|
||||
N = len(ktemp_wn0[:, height_no])
|
||||
# 循环周期索引
|
||||
x = np.arange(N)
|
||||
|
||||
y1_1 = ktemp_wn0[:, height_no]
|
||||
y1_2 = ktemp_fit_wn1[:, height_no]
|
||||
|
||||
y2_1 = ktemp_wn1[:, height_no]
|
||||
y2_2 = ktemp_fit_wn2[:, height_no]
|
||||
|
||||
y3_1 = ktemp_wn2[:, height_no]
|
||||
y3_2 = ktemp_fit_wn3[:, height_no]
|
||||
|
||||
y4_1 = ktemp_wn3[:, height_no]
|
||||
y4_2 = ktemp_fit_wn4[:, height_no]
|
||||
|
||||
y5_1 = ktemp_wn4[:, height_no]
|
||||
y5_2 = ktemp_fit_wn5[:, height_no]
|
||||
|
||||
y6 = ktemp_wn5[:, height_no]
|
||||
|
||||
plt.figure(figsize=(16, 10)) # 调整图形大小
|
||||
# 原始信号的时间序列
|
||||
plt.subplot(2, 3, 1)
|
||||
plt.plot(x, y1_1, label='原始信号')
|
||||
plt.plot(x, y1_2, label='拟合信号', linestyle='--')
|
||||
plt.title('(a)波数k=1')
|
||||
plt.xlabel('Cycles', labelpad=10) # 增加标签间距
|
||||
plt.ylabel('温度 (K)', labelpad=10) # 增加标签间距
|
||||
plt.legend()
|
||||
plt.tight_layout() # 调整子图参数以适应图形区域
|
||||
|
||||
plt.subplot(2, 3, 2)
|
||||
plt.plot(x, y2_1, label='原始信号')
|
||||
plt.plot(x, y2_2, label='拟合信号', linestyle='--')
|
||||
plt.title('(b)波数k=2')
|
||||
plt.xlabel('Cycles', labelpad=10) # 增加标签间距
|
||||
plt.ylabel('温度 (K)', labelpad=10) # 增加标签间距
|
||||
plt.legend()
|
||||
plt.tight_layout() # 调整子图参数以适应图形区域
|
||||
|
||||
plt.subplot(2, 3, 3)
|
||||
plt.plot(x, y3_1, label='原始信号')
|
||||
plt.plot(x, y3_2, label='拟合信号', linestyle='--')
|
||||
plt.title('(c)波数k=3')
|
||||
plt.xlabel('Cycles', labelpad=10) # 增加标签间距
|
||||
plt.ylabel('温度 (K)', labelpad=10) # 增加标签间距
|
||||
plt.legend()
|
||||
plt.tight_layout() # 调整子图参数以适应图形区域
|
||||
|
||||
plt.subplot(2, 3, 4)
|
||||
plt.plot(x, y4_1, label='原始信号')
|
||||
plt.plot(x, y4_2, label='拟合信号', linestyle='--')
|
||||
plt.title('(d)波数k=4')
|
||||
plt.xlabel('Cycles', labelpad=10) # 增加标签间距
|
||||
plt.ylabel('温度 (K)', labelpad=10) # 增加标签间距
|
||||
plt.legend()
|
||||
plt.tight_layout() # 调整子图参数以适应图形区域
|
||||
|
||||
plt.subplot(2, 3, 5)
|
||||
plt.plot(x, y5_1, label='原始信号')
|
||||
plt.plot(x, y5_2, label='拟合信号', linestyle='--')
|
||||
plt.title('(e)波数k=5')
|
||||
plt.xlabel('Cycles', labelpad=10) # 增加标签间距
|
||||
plt.ylabel('温度 (K)', labelpad=10) # 增加标签间距
|
||||
plt.legend()
|
||||
plt.tight_layout() # 调整子图参数以适应图形区域
|
||||
|
||||
plt.subplot(2, 3, 6)
|
||||
plt.plot(x, y6, label='滤波信号')
|
||||
plt.title('(f)滤波1-5后信号')
|
||||
plt.xlabel('Cycles', labelpad=10) # 增加标签间距
|
||||
plt.ylabel('温度 (K)', labelpad=10) # 增加标签间距
|
||||
plt.tight_layout() # 调整子图参数以适应图形区域
|
||||
|
||||
# 调整子图之间的边距
|
||||
plt.subplots_adjust(top=0.8, bottom=0.2, left=0.1,
|
||||
right=0.8, hspace=0.3, wspace=0.2)
|
||||
|
||||
plt.show()
|
||||
|
||||
|
||||
def day_fit_wave_plotg(height_no, ktemp_wn0, ktemp_fit_wn1, ktemp_wn1, ktemp_fit_wn2,
|
||||
ktemp_wn2, ktemp_fit_wn3, ktemp_wn3, ktemp_fit_wn4,
|
||||
ktemp_wn4, ktemp_fit_wn5, ktemp_wn5):
|
||||
|
||||
N = len(ktemp_wn0[:, height_no])
|
||||
x = np.arange(N)
|
||||
|
||||
y1_1, y1_2 = ktemp_wn0[:, height_no], ktemp_fit_wn1[:, height_no]
|
||||
y2_1, y2_2 = ktemp_wn1[:, height_no], ktemp_fit_wn2[:, height_no]
|
||||
y3_1, y3_2 = ktemp_wn2[:, height_no], ktemp_fit_wn3[:, height_no]
|
||||
y4_1, y4_2 = ktemp_wn3[:, height_no], ktemp_fit_wn4[:, height_no]
|
||||
y5_1, y5_2 = ktemp_wn4[:, height_no], ktemp_fit_wn5[:, height_no]
|
||||
y6 = ktemp_wn5[:, height_no]
|
||||
|
||||
plt.figure(figsize=(16, 10))
|
||||
|
||||
y_limits = (min(min(y1_1), min(y2_1), min(y3_1), min(y4_1), min(y5_1), min(y6)),
|
||||
max(max(y1_1), max(y2_1), max(y3_1), max(y4_1), max(y5_1), max(y6)))
|
||||
|
||||
for i, (y1, y2) in enumerate([(y1_1, y1_2), (y2_1, y2_2), (y3_1, y3_2),
|
||||
(y4_1, y4_2), (y5_1, y5_2), (y6, None)]):
|
||||
plt.subplot(2, 3, i + 1)
|
||||
plt.plot(x, y1, label='原始信号')
|
||||
if y2 is not None:
|
||||
plt.plot(x, y2, label='拟合信号', linestyle='--')
|
||||
plt.title(f'({"abcdef"[i]})波数k={i + 1 if i < 5 else "滤波0-5"}')
|
||||
plt.xlabel('Cycles', labelpad=10)
|
||||
plt.ylabel('温度 (K)', labelpad=10)
|
||||
plt.legend()
|
||||
plt.xticks(x) # 设置横坐标为整数
|
||||
plt.ylim(y_limits) # 设置统一纵坐标范围
|
||||
plt.tight_layout()
|
||||
|
||||
plt.subplots_adjust(top=0.8, bottom=0.2, left=0.1,
|
||||
right=0.8, hspace=0.3, wspace=0.2)
|
||||
plt.show()
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------------------------
|
||||
# 6-2 示例的高度滤波处理--不同循环周期 曲线图
|
||||
def day_fft_ifft_plot(cycle_no, ktemp_wn5, ktemp_fft, ktemp_ifft, altitude_min, altitude_max, lamda_low, lamda_high):
|
||||
|
||||
N = len(ktemp_wn5[cycle_no, :])
|
||||
# 采样时间间隔,其倒数等于采用频率,以1km为标准尺度等同于1s,假设波的速度为1km/s
|
||||
dt = (altitude_max-altitude_min)/(N-1)
|
||||
# 时间序列索引
|
||||
n = np.arange(N)
|
||||
f = n / (N * dt)
|
||||
t = np.round(np.linspace(altitude_min, altitude_max, N), 2)
|
||||
|
||||
# 原始扰动温度
|
||||
x = ktemp_wn5[cycle_no, :]
|
||||
# 傅里叶变换频谱分析
|
||||
y = ktemp_fft[cycle_no, :]
|
||||
# 滤波后的傅里叶变换频谱分析
|
||||
yy = ktemp_fft_lvbo[cycle_no, :]
|
||||
# 傅里叶逆变换后的扰动温度
|
||||
yyy = ktemp_ifft[cycle_no, :]
|
||||
|
||||
plt.figure(figsize=(15, 10)) # 调整图形大小
|
||||
# 原始信号的时间序列
|
||||
plt.subplot(2, 2, 1)
|
||||
plt.plot(t, x)
|
||||
plt.title('(a)原始信号')
|
||||
plt.xlabel('高度 (km)', labelpad=10) # 增加标签间距
|
||||
plt.ylabel('温度 (K)', labelpad=10) # 增加标签间距
|
||||
# 原始振幅谱
|
||||
plt.subplot(2, 2, 2)
|
||||
plt.plot(f, np.abs(y) * 2 / N)
|
||||
plt.title('(b))原始振幅谱')
|
||||
plt.xlabel('频率/Hz', labelpad=10) # 增加标签间距
|
||||
plt.ylabel('振幅', labelpad=10) # 增加标签间距
|
||||
|
||||
# 通过IFFT回到时间域
|
||||
plt.subplot(2, 2, 3)
|
||||
plt.plot(t, yyy)
|
||||
plt.title('(c))傅里叶逆变换')
|
||||
plt.xlabel('高度 (km)', labelpad=10) # 增加标签间距
|
||||
plt.ylabel('温度 (K)', labelpad=10) # 增加标签间距
|
||||
|
||||
# 滤波后的振幅谱
|
||||
plt.subplot(2, 2, 4)
|
||||
plt.plot(f, np.abs(yy) * 2 / N)
|
||||
plt.title(f'(d)滤除波长 < {lamda_low} km, > {lamda_high} km的波')
|
||||
plt.xlabel('频率/Hz', labelpad=10) # 增加标签间距
|
||||
plt.ylabel('振幅', labelpad=10) # 增加标签间距
|
||||
|
||||
# 调整子图之间的边距
|
||||
plt.subplots_adjust(top=0.8, bottom=0.2, left=0.2,
|
||||
right=0.8, hspace=0.3, wspace=0.2)
|
||||
|
||||
plt.show()
|
||||
|
||||
# 绘制原始信号与滤波后信号
|
||||
plt.figure(figsize=(12, 6)) # 调整图形大小
|
||||
plt.plot(t, x, label='原始信号')
|
||||
plt.plot(t, yyy, label='滤波后信号', linestyle='--')
|
||||
plt.title('信号比较')
|
||||
plt.xlabel('高度 (km)', labelpad=10) # 增加标签间距
|
||||
plt.ylabel('温度 (K)', labelpad=10) # 增加标签间距
|
||||
plt.legend()
|
||||
plt.tight_layout() # 调整子图参数以适应图形区域
|
||||
plt.show()
|
||||
|
||||
|
||||
def day_fft_ifft_plotg(cycle_no, ktemp_wn5, ktemp_fft, ktemp_ifft, altitude_min, altitude_max, lamda_low, lamda_high):
|
||||
|
||||
N = len(ktemp_wn5[cycle_no, :])
|
||||
dt = (altitude_max - altitude_min) / (N - 1) # 采样时间间隔
|
||||
n = np.arange(N) # 时间序列索引
|
||||
f = n / (N * dt)
|
||||
t = np.round(np.linspace(altitude_min, altitude_max, N), 2)
|
||||
|
||||
x = ktemp_wn5[cycle_no, :] # 原始扰动温度
|
||||
y = ktemp_fft[cycle_no, :] # 傅里叶变换频谱分析
|
||||
yy = ktemp_fft_lvbo[cycle_no, :] # 滤波后的傅里叶变换频谱分析
|
||||
yyy = ktemp_ifft[cycle_no, :] # 傅里叶逆变换后的扰动温度
|
||||
|
||||
plt.figure(figsize=(15, 10)) # 调整图形大小
|
||||
|
||||
# 计算纵坐标范围
|
||||
temp_limits = (min(min(x), min(yyy)), max(max(x), max(yyy)))
|
||||
amp_limits = (0, max(np.max(np.abs(y) * 2 / N),
|
||||
np.max(np.abs(yy) * 2 / N)))
|
||||
|
||||
# 原始信号的时间序列
|
||||
plt.subplot(2, 2, 1)
|
||||
plt.plot(t, x)
|
||||
plt.title('(a)原始信号')
|
||||
plt.xlabel('高度 (km)', labelpad=10)
|
||||
plt.ylabel('温度 (K)', labelpad=10)
|
||||
plt.ylim(temp_limits) # 设置统一纵坐标范围
|
||||
|
||||
# 原始振幅谱
|
||||
plt.subplot(2, 2, 2)
|
||||
plt.plot(f, np.abs(y) * 2 / N)
|
||||
plt.title('(b)原始振幅谱')
|
||||
plt.xlabel('频率/Hz', labelpad=10)
|
||||
plt.ylabel('振幅', labelpad=10)
|
||||
plt.ylim(amp_limits) # 设置统一纵坐标范围
|
||||
|
||||
# 通过IFFT回到时间域
|
||||
plt.subplot(2, 2, 3)
|
||||
plt.plot(t, yyy)
|
||||
plt.title('(c)傅里叶逆变换')
|
||||
plt.xlabel('高度 (km)', labelpad=10)
|
||||
plt.ylabel('温度 (K)', labelpad=10)
|
||||
plt.ylim(temp_limits) # 设置统一纵坐标范围
|
||||
|
||||
# 滤波后的振幅谱
|
||||
plt.subplot(2, 2, 4)
|
||||
plt.plot(f, np.abs(yy) * 2 / N)
|
||||
plt.title(f'(d)滤除波长 < {lamda_low} km, > {lamda_high} km的波')
|
||||
plt.xlabel('频率/Hz', labelpad=10)
|
||||
plt.ylabel('振幅', labelpad=10)
|
||||
plt.ylim(amp_limits) # 设置统一纵坐标范围
|
||||
|
||||
# 调整子图之间的边距
|
||||
plt.subplots_adjust(top=0.8, bottom=0.2, left=0.2,
|
||||
right=0.8, hspace=0.3, wspace=0.2)
|
||||
plt.show()
|
||||
|
||||
# 绘制原始信号与滤波后信号
|
||||
plt.figure(figsize=(6, 8)) # 调整图形大小
|
||||
plt.plot(x, t, label='原始信号')
|
||||
plt.plot(yyy, t, label='滤波后信号', linestyle='--')
|
||||
plt.title('信号比较')
|
||||
plt.ylabel('高度 (km)', labelpad=10) # 增加标签间距
|
||||
plt.xlabel('温度 (K)', labelpad=10) # 增加标签间距
|
||||
plt.legend()
|
||||
plt.xlim(temp_limits) # 设置统一纵坐标范围
|
||||
plt.tight_layout()
|
||||
plt.show()
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------------------------
|
||||
# 6-3 示例的按高度的重力波势能变化曲线图
|
||||
|
||||
|
||||
def day_cycle_power_wave_plot(cycle_no, altitude_min, altitude_max, ktemp_Nz, ktemp_Ptz):
|
||||
|
||||
N = len(ktemp_Nz[cycle_no, :])
|
||||
y = np.round(np.linspace(altitude_min, altitude_max, N), 2)
|
||||
x1 = ktemp_Nz[cycle_no, :]
|
||||
x2 = ktemp_Ptz[cycle_no, :]
|
||||
|
||||
plt.figure(figsize=(12, 10)) # 调整图形大小
|
||||
# 原始信号的时间序列
|
||||
plt.subplot(1, 2, 1)
|
||||
plt.plot(x1[::-1], y, label='原始信号')
|
||||
plt.title('(a)Nz')
|
||||
plt.xlabel('Nz', labelpad=10) # 增加标签间距
|
||||
plt.ylabel('高度 (km)', labelpad=10) # 增加标签间距
|
||||
|
||||
# 原始信号的时间序列
|
||||
plt.subplot(1, 2, 2)
|
||||
plt.plot(x2[::-1], y, label='原始信号')
|
||||
plt.title('(b)Ptz')
|
||||
plt.xlabel('Ptz', labelpad=10) # 增加标签间距
|
||||
plt.ylabel('高度 (km)', labelpad=10) # 增加标签间距
|
||||
|
||||
# 调整子图之间的边距
|
||||
plt.subplots_adjust(top=0.8, bottom=0.2, left=0.1,
|
||||
right=0.8, hspace=0.3, wspace=0.2)
|
||||
plt.tight_layout() # 调整子图参数以适应图形区域
|
||||
|
||||
plt.show()
|
||||
# -----------------------------------------------------------------------------------------------------------------------------
|
||||
# 6-4 按日统计的按周期计算的不同高度的重力波势能 平面图
|
||||
|
||||
|
||||
def day_power_wave_plot(altitude_min, altitude_max, ktemp_Nz, ktemp_Ptz):
|
||||
# 假设 ktemp_Nz 和 ktemp_Ptz 以及 altitude_min, altitude_max 已经定义好
|
||||
x = np.arange(ktemp_Nz.shape[0])
|
||||
y = np.round(np.linspace(altitude_min, altitude_max, ktemp_Nz.shape[1]), 2)
|
||||
|
||||
# 创建一个图形,并指定两个子图
|
||||
fig, axs = plt.subplots(1, 2, figsize=(15, 10))
|
||||
|
||||
# 第一幅图 (a) NZ
|
||||
cax1 = axs[0].imshow(ktemp_Nz.T[::-1], aspect='auto', cmap='viridis',
|
||||
origin='lower', extent=[x[0], x[-1], y[0], y[-1]])
|
||||
fig.colorbar(cax1, ax=axs[0]) # 为第一幅图添加颜色条
|
||||
axs[0].set_title('(a) NZ')
|
||||
axs[0].set_ylabel('Height (km)')
|
||||
axs[0].set_xlabel('Cycles')
|
||||
axs[0].set_yticks(np.linspace(30, 90, 7))
|
||||
axs[0].set_yticklabels(np.round(np.linspace(30, 90, 7), 1))
|
||||
axs[0].set_xticks(np.arange(15))
|
||||
axs[0].set_xticklabels(np.arange(15))
|
||||
|
||||
# 第二幅图 (b) PTZ
|
||||
cax2 = axs[1].imshow(ktemp_Ptz.T[::-1], aspect='auto', cmap='viridis',
|
||||
origin='lower', extent=[x[0], x[-1], y[0], y[-1]])
|
||||
fig.colorbar(cax2, ax=axs[1]) # 为第二幅图添加颜色条
|
||||
axs[1].set_title('(b) PTZ')
|
||||
axs[1].set_ylabel('Height (km)')
|
||||
axs[1].set_xlabel('Cycles')
|
||||
axs[1].set_yticks(np.linspace(30, 90, 7))
|
||||
axs[1].set_yticklabels(np.round(np.linspace(30, 90, 7), 1))
|
||||
axs[1].set_xticks(np.arange(15))
|
||||
axs[1].set_xticklabels(np.arange(15))
|
||||
|
||||
# 调整子图之间的边距
|
||||
plt.subplots_adjust(top=0.9, bottom=0.1, left=0.05,
|
||||
right=0.95, hspace=0.3, wspace=0.3)
|
||||
plt.tight_layout() # 调整布局以避免重叠
|
||||
plt.show()
|
||||
# -----------------------------------------------------------------------------------------------------------------------------
|
||||
# 6-5 按月统计的每日重力波势能随天变化的图
|
||||
|
||||
|
||||
def month_power_wave_plot(altitude_min, altitude_max, ktemp_Nz_mon, ktemp_Ptz_mon):
|
||||
if ktemp_Nz_mon and ktemp_Nz_mon[0] is not None:
|
||||
nz_shape = np.array(ktemp_Nz_mon[0]).shape
|
||||
else:
|
||||
nz_shape = (15, 157)
|
||||
if ktemp_Ptz_mon and ktemp_Ptz_mon[0] is not None:
|
||||
ptz_shape = np.array(ktemp_Ptz_mon[0]).shape
|
||||
else:
|
||||
ptz_shape = (15, 157)
|
||||
y = np.round(np.linspace(altitude_min, altitude_max, nz_shape[1]), 2)
|
||||
x = np.arange(len(date_time.data))
|
||||
# 处理 ktemp_Nz_mon
|
||||
ktemp_Nz_plot = np.array([np.mean(day_data if day_data is not None else np.zeros(
|
||||
nz_shape), axis=0) for day_data in ktemp_Nz_mon])
|
||||
ktemp_Ptz_plot = np.array(
|
||||
[np.mean(day_data if day_data is not None else np.zeros(nz_shape), axis=0) for day_data in ktemp_Ptz_mon])
|
||||
# 处理 ktemp_Ptz_mon(以100为界剔除异常值)
|
||||
# ktemp_Ptz_plot = np.array([np.mean(day_data if day_data is not None and np.all(day_data <= 100) else np.zeros(ptz_shape), axis=0) for day_data in ktemp_Ptz_mon])
|
||||
# 创建一个图形,并指定两个子图
|
||||
fig, axs = plt.subplots(1, 2, figsize=(15, 10))
|
||||
|
||||
# 第一幅图 (a) NZ
|
||||
cax1 = axs[0].imshow(ktemp_Nz_plot.T[::-1], aspect='auto', cmap='rainbow', origin='lower',
|
||||
extent=[x[0], x[-1], y[0], y[-1]])
|
||||
fig.colorbar(cax1, ax=axs[0]) # 为第一幅图添加颜色条
|
||||
axs[0].set_title('(a) NZ')
|
||||
axs[0].set_xlabel('Time')
|
||||
axs[0].set_ylabel('Height')
|
||||
axs[0].set_yticks(np.linspace(30, 100, 8))
|
||||
axs[0].set_yticklabels(np.round(np.linspace(30, 100, 8), 1))
|
||||
axs[0].set_xticks(x)
|
||||
axs[0].set_xticklabels(x)
|
||||
|
||||
# 第二幅图 (b) PTZ
|
||||
cax2 = axs[1].imshow(np.log(ktemp_Ptz_plot.T[::-1]), aspect='auto',
|
||||
cmap='rainbow', origin='lower', extent=[x[0], x[-1], y[0], y[-1]])
|
||||
fig.colorbar(cax2, ax=axs[1]) # 为第二幅图添加颜色条
|
||||
axs[1].set_title('(b) PTZ')
|
||||
axs[1].set_xlabel('Time')
|
||||
axs[1].set_ylabel('Height')
|
||||
axs[1].set_yticks(np.linspace(30, 100, 8))
|
||||
axs[1].set_yticklabels(np.round(np.linspace(30, 100, 8), 1))
|
||||
axs[1].set_xticks(x)
|
||||
axs[1].set_xticklabels(x)
|
||||
|
||||
# 调整子图之间的边距
|
||||
plt.subplots_adjust(top=0.9, bottom=0.1, left=0.05,
|
||||
right=0.95, hspace=0.3, wspace=0.3)
|
||||
plt.tight_layout() # 调整布局以避免重叠
|
||||
plt.show()
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------------------------
|
||||
# 6-6 按年统计的每月重力波势能随月变化的图
|
||||
|
||||
|
||||
def year_power_wave_plot(year, path, latitude_min, latitude_max, altitude_min, altitude_max, lamda_low, lamda_high,
|
||||
lvboin):
|
||||
# 假设我们已经从process_yearly_data函数中获取了一年的Nz和Ptz数据
|
||||
results = process_yearly_data(year, path, latitude_min, latitude_max, altitude_min, altitude_max, lamda_low,
|
||||
lamda_high, lvboin)
|
||||
ktemp_Nz_mon_list = results["ktemp_Nz_mon_list"]
|
||||
ktemp_Ptz_mon_list = results["ktemp_Ptz_mon_list"]
|
||||
ktemp_Ptz_mon_list.pop(0)
|
||||
ktemp_Nz_mon_list.pop(0)
|
||||
|
||||
# 准备日期数据,这里假设date_time_list是一年中的所有日期
|
||||
date_time_list = results["date_time_list"]
|
||||
date_time_list.pop(0)
|
||||
date_nums = mdates.date2num(date_time_list) # 将日期转换为matplotlib可以理解的数字格式
|
||||
|
||||
# 获取date_time_list长度作为横坐标新的依据
|
||||
x_ticks_length = len(date_time_list)
|
||||
x_ticks = np.arange(0, x_ticks_length, 30)
|
||||
x_labels = [date_time_list[i] if i < len(
|
||||
date_time_list) else "" for i in x_ticks]
|
||||
|
||||
# 准备高度数据
|
||||
# 假设高度数据有157个点
|
||||
y = np.round(np.linspace(altitude_min, altitude_max, 157), 2)
|
||||
|
||||
# 创建一个图形,并指定两个子图
|
||||
fig, axs = plt.subplots(1, 2, figsize=(15, 10))
|
||||
|
||||
# 处理 ktemp_Nz_mon
|
||||
ktemp_Nz_plot = np.array(
|
||||
[np.mean(day_data if day_data is not None else np.zeros((15, 157)), axis=0) for day_data in ktemp_Nz_mon_list])
|
||||
# 处理 ktemp_Ptz_mon
|
||||
ktemp_Ptz_plot = np.array(
|
||||
[np.mean(day_data if day_data is not None else np.zeros((15, 157)), axis=0) for day_data in ktemp_Ptz_mon_list])
|
||||
# ktemp_Ptz_plot = np.array(
|
||||
# [np.mean(day_data if day_data is not None and np.all(day_data <= 100) else np.zeros((15, 157)), axis=0) for
|
||||
# day_data in ktemp_Ptz_mon_list])
|
||||
|
||||
# 第一幅图 (a) NZ
|
||||
cax1 = axs[0].imshow(ktemp_Nz_plot.T[::-1], aspect='auto', cmap='rainbow', origin='lower',
|
||||
extent=[0, x_ticks_length - 1, y[0], y[-1]])
|
||||
fig.colorbar(cax1, ax=axs[0]) # 为第一幅图添加颜色条
|
||||
axs[0].set_title('(a) NZ')
|
||||
axs[0].set_xlabel('Time')
|
||||
axs[0].set_ylabel('Height')
|
||||
axs[0].set_yticks(np.linspace(30, 100, 8))
|
||||
axs[0].set_yticklabels(np.round(np.linspace(30, 100, 8), 1))
|
||||
axs[0].set_xticks(x_ticks) # 设置新的横坐标刻度
|
||||
axs[0].set_xticklabels(x_labels, rotation=45)
|
||||
|
||||
# 第二幅图 (b) PTZ
|
||||
cax2 = axs[1].imshow(np.log(ktemp_Ptz_plot.T[::-1]), aspect='auto', cmap='rainbow', origin='lower',
|
||||
extent=[0, x_ticks_length - 1, y[0], y[-1]])
|
||||
fig.colorbar(cax2, ax=axs[1]) # 为第二幅图添加颜色条
|
||||
axs[1].set_title('(b) PTZ')
|
||||
axs[1].set_xlabel('Time')
|
||||
axs[1].set_ylabel('Height')
|
||||
axs[1].set_yticks(np.linspace(30, 100, 8))
|
||||
axs[1].set_yticklabels(np.round(np.linspace(30, 100, 8), 1))
|
||||
axs[1].set_xticks(x_ticks) # 设置新的横坐标刻度
|
||||
axs[1].set_xticklabels(x_labels, rotation=45)
|
||||
|
||||
# 调整子图之间的边距
|
||||
plt.subplots_adjust(top=0.9, bottom=0.1, left=0.05,
|
||||
right=0.95, hspace=0.3, wspace=0.3)
|
||||
plt.tight_layout() # 调整布局以避免重叠
|
||||
plt.show()
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------------------------
|
||||
# 7 主程序,运行数据,并输出主要统计分析图 -
|
||||
# -----------------------------------------------------------------------------------------------------------------------------
|
||||
# 7-1 挑选某一天进行运行主要程序
|
||||
file_path = "./SABER/data\\2016\\SABER_Temp_O3_April2016_v2.0.nc"
|
||||
# day_read=2018113,
|
||||
day_read = 2016094
|
||||
# 初始化某一天、某个纬度、高度范围等参数
|
||||
latitude_min = 30.0
|
||||
latitude_max = 40.0
|
||||
altitude_min = 30.0
|
||||
altitude_max = 100.0
|
||||
lamda_low = 2
|
||||
lamda_high = 15
|
||||
lvboin = True
|
||||
|
||||
(ktemp_cycles, altitude_cycles,
|
||||
ktemp_wn0,
|
||||
ktemp_fit_wn1, ktemp_wn1,
|
||||
ktemp_fit_wn2, ktemp_wn2,
|
||||
ktemp_fit_wn3, ktemp_wn3,
|
||||
ktemp_fit_wn4, ktemp_wn4,
|
||||
ktemp_fit_wn5, ktemp_wn5,
|
||||
ktemp_fft, ktemp_fft_lvbo, ktemp_ifft,
|
||||
ktemp_Nz, ktemp_Ptz) = day_process_main(
|
||||
file_path,
|
||||
# day_read=2018113,
|
||||
day_read,
|
||||
# 初始化某一天、某个纬度、高度范围等参数
|
||||
latitude_min,
|
||||
latitude_max,
|
||||
altitude_min,
|
||||
altitude_max,
|
||||
lamda_low, lamda_high, lvboin)
|
||||
# -----------------------------------------------------------------------------------------------------------------------------
|
||||
# 7-2 挑选某一个月进行运行主要程序
|
||||
(date_time, ktemp_cycles_mon, altitude_cycles_mon, # 某月份 逐日时间 每日不同循环周期温度 不同循环周期高度数据
|
||||
# 距平扰动温度
|
||||
ktemp_wn0_mon,
|
||||
# 波数为1的拟合温度 滤波后的扰动温度
|
||||
ktemp_fit_wn1_mon, ktemp_wn1_mon,
|
||||
# 波数为2的拟合温度 滤波后的扰动温度
|
||||
ktemp_fit_wn2_mon, ktemp_wn2_mon,
|
||||
# 波数为3的拟合温度 滤波后的扰动温度
|
||||
ktemp_fit_wn3_mon, ktemp_wn3_mon,
|
||||
# 波数为4的拟合温度 滤波后的扰动温度
|
||||
ktemp_fit_wn4_mon, ktemp_wn4_mon,
|
||||
# 波数为5的拟合温度 滤波后的扰动温度
|
||||
ktemp_fit_wn5_mon, ktemp_wn5_mon,
|
||||
# 滤波0-5后扰动温度傅里叶频谱分析 滤除波长为2km-15km内后频谱分析 滤波后的扰动温度
|
||||
ktemp_fft_mon, ktemp_fft_lvbo_mon, ktemp_ifft_mon,
|
||||
# 滤波后NZ、PTZ重力波势能指标计算
|
||||
ktemp_Nz_mon, ktemp_Ptz_mon) = mon_process_main(
|
||||
file_path,
|
||||
# 初始化某一天、某个纬度、高度范围等参数
|
||||
latitude_min,
|
||||
latitude_max,
|
||||
altitude_min,
|
||||
altitude_max,
|
||||
lamda_low, lamda_high, lvboin)
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------------------------
|
||||
# 7-3挑选某一年进行运行主要程序
|
||||
|
||||
|
||||
def filename_read(year, month_num, path):
|
||||
# 月份映射
|
||||
month_map = {
|
||||
1: "January", 2: "February", 3: "March", 4: "April",
|
||||
5: "May", 6: "June", 7: "July", 8: "August",
|
||||
9: "September", 10: "October", 11: "November", 12: "December"}
|
||||
|
||||
# 获取月份名称
|
||||
month = month_map.get(month_num, "Invalid month number")
|
||||
|
||||
# 构造文件路径:E:\SABER\year\SABER_Temp_O3_MonthYear_v2.0.nc
|
||||
file_path = f"{path}\\{year}\\SABER_Temp_O3_{month}{year}_v2.0.nc"
|
||||
|
||||
# 打印路径
|
||||
print(file_path)
|
||||
|
||||
# 返回文件路径
|
||||
return file_path
|
||||
|
||||
|
||||
year = 2018
|
||||
path = "./saber/data"
|
||||
# 初始化某一天、某个纬度、高度范围等参数
|
||||
latitude_min = 30.0
|
||||
latitude_max = 40.0
|
||||
altitude_min = 20.0
|
||||
altitude_max = 105.0
|
||||
lamda_low = 2
|
||||
lamda_high = 15
|
||||
lvboin = True
|
||||
# 调用函数处理所有月份
|
||||
results = process_yearly_data(year, path, latitude_min, latitude_max,
|
||||
altitude_min, altitude_max, lamda_low, lamda_high, lvboin)
|
||||
|
||||
# 解包返回的字典
|
||||
date_time_list = results["date_time_list"]
|
||||
ktemp_cycles_mon_list = results["ktemp_cycles_mon_list"]
|
||||
altitude_cycles_mon_list = results["altitude_cycles_mon_list"]
|
||||
ktemp_wn0_mon_list = results["ktemp_wn0_mon_list"]
|
||||
ktemp_fit_wn1_mon_list = results["ktemp_fit_wn1_mon_list"]
|
||||
ktemp_wn1_mon_list = results["ktemp_wn1_mon_list"]
|
||||
ktemp_fit_wn2_mon_list = results["ktemp_fit_wn2_mon_list"]
|
||||
ktemp_wn2_mon_list = results["ktemp_wn2_mon_list"]
|
||||
ktemp_fit_wn3_mon_list = results["ktemp_fit_wn3_mon_list"]
|
||||
ktemp_wn3_mon_list = results["ktemp_wn3_mon_list"]
|
||||
ktemp_fit_wn4_mon_list = results["ktemp_fit_wn4_mon_list"]
|
||||
ktemp_wn4_mon_list = results["ktemp_wn4_mon_list"]
|
||||
ktemp_fit_wn5_mon_list = results["ktemp_fit_wn5_mon_list"]
|
||||
ktemp_wn5_mon_list = results["ktemp_wn5_mon_list"]
|
||||
ktemp_fft_mon_list = results["ktemp_fft_mon_list"]
|
||||
ktemp_fft_lvbo_mon_list = results["ktemp_fft_lvbo_mon_list"]
|
||||
ktemp_ifft_mon_list = results["ktemp_ifft_mon_list"]
|
||||
ktemp_Nz_mon_list = results["ktemp_Nz_mon_list"]
|
||||
ktemp_Ptz_mon_list = results["ktemp_Ptz_mon_list"]
|
||||
# -----------------------------------------------------------------------------------------------------------------------------
|
||||
# 7-3 绘制不同结果图
|
||||
height_no = 1
|
||||
day_fit_wave_plotg(height_no, ktemp_wn0, ktemp_fit_wn1, ktemp_wn1, ktemp_fit_wn2, ktemp_wn2,
|
||||
ktemp_fit_wn3, ktemp_wn3, ktemp_fit_wn4, ktemp_wn4, ktemp_fit_wn5, ktemp_wn5)
|
||||
cycle_no = 1
|
||||
day_fft_ifft_plotg(cycle_no, ktemp_wn5, ktemp_fft, ktemp_ifft,
|
||||
altitude_min, altitude_max, lamda_low, lamda_high)
|
||||
day_cycle_power_wave_plot(cycle_no, altitude_min,
|
||||
altitude_max, ktemp_Nz, ktemp_Ptz)
|
||||
day_power_wave_plot(altitude_min, altitude_max, ktemp_Nz, ktemp_Ptz)
|
||||
month_power_wave_plot(altitude_min, altitude_max, ktemp_Nz_mon, ktemp_Ptz_mon)
|
||||
year_power_wave_plot(year, path, latitude_min, latitude_max,
|
||||
altitude_min, altitude_max, lamda_low, lamda_high, lvboin)
|
||||
840
saber/archive/zjy_wave_fit_plot.py
Normal file
840
saber/archive/zjy_wave_fit_plot.py
Normal file
@ -0,0 +1,840 @@
|
||||
from io import BytesIO
|
||||
import netCDF4 as nc
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
import pandas as pd
|
||||
from scipy.optimize import curve_fit
|
||||
# from matplotlib.colors import LinearSegmentedColormap
|
||||
# 设置字体为支持中文的字体
|
||||
plt.rcParams['font.family'] = 'SimHei' # 设置为黑体(需要你的环境中有该字体)
|
||||
plt.rcParams['axes.unicode_minus'] = False # 解决负号'-'显示为方块的问题
|
||||
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
# 1---打开文件并读取不同变量数据 -
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
def data_nc_load(file_path):
|
||||
|
||||
dataset = nc.Dataset(file_path, 'r')
|
||||
|
||||
# 纬度数据,二维数组形状为(42820,379) 42820为事件,379则为不同高度
|
||||
tplatitude = dataset.variables['tplatitude'][:, :]
|
||||
tplongitude = dataset.variables['tplongitude'][:, :] # 经度数据
|
||||
tpaltitude = dataset.variables['tpaltitude'][:, :] # 高度,二维数组形状为(42820,379)
|
||||
time = dataset.variables['time'][:, :] # 二维数组形状为(42820,379)
|
||||
date = dataset.variables['date'][:]
|
||||
date_time = np.unique(date) # 输出数据时间信息
|
||||
ktemp = dataset.variables['ktemp'][:, :] # 温度数据,二维数组形状为(42820,379)
|
||||
|
||||
return dataset, tplatitude, tplongitude, tpaltitude, ktemp, time, date, date_time
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
# 2---筛选某一天、某个纬度和高度范围15个不同cycle的温度数据 -
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
# 2-1 读取某一天的所有事件及其对应的纬度数据
|
||||
|
||||
|
||||
def day_data_read(date, day_read, tplatitude):
|
||||
|
||||
events = np.where(date == day_read)[0] # 读取筛选天的事件编号 4294-5714位置,从0开始编号
|
||||
time_events = date[date == day_read] # 读取筛选天的事件编号 4294-5714位置,从0开始编号
|
||||
latitudes = tplatitude[events, 189] # 输出每个事件中间位置 即第189个经纬度
|
||||
df = pd.DataFrame([ # 创建一个包含事件编号、纬度的列表,共1421个事件
|
||||
{'time': time, 'event': event, 'latitude': lat}
|
||||
for time, event, lat in zip(time_events, events, latitudes)])
|
||||
|
||||
# print(df.head()) # 打印前几行数据以检查
|
||||
|
||||
return df
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
# 2-2 将事件按照卫星轨迹周期进行输出处理,并输出落在纬度范围内的每个周期的事件的行号
|
||||
|
||||
|
||||
def data_cycle_identify(df, latitude_min, latitude_max):
|
||||
|
||||
cycles = [] # 存储每个周期的事件编号列表
|
||||
# 存储当前周期的事件编号
|
||||
current_cycle_events = []
|
||||
prev_latitude = None
|
||||
|
||||
# 遍历DataFrame中的每一行以识别周期和筛选事件
|
||||
for index, row in df.iterrows():
|
||||
current_event = int(row['event'])
|
||||
current_latitude = row['latitude']
|
||||
|
||||
if prev_latitude is not None and prev_latitude < 0 and current_latitude >= 0: # 检查是否是新周期的开始(纬度从负变正,且首次变正)
|
||||
# 重置当前周期的事件编号列表
|
||||
current_cycle_events = []
|
||||
|
||||
if latitude_min <= current_latitude <= latitude_max: # 如果事件的纬度在指定范围内,添加到当前周期的事件编号列表
|
||||
current_cycle_events.append(current_event)
|
||||
|
||||
if prev_latitude is not None and prev_latitude >= 0 and current_latitude < 0: # 检查是否是周期的结束(纬度从正变负)
|
||||
|
||||
# 添加当前周期的事件编号列表到周期列表
|
||||
if current_cycle_events: # 确保当前周期有事件编号
|
||||
cycles.append(current_cycle_events)
|
||||
current_cycle_events = [] # 重置当前周期的事件编号列表
|
||||
prev_latitude = current_latitude
|
||||
|
||||
if current_cycle_events: # 处理最后一个周期,如果存在的话
|
||||
cycles.append(current_cycle_events)
|
||||
|
||||
print(f"一天周期为 {len(cycles)}")
|
||||
for cycle_index, cycle in enumerate(cycles, start=0):
|
||||
# 屏幕显示每个循环周期的事件
|
||||
print(f"周期 {cycle_index} 包含事件个数: {len(cycle)} 具体事件为: {cycle} ")
|
||||
|
||||
return cycles
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
# 2-3---按照循环周期合并同周期数据,并输出处理后的温度数据、对应的高度数据
|
||||
|
||||
|
||||
def data_cycle_generate(cycles, ktemp, tpaltitude, altitude_min, altitude_max):
|
||||
|
||||
# 初始化列表存储每个周期的温度
|
||||
ktemp_cycles = []
|
||||
# 初始化每个循环周期的高度数据
|
||||
altitude_cycles = []
|
||||
print(f"周期数为 {len(cycles)}")
|
||||
if cycles is not None: # 检查周期是否有事件编号
|
||||
for event in cycles:
|
||||
# 获取每个周期各个事件的ktemp数据
|
||||
ktemp_cycles_events = np.array(ktemp[event, :])
|
||||
ktemp_cycles_events[np.logical_or(
|
||||
ktemp_cycles_events == -999, ktemp_cycles_events > 999)] = np.NaN # 缺失值处理,避免影响结果
|
||||
ktemp_cycles_mean = np.nanmean(
|
||||
ktemp_cycles_events, axis=0) # 对所有周期的 ktemp 数据取均值
|
||||
|
||||
# altitude_cycles_events =np.array(tpaltitude[event,:]) # 获取每个周期各个事件的tpaltitude数据
|
||||
# altitude_cycles_mean = np.nanmean(altitude_cycles_events, axis=0) # 对所有周期的 altitude 数据取均值
|
||||
# 使用第一个的高度来表征所有的
|
||||
altitude_cycles_mean = tpaltitude[event[0], :]
|
||||
altitude_indices = np.where((altitude_cycles_mean >= altitude_min) & (
|
||||
altitude_cycles_mean <= altitude_max))[0]
|
||||
|
||||
ktemp_cycles.append(np.array(ktemp_cycles_mean[altitude_indices]))
|
||||
altitude_cycles.append(
|
||||
np.array(altitude_cycles_mean[altitude_indices]))
|
||||
|
||||
# 找到最短列表的长度
|
||||
min_length = min(len(arr) for arr in ktemp_cycles)
|
||||
# 创建新的列表,将每个子列表截断为最短长度
|
||||
ktemp_cycles = np.vstack([arr[:min_length] for arr in ktemp_cycles])
|
||||
altitude_cycles = np.vstack([arr[:min_length] for arr in altitude_cycles])
|
||||
|
||||
return ktemp_cycles, altitude_cycles
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
# 4---高度相同下不同循环周期数据的波拟合和滤波处理 -
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# 对输入数据按照行(即纬向)进行波数为k的滤波,数据为15*157
|
||||
def fit_wave(ktemp_wn0, k):
|
||||
|
||||
wave_fit = []
|
||||
|
||||
def single_harmonic(x, A, phi, C, k):
|
||||
return A * np.sin(2 * np.pi / (15/k) * x + phi) + C
|
||||
|
||||
# 数据转置并对每行进行操作,按照同高度数据进行处理
|
||||
for rtemp in ktemp_wn0.T:
|
||||
# 为当前高度层创建索引数组
|
||||
indices = np.arange(rtemp.size)
|
||||
def fit_temp(x, A, phi, C): return single_harmonic(
|
||||
x, A, phi, C, k) # 定义只拟合 A, phi, C 的 lambda 函数,k 固定
|
||||
params, params_covariance = curve_fit(
|
||||
fit_temp, indices, rtemp) # 使用 curve_fit 进行拟合
|
||||
# 提取拟合参数 A, phi, C
|
||||
A, phi, C = params
|
||||
# 使用拟合参数计算 wn3
|
||||
rtemp1 = single_harmonic(indices, A, phi, C, k)
|
||||
# 存储拟合参数和拟合曲线
|
||||
wave_fit.append(np.array(rtemp1))
|
||||
wave_fit = np.vstack(wave_fit)
|
||||
|
||||
wave_fit = wave_fit.T
|
||||
wave_wn = ktemp_wn0-wave_fit
|
||||
|
||||
return wave_fit, wave_wn
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
# 4---同周期下不同高度数据的波拟合和滤波处理 -
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
# 对输入数据按照列(即纬向)进行滤波,滤除波长2和15km以内的波, 数据为15*157
|
||||
def fft_ifft_wave(ktemp_wn5, lamda_low, lamda_high, altitude_min, altitude_max, lvboin):
|
||||
|
||||
ktemp_fft = []
|
||||
ktemp_ifft = []
|
||||
ktemp_fft_lvbo = []
|
||||
|
||||
for rtemp in ktemp_wn5:
|
||||
# 采样点数或长度
|
||||
N = len(rtemp)
|
||||
# 采样时间间隔,其倒数等于采用频率,以1km为标准尺度等同于1s,假设波的速度为1km/s
|
||||
dt = (altitude_max-altitude_min)/(N-1)
|
||||
# 时间序列索引
|
||||
n = np.arange(N)
|
||||
# # t = altitude_min + n * dt # 时间向量
|
||||
# t = np.round(np.linspace(altitude_min, altitude_max, N),2)
|
||||
# 频率索引向量
|
||||
f = n / (N * dt)
|
||||
# 对输入信号进行傅里叶变换
|
||||
y = np.fft.fft(rtemp)
|
||||
|
||||
# f_low = 2*np.pi / lamda_high # 定义波长滤波范围(以频率计算) # 高频截止频率
|
||||
# f_high = 2*np.pi / lamda_low
|
||||
|
||||
# 定义波长滤波范围(以频率计算) # 高频截止频率
|
||||
f_low = 1 / lamda_high
|
||||
# 低频截止频率
|
||||
f_high = 1 / lamda_low
|
||||
|
||||
# 创建滤波后的频域信号
|
||||
yy = y.copy()
|
||||
|
||||
# 使用逻辑索引过滤特定频段(未确定)
|
||||
if lvboin:
|
||||
freq_filter = (f > f_low) & (f < f_high) # 创建逻辑掩码
|
||||
else:
|
||||
freq_filter = (f < f_low) | (f > f_high) # 创建逻辑掩码
|
||||
|
||||
yy[freq_filter] = 0 # 过滤掉指定频段
|
||||
yy_ifft = np.real(np.fft.ifft(yy))
|
||||
|
||||
ktemp_fft.append(y)
|
||||
# 存储拟合参数和拟合曲线
|
||||
ktemp_ifft.append(np.array(yy_ifft))
|
||||
ktemp_fft_lvbo.append(yy)
|
||||
|
||||
ktemp_fft = np.vstack(ktemp_fft)
|
||||
ktemp_ifft = np.vstack(ktemp_ifft)
|
||||
ktemp_fft_lvbo = np.vstack(ktemp_fft_lvbo)
|
||||
|
||||
return ktemp_fft, ktemp_fft_lvbo, ktemp_ifft
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
# 5---同周期下不同高度数据的BT_z背景位等指标计算 -
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
# ----------------------------------------------------------------------------------
|
||||
|
||||
|
||||
def power_indices(ktemp_cycles, ktemp_wn5, ktemp_ifft, altitude_min, altitude_max):
|
||||
|
||||
# 定义Brunt-Väisälä频率计算函数
|
||||
def brunt_vaisala_frequency(g, BT_z, c_p, height):
|
||||
# 计算位温随高度的变化率
|
||||
dBT_z_dz = np.gradient(BT_z, height)
|
||||
# 计算 Brunt-Väisälä 频率
|
||||
return np.sqrt((g / BT_z) * ((g / c_p) + dBT_z_dz))
|
||||
|
||||
# 定义势能计算函数
|
||||
def calculate_gravitational_potential_energy(g, BT_z, N_z, PT_z):
|
||||
return 0.5 * ((g / N_z) ** 2) * ((PT_z / BT_z) ** 2)
|
||||
|
||||
# 重力加速度
|
||||
g = 9.81
|
||||
# 空气比热容
|
||||
c_p = 1004.5
|
||||
|
||||
height = np.linspace(altitude_min, altitude_max,
|
||||
ktemp_cycles.shape[1]) * 1000 # 高度
|
||||
background = ktemp_cycles - ktemp_wn5
|
||||
|
||||
# 初始化结果矩阵
|
||||
N_z_matrix = []
|
||||
# 初始化结果矩阵
|
||||
PT_z_matrix = []
|
||||
|
||||
# 循环处理background和filtered_perturbation所有行
|
||||
for i in range(background.shape[0]):
|
||||
BT_z = np.array(background[i])
|
||||
# 滤波后的扰动
|
||||
PT_z = np.array(ktemp_ifft[i])
|
||||
|
||||
# 调用Brunt-Väisälä频率函数
|
||||
N_z = brunt_vaisala_frequency(g, BT_z, c_p, height)
|
||||
PT_z = calculate_gravitational_potential_energy(
|
||||
g, BT_z, N_z, PT_z) # 调用势能函数
|
||||
N_z_matrix.append(N_z)
|
||||
PT_z_matrix.append(PT_z)
|
||||
|
||||
ktemp_Nz = np.vstack(N_z_matrix)
|
||||
ktemp_Ptz = np.vstack(PT_z_matrix)
|
||||
return ktemp_Nz, ktemp_Ptz
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
# 5---main程序环节 -
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
# 按日处理资料
|
||||
|
||||
|
||||
def day_process_main(file_path, day_read, latitude_min, latitude_max, altitude_min, altitude_max, lamda_low, lamda_high, lvboin):
|
||||
|
||||
dataset, tplatitude, tplongitude, tpaltitude, ktemp, time, date, date_time = data_nc_load(
|
||||
file_path)
|
||||
# 2018年的第94天 # 4月4日的日期,以年份和年内的第几天表示
|
||||
df = day_data_read(date, day_read, tplatitude)
|
||||
cycles = data_cycle_identify(df, latitude_min, latitude_max)
|
||||
ktemp_cycles, altitude_cycles = data_cycle_generate(
|
||||
cycles, ktemp, tpaltitude, altitude_min, altitude_max)
|
||||
# 按照纬向计算平均温度 wn0_temp
|
||||
ktemp_wn0 = ktemp_cycles - np.mean(ktemp_cycles, axis=0)
|
||||
ktemp_fit_wn1, ktemp_wn1 = fit_wave(ktemp_wn0, 1)
|
||||
ktemp_fit_wn2, ktemp_wn2 = fit_wave(ktemp_wn1, 2)
|
||||
ktemp_fit_wn3, ktemp_wn3 = fit_wave(ktemp_wn2, 3)
|
||||
ktemp_fit_wn4, ktemp_wn4 = fit_wave(ktemp_wn3, 4)
|
||||
ktemp_fit_wn5, ktemp_wn5 = fit_wave(ktemp_wn4, 5)
|
||||
ktemp_fft, ktemp_fft_lvbo, ktemp_ifft = fft_ifft_wave(
|
||||
ktemp_wn5, lamda_low, lamda_high, altitude_min, altitude_max, lvboin)
|
||||
ktemp_Nz, ktemp_Ptz = power_indices(
|
||||
ktemp_cycles, ktemp_wn5, ktemp_ifft, altitude_min, altitude_max)
|
||||
|
||||
return ktemp_cycles, altitude_cycles, ktemp_wn0, ktemp_fit_wn1, ktemp_wn1, ktemp_fit_wn2, ktemp_wn2, ktemp_fit_wn3, ktemp_wn3, ktemp_fit_wn4, ktemp_wn4, ktemp_fit_wn5, ktemp_wn5, ktemp_fft, ktemp_fft_lvbo, ktemp_ifft, ktemp_Nz, ktemp_Ptz
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
# 按文件中单日处理资料
|
||||
|
||||
|
||||
def day_process_maing(dataset, tplatitude, tplongitude, tpaltitude, ktemp, time, date, date_time, day_read, latitude_min, latitude_max, altitude_min, altitude_max, lamda_low, lamda_high, lvboin):
|
||||
|
||||
# dataset, tplatitude, tplongitude, tpaltitude, ktemp, time, date, date_time = data_nc_load (file_path)
|
||||
# 2018年的第94天 # 4月4日的日期,以年份和年内的第几天表示
|
||||
df = day_data_read(date, day_read, tplatitude)
|
||||
cycles = data_cycle_identify(df, latitude_min, latitude_max)
|
||||
ktemp_cycles, altitude_cycles = data_cycle_generate(
|
||||
cycles, ktemp, tpaltitude, altitude_min, altitude_max)
|
||||
# 按照纬向计算平均温度 wn0_temp
|
||||
ktemp_wn0 = ktemp_cycles - np.mean(ktemp_cycles, axis=0)
|
||||
ktemp_fit_wn1, ktemp_wn1 = fit_wave(ktemp_wn0, 1)
|
||||
ktemp_fit_wn2, ktemp_wn2 = fit_wave(ktemp_wn1, 2)
|
||||
ktemp_fit_wn3, ktemp_wn3 = fit_wave(ktemp_wn2, 3)
|
||||
ktemp_fit_wn4, ktemp_wn4 = fit_wave(ktemp_wn3, 4)
|
||||
ktemp_fit_wn5, ktemp_wn5 = fit_wave(ktemp_wn4, 5)
|
||||
ktemp_fft, ktemp_fft_lvbo, ktemp_ifft = fft_ifft_wave(
|
||||
ktemp_wn5, lamda_low, lamda_high, altitude_min, altitude_max, lvboin)
|
||||
ktemp_Nz, ktemp_Ptz = power_indices(
|
||||
ktemp_cycles, ktemp_wn5, ktemp_ifft, altitude_min, altitude_max)
|
||||
|
||||
return ktemp_cycles, altitude_cycles, ktemp_wn0, ktemp_fit_wn1, ktemp_wn1, ktemp_fit_wn2, ktemp_wn2, ktemp_fit_wn3, ktemp_wn3, ktemp_fit_wn4, ktemp_wn4, ktemp_fit_wn5, ktemp_wn5, ktemp_fft, ktemp_fft_lvbo, ktemp_ifft, ktemp_Nz, ktemp_Ptz
|
||||
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
# 按月处理资料
|
||||
|
||||
|
||||
def mon_process_main(file_path, latitude_min, latitude_max, altitude_min, altitude_max, lamda_low, lamda_high, lvboin):
|
||||
|
||||
dataset, tplatitude, tplongitude, tpaltitude, ktemp, time, date, date_time = data_nc_load(
|
||||
file_path)
|
||||
|
||||
ktemp_cycles_mon = []
|
||||
altitude_cycles_mon = []
|
||||
ktemp_wn0_mon = []
|
||||
ktemp_fit_wn1_mon = []
|
||||
ktemp_wn1_mon = []
|
||||
ktemp_fit_wn2_mon = []
|
||||
ktemp_wn2_mon = []
|
||||
ktemp_fit_wn3_mon = []
|
||||
ktemp_wn3_mon = []
|
||||
ktemp_fit_wn4_mon = []
|
||||
ktemp_wn4_mon = []
|
||||
ktemp_fit_wn5_mon = []
|
||||
ktemp_wn5_mon = []
|
||||
ktemp_fft_mon = []
|
||||
ktemp_fft_lvbo_mon = []
|
||||
ktemp_ifft_mon = []
|
||||
ktemp_Nz_mon = []
|
||||
ktemp_Ptz_mon = []
|
||||
|
||||
for day_read in date_time:
|
||||
print(f"读取日期 {day_read}")
|
||||
(ktemp_cycles0, altitude_cycles0,
|
||||
ktemp_wn00,
|
||||
ktemp_fit_wn10, ktemp_wn10,
|
||||
ktemp_fit_wn20, ktemp_wn20,
|
||||
ktemp_fit_wn30, ktemp_wn30,
|
||||
ktemp_fit_wn40, ktemp_wn40,
|
||||
ktemp_fit_wn50, ktemp_wn50,
|
||||
ktemp_fft0, ktemp_fft_lvbo0, ktemp_ifft0,
|
||||
ktemp_Nz0, ktemp_Ptz0) = day_process_maing(
|
||||
dataset, tplatitude, tplongitude, tpaltitude, ktemp, time, date, date_time,
|
||||
day_read, latitude_min, latitude_max, altitude_min, altitude_max, lamda_low, lamda_high, lvboin)
|
||||
|
||||
ktemp_cycles_mon.append(ktemp_cycles0)
|
||||
altitude_cycles_mon.append(altitude_cycles0)
|
||||
ktemp_wn0_mon.append(ktemp_wn00)
|
||||
ktemp_fit_wn1_mon.append(ktemp_fit_wn50)
|
||||
ktemp_wn1_mon.append(ktemp_wn50)
|
||||
ktemp_fit_wn2_mon.append(ktemp_fit_wn50)
|
||||
ktemp_wn2_mon.append(ktemp_wn50)
|
||||
ktemp_fit_wn3_mon.append(ktemp_fit_wn50)
|
||||
ktemp_wn3_mon.append(ktemp_wn50)
|
||||
ktemp_fit_wn4_mon.append(ktemp_fit_wn50)
|
||||
ktemp_wn4_mon.append(ktemp_wn50)
|
||||
ktemp_fit_wn5_mon.append(ktemp_fit_wn50)
|
||||
ktemp_wn5_mon.append(ktemp_wn50)
|
||||
ktemp_fft_mon.append(ktemp_fft0)
|
||||
ktemp_fft_lvbo_mon.append(ktemp_fft_lvbo0)
|
||||
ktemp_ifft_mon.append(ktemp_ifft0)
|
||||
ktemp_Nz_mon.append(ktemp_Nz0)
|
||||
ktemp_Ptz_mon.append(ktemp_Ptz0)
|
||||
|
||||
return [date_time, ktemp_cycles_mon, altitude_cycles_mon, # 某月份 逐日时间 每日不同循环周期温度 不同循环周期高度数据
|
||||
# 距平扰动温度
|
||||
ktemp_wn0_mon,
|
||||
# 波数为1的拟合温度 滤波后的扰动温度
|
||||
ktemp_fit_wn1_mon, ktemp_wn1_mon,
|
||||
# 波数为2的拟合温度 滤波后的扰动温度
|
||||
ktemp_fit_wn2_mon, ktemp_wn2_mon,
|
||||
# 波数为3的拟合温度 滤波后的扰动温度
|
||||
ktemp_fit_wn3_mon, ktemp_wn3_mon,
|
||||
# 波数为4的拟合温度 滤波后的扰动温度
|
||||
ktemp_fit_wn4_mon, ktemp_wn4_mon,
|
||||
# 波数为5的拟合温度 滤波后的扰动温度
|
||||
ktemp_fit_wn5_mon, ktemp_wn5_mon,
|
||||
# 滤波0-5后扰动温度傅里叶频谱分析 滤除波长为2km-15km内后频谱分析 滤波后的扰动温度
|
||||
ktemp_fft_mon, ktemp_fft_lvbo_mon, ktemp_ifft_mon,
|
||||
ktemp_Nz_mon, ktemp_Ptz_mon] # 滤波后NZ、PTZ重力波势能指标计算
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------------------------
|
||||
# 6-主要统计分析图绘制 -
|
||||
# -----------------------------------------------------------------------------------------------------------------------------
|
||||
# 6-1 示例的逐层滤波效果图---不同波数 曲线图
|
||||
|
||||
|
||||
day_fit_wave_modes = ["k=1", "k=2", "k=3", "k=4", "k=5", "滤波"]
|
||||
|
||||
|
||||
def day_fit_wave_plot(height_no, ktemp_wn0, ktemp_fit_wn1, ktemp_wn1, ktemp_fit_wn2, ktemp_wn2, ktemp_fit_wn3, ktemp_wn3, ktemp_fit_wn4, ktemp_wn4, ktemp_fit_wn5, ktemp_wn5, mode):
|
||||
|
||||
N = len(ktemp_wn0[:, height_no])
|
||||
# 循环周期索引
|
||||
x = np.arange(N)
|
||||
|
||||
y1_1 = ktemp_wn0[:, height_no]
|
||||
y1_2 = ktemp_fit_wn1[:, height_no]
|
||||
|
||||
y2_1 = ktemp_wn1[:, height_no]
|
||||
y2_2 = ktemp_fit_wn2[:, height_no]
|
||||
|
||||
y3_1 = ktemp_wn2[:, height_no]
|
||||
y3_2 = ktemp_fit_wn3[:, height_no]
|
||||
|
||||
y4_1 = ktemp_wn3[:, height_no]
|
||||
y4_2 = ktemp_fit_wn4[:, height_no]
|
||||
|
||||
y5_1 = ktemp_wn4[:, height_no]
|
||||
y5_2 = ktemp_fit_wn5[:, height_no]
|
||||
|
||||
y6 = ktemp_wn5[:, height_no]
|
||||
|
||||
if mode not in ["k=1", "k=2", "k=3", "k=4", "k=5", "滤波"]:
|
||||
raise ValueError(
|
||||
"mode 参数应为 'k=1', 'k=2', 'k=3', 'k=4', 'k=5', 'lvbo' 中的一个")
|
||||
|
||||
plt.figure(figsize=(16, 10)) # 调整图形大小
|
||||
# 原始信号的时间序列
|
||||
if mode == "k=1":
|
||||
plt.plot(x, y1_1, label='原始信号')
|
||||
plt.plot(x, y1_2, label='拟合信号', linestyle='--')
|
||||
plt.title('(a)波数k=1')
|
||||
plt.xlabel('Cycles', labelpad=10) # 增加标签间距
|
||||
plt.ylabel('温度 (°C)', labelpad=10) # 增加标签间距
|
||||
plt.legend()
|
||||
plt.tight_layout() # 调整子图参数以适应图形区域
|
||||
|
||||
if mode == "k=2":
|
||||
plt.plot(x, y2_1, label='原始信号')
|
||||
plt.plot(x, y2_2, label='拟合信号', linestyle='--')
|
||||
plt.title('(b)波数k=2')
|
||||
plt.xlabel('Cycles', labelpad=10) # 增加标签间距
|
||||
plt.ylabel('温度 (°C)', labelpad=10) # 增加标签间距
|
||||
plt.legend()
|
||||
plt.tight_layout() # 调整子图参数以适应图形区域
|
||||
|
||||
if mode == "k=3":
|
||||
plt.plot(x, y3_1, label='原始信号')
|
||||
plt.plot(x, y3_2, label='拟合信号', linestyle='--')
|
||||
plt.title('(c)波数k=3')
|
||||
plt.xlabel('Cycles', labelpad=10) # 增加标签间距
|
||||
plt.ylabel('温度 (°C)', labelpad=10) # 增加标签间距
|
||||
plt.legend()
|
||||
plt.tight_layout() # 调整子图参数以适应图形区域
|
||||
|
||||
if mode == "k=4":
|
||||
plt.plot(x, y4_1, label='原始信号')
|
||||
plt.plot(x, y4_2, label='拟合信号', linestyle='--')
|
||||
plt.title('(d)波数k=4')
|
||||
plt.xlabel('Cycles', labelpad=10) # 增加标签间距
|
||||
plt.ylabel('温度 (°C)', labelpad=10) # 增加标签间距
|
||||
plt.legend()
|
||||
plt.tight_layout() # 调整子图参数以适应图形区域
|
||||
|
||||
if mode == "k=5":
|
||||
plt.plot(x, y5_1, label='原始信号')
|
||||
plt.plot(x, y5_2, label='拟合信号', linestyle='--')
|
||||
plt.title('(e)波数k=5')
|
||||
plt.xlabel('Cycles', labelpad=10) # 增加标签间距
|
||||
plt.ylabel('温度 (°C)', labelpad=10) # 增加标签间距
|
||||
plt.legend()
|
||||
plt.tight_layout() # 调整子图参数以适应图形区域
|
||||
|
||||
if mode == "滤波":
|
||||
plt.plot(x, y6, label='滤波信号')
|
||||
plt.title('(f)滤波1-5后信号')
|
||||
plt.xlabel('Cycles', labelpad=10) # 增加标签间距
|
||||
plt.ylabel('温度 (°C)', labelpad=10) # 增加标签间距
|
||||
plt.tight_layout() # 调整子图参数以适应图形区域
|
||||
|
||||
|
||||
def day_fit_wave_plotg(height_no, ktemp_wn0, ktemp_fit_wn1, ktemp_wn1, ktemp_fit_wn2,
|
||||
ktemp_wn2, ktemp_fit_wn3, ktemp_wn3, ktemp_fit_wn4,
|
||||
ktemp_wn4, ktemp_fit_wn5, ktemp_wn5):
|
||||
|
||||
N = len(ktemp_wn0[:, height_no])
|
||||
x = np.arange(N)
|
||||
|
||||
y1_1, y1_2 = ktemp_wn0[:, height_no], ktemp_fit_wn1[:, height_no]
|
||||
y2_1, y2_2 = ktemp_wn1[:, height_no], ktemp_fit_wn2[:, height_no]
|
||||
y3_1, y3_2 = ktemp_wn2[:, height_no], ktemp_fit_wn3[:, height_no]
|
||||
y4_1, y4_2 = ktemp_wn3[:, height_no], ktemp_fit_wn4[:, height_no]
|
||||
y5_1, y5_2 = ktemp_wn4[:, height_no], ktemp_fit_wn5[:, height_no]
|
||||
y6 = ktemp_wn5[:, height_no]
|
||||
|
||||
plt.figure(figsize=(16, 10))
|
||||
|
||||
y_limits = (min(min(y1_1), min(y2_1), min(y3_1), min(y4_1), min(y5_1), min(y6)),
|
||||
max(max(y1_1), max(y2_1), max(y3_1), max(y4_1), max(y5_1), max(y6)))
|
||||
|
||||
for i, (y1, y2) in enumerate([(y1_1, y1_2), (y2_1, y2_2), (y3_1, y3_2),
|
||||
(y4_1, y4_2), (y5_1, y5_2), (y6, None)]):
|
||||
plt.subplot(2, 3, i + 1)
|
||||
plt.plot(x, y1, label='原始信号')
|
||||
if y2 is not None:
|
||||
plt.plot(x, y2, label='拟合信号', linestyle='--')
|
||||
plt.title(f'({"abcdef"[i]})波数k={i + 1 if i < 5 else "滤波"}')
|
||||
plt.xlabel('Cycles', labelpad=10)
|
||||
plt.ylabel('温度 (°C)', labelpad=10)
|
||||
plt.legend()
|
||||
plt.xticks(x) # 设置横坐标为整数
|
||||
plt.ylim(y_limits) # 设置统一纵坐标范围
|
||||
plt.tight_layout()
|
||||
|
||||
plt.subplots_adjust(top=0.8, bottom=0.2, left=0.1,
|
||||
right=0.8, hspace=0.3, wspace=0.2)
|
||||
plt.show()
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------------------------
|
||||
# 6-2 示例的高度滤波处理--不同循环周期 曲线图
|
||||
day_fft_ifft_modes = ["原始信号", "fft", "ifft", "滤波", "比较"]
|
||||
|
||||
|
||||
def day_fft_ifft_plot(cycle_no, ktemp_wn5, ktemp_fft, ktemp_ifft, altitude_min, altitude_max, lamda_low, lamda_high, ktemp_fft_lvbo, mode):
|
||||
|
||||
N = len(ktemp_wn5[cycle_no, :])
|
||||
# 采样时间间隔,其倒数等于采用频率,以1km为标准尺度等同于1s,假设波的速度为1km/s
|
||||
dt = (altitude_max-altitude_min)/(N-1)
|
||||
# 时间序列索引
|
||||
n = np.arange(N)
|
||||
f = n / (N * dt)
|
||||
t = np.round(np.linspace(altitude_min, altitude_max, N), 2)
|
||||
|
||||
# 原始扰动温度
|
||||
x = ktemp_wn5[cycle_no, :]
|
||||
# 傅里叶变换频谱分析
|
||||
y = ktemp_fft[cycle_no, :]
|
||||
# 滤波后的傅里叶变换频谱分析
|
||||
yy = ktemp_fft_lvbo[cycle_no, :]
|
||||
# 傅里叶逆变换后的扰动温度
|
||||
yyy = ktemp_ifft[cycle_no, :]
|
||||
|
||||
if mode not in ["原始信号", "fft", "ifft", "滤波", "比较"]:
|
||||
raise ValueError(
|
||||
"mode 参数应为 '原始信号', 'fft', 'ifft', 'lv bo' 中的一个")
|
||||
|
||||
plt.figure(figsize=(15, 10)) # 调整图形大小
|
||||
# 原始信号的时间序列
|
||||
if mode == "原始信号":
|
||||
plt.plot(t, x)
|
||||
plt.title('(a)原始信号')
|
||||
plt.xlabel('高度 (km)', labelpad=10) # 增加标签间距
|
||||
plt.ylabel('温度 (°C)', labelpad=10) # 增加标签间距
|
||||
# 原始振幅谱
|
||||
if mode == "fft":
|
||||
plt.plot(f, np.abs(y) * 2 / N)
|
||||
plt.title('(b))原始振幅谱')
|
||||
plt.xlabel('频率/Hz', labelpad=10) # 增加标签间距
|
||||
plt.ylabel('振幅', labelpad=10) # 增加标签间距
|
||||
|
||||
# 通过IFFT回到时间域
|
||||
if mode == "ifft":
|
||||
plt.plot(t, yyy)
|
||||
plt.title('(c))傅里叶逆变换')
|
||||
plt.xlabel('高度 (km)', labelpad=10) # 增加标签间距
|
||||
plt.ylabel('温度 (°C)', labelpad=10) # 增加标签间距
|
||||
|
||||
if mode == "滤波":
|
||||
plt.plot(f, np.abs(yy) * 2 / N)
|
||||
plt.title(f'(d)滤除波长 < {lamda_low} km, > {lamda_high} km的波')
|
||||
plt.xlabel('频率/Hz', labelpad=10) # 增加标签间距
|
||||
plt.ylabel('振幅', labelpad=10) # 增加标签间距
|
||||
|
||||
# 调整子图之间的边距
|
||||
|
||||
# 绘制原始信号与滤波后信号
|
||||
if mode == '比较':
|
||||
plt.plot(t, x, label='原始信号')
|
||||
plt.plot(t, yyy, label='滤波后信号', linestyle='--')
|
||||
plt.title('信号比较')
|
||||
plt.xlabel('高度 (km)', labelpad=10) # 增加标签间距
|
||||
plt.ylabel('温度 (°C)', labelpad=10) # 增加标签间距
|
||||
plt.legend()
|
||||
plt.tight_layout() # 调整子图参数以适应图形区域
|
||||
|
||||
|
||||
day_fft_ifft_modes = ["原始信号", "原始振幅谱", "傅里叶逆变换", "滤波后振幅谱", "信号比较"]
|
||||
|
||||
|
||||
def day_fft_ifft_plotg(cycle_no, ktemp_wn5, ktemp_fft, ktemp_ifft, altitude_min, altitude_max, lamda_low, lamda_high, mode):
|
||||
|
||||
N = len(ktemp_wn5[cycle_no, :])
|
||||
dt = (altitude_max - altitude_min) / (N - 1) # 采样时间间隔
|
||||
n = np.arange(N) # 时间序列索引
|
||||
f = n / (N * dt)
|
||||
t = np.round(np.linspace(altitude_min, altitude_max, N), 2)
|
||||
|
||||
x = ktemp_wn5[cycle_no, :] # 原始扰动温度
|
||||
y = ktemp_fft[cycle_no, :] # 傅里叶变换频谱分析
|
||||
yy = ktemp_fft_lvbo[cycle_no, :] # 滤波后的傅里叶变换频谱分析
|
||||
yyy = ktemp_ifft[cycle_no, :] # 傅里叶逆变换后的扰动温度
|
||||
|
||||
plt.figure(figsize=(15, 10)) # 调整图形大小
|
||||
|
||||
# 计算纵坐标范围
|
||||
temp_limits = (min(min(x), min(yyy)), max(max(x), max(yyy)))
|
||||
amp_limits = (0, max(np.max(np.abs(y) * 2 / N),
|
||||
np.max(np.abs(yy) * 2 / N)))
|
||||
|
||||
if mode not in ["原始信号", "原始振幅谱", "傅里叶逆变换", "滤波后振幅谱", "信号比较"]:
|
||||
raise ValueError(
|
||||
"mode 参数应为 '原始信号', '原始振幅谱', '傅里叶逆变换', '滤波后振幅谱', '信号比较' 中的一个")
|
||||
|
||||
# 原始信号的时间序列
|
||||
if mode == "原始信号":
|
||||
plt.plot(t, x)
|
||||
plt.title('(a)原始信号')
|
||||
plt.xlabel('高度 (km)', labelpad=10)
|
||||
plt.ylabel('温度 (°C)', labelpad=10)
|
||||
plt.ylim(temp_limits) # 设置统一纵坐标范围
|
||||
|
||||
# 原始振幅谱
|
||||
if mode == "原始振幅谱":
|
||||
plt.plot(f, np.abs(y) * 2 / N)
|
||||
plt.title('(b)原始振幅谱')
|
||||
plt.xlabel('频率/Hz', labelpad=10)
|
||||
plt.ylabel('振幅', labelpad=10)
|
||||
plt.ylim(amp_limits) # 设置统一纵坐标范围
|
||||
|
||||
# 通过IFFT回到时间域
|
||||
if mode == "傅里叶逆变换":
|
||||
plt.plot(t, yyy)
|
||||
plt.title('(c)傅里叶逆变换')
|
||||
plt.xlabel('高度 (km)', labelpad=10)
|
||||
plt.ylabel('温度 (°C)', labelpad=10)
|
||||
plt.ylim(temp_limits) # 设置统一纵坐标范围
|
||||
|
||||
# 滤波后的振幅谱
|
||||
if mode == "滤波后振幅谱":
|
||||
plt.plot(f, np.abs(yy) * 2 / N)
|
||||
plt.title(f'(d)滤除波长 < {lamda_low} km, > {lamda_high} km的波')
|
||||
plt.xlabel('频率/Hz', labelpad=10)
|
||||
plt.ylabel('振幅', labelpad=10)
|
||||
plt.ylim(amp_limits) # 设置统一纵坐标范围
|
||||
|
||||
if mode == "信号比较":
|
||||
# 绘制原始信号与滤波后信号
|
||||
plt.figure(figsize=(6, 8)) # 调整图形大小
|
||||
plt.plot(x, t, label='原始信号')
|
||||
plt.plot(yyy, t, label='滤波后信号', linestyle='--')
|
||||
plt.title('信号比较')
|
||||
plt.ylabel('高度 (km)', labelpad=10) # 增加标签间距
|
||||
plt.xlabel('温度 (°C)', labelpad=10) # 增加标签间距
|
||||
plt.legend()
|
||||
plt.xlim(temp_limits) # 设置统一纵坐标范围
|
||||
plt.tight_layout()
|
||||
# -----------------------------------------------------------------------------------------------------------------------------
|
||||
# 6-3 示例的按高度的重力波势能变化曲线图
|
||||
|
||||
|
||||
def day_cycle_power_wave_plot(cycle_no, altitude_min, altitude_max, ktemp_Nz, ktemp_Ptz):
|
||||
|
||||
N = len(ktemp_Nz[cycle_no, :])
|
||||
y = np.round(np.linspace(altitude_min, altitude_max, N), 2)
|
||||
x1 = ktemp_Nz[cycle_no, :]
|
||||
x2 = ktemp_Ptz[cycle_no, :]
|
||||
|
||||
plt.figure(figsize=(12, 10)) # 调整图形大小
|
||||
# 原始信号的时间序列
|
||||
plt.subplot(1, 2, 1)
|
||||
plt.plot(x1, y, label='原始信号')
|
||||
plt.title('(a)Nz')
|
||||
plt.xlabel('Nz', labelpad=10) # 增加标签间距
|
||||
plt.ylabel('高度 (km)', labelpad=10) # 增加标签间距
|
||||
|
||||
# 原始信号的时间序列
|
||||
plt.subplot(1, 2, 2)
|
||||
plt.plot(x2, y, label='原始信号')
|
||||
plt.title('(b)Ptz')
|
||||
plt.xlabel('Ptz', labelpad=10) # 增加标签间距
|
||||
plt.ylabel('高度 (km)', labelpad=10) # 增加标签间距
|
||||
|
||||
# 调整子图之间的边距
|
||||
plt.subplots_adjust(top=0.8, bottom=0.2, left=0.1,
|
||||
right=0.8, hspace=0.3, wspace=0.2)
|
||||
plt.tight_layout() # 调整子图参数以适应图形区域
|
||||
|
||||
plt.show()
|
||||
# -----------------------------------------------------------------------------------------------------------------------------
|
||||
# 6-4 按日统计的按周期计算的不同高度的重力波势能 平面图
|
||||
|
||||
|
||||
def day_power_wave_plot(altitude_min, altitude_max, ktemp_Nz, ktemp_Ptz):
|
||||
# 假设 ktemp_Nz 和 ktemp_Ptz 以及 altitude_min, altitude_max 已经定义好
|
||||
x = np.arange(ktemp_Nz.shape[0])
|
||||
y = np.round(np.linspace(altitude_min, altitude_max, ktemp_Nz.shape[1]), 2)
|
||||
|
||||
# 创建一个图形,并指定两个子图
|
||||
fig, axs = plt.subplots(1, 2, figsize=(15, 10))
|
||||
|
||||
# 第一幅图 (a) NZ
|
||||
cax1 = axs[0].imshow(ktemp_Nz.T, aspect='auto', cmap='viridis',
|
||||
origin='lower', extent=[x[0], x[-1], y[0], y[-1]])
|
||||
fig.colorbar(cax1, ax=axs[0]) # 为第一幅图添加颜色条
|
||||
axs[0].set_title('(a) NZ')
|
||||
axs[0].set_ylabel('Height (km)')
|
||||
axs[0].set_xlabel('Cycles')
|
||||
axs[0].set_yticks(np.linspace(30, 90, 7))
|
||||
axs[0].set_yticklabels(np.round(np.linspace(30, 90, 7), 1))
|
||||
axs[0].set_xticks(np.arange(15))
|
||||
axs[0].set_xticklabels(np.arange(15))
|
||||
|
||||
# 第二幅图 (b) PTZ
|
||||
cax2 = axs[1].imshow(ktemp_Ptz.T, aspect='auto', cmap='viridis',
|
||||
origin='lower', extent=[x[0], x[-1], y[0], y[-1]])
|
||||
fig.colorbar(cax2, ax=axs[1]) # 为第二幅图添加颜色条
|
||||
axs[1].set_title('(b) PTZ')
|
||||
axs[1].set_ylabel('Height (km)')
|
||||
axs[1].set_xlabel('Cycles')
|
||||
axs[1].set_yticks(np.linspace(30, 90, 7))
|
||||
axs[1].set_yticklabels(np.round(np.linspace(30, 90, 7), 1))
|
||||
axs[1].set_xticks(np.arange(15))
|
||||
axs[1].set_xticklabels(np.arange(15))
|
||||
|
||||
# 调整子图之间的边距
|
||||
plt.subplots_adjust(top=0.9, bottom=0.1, left=0.05,
|
||||
right=0.95, hspace=0.3, wspace=0.3)
|
||||
plt.tight_layout() # 调整布局以避免重叠
|
||||
plt.show()
|
||||
# -----------------------------------------------------------------------------------------------------------------------------
|
||||
# 6-5 按月统计的每日重力波势能随天变化的图
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------------------------
|
||||
# 6-6 按年统计的每月重力波势能随月变化的图
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------------------------
|
||||
# 7 主程序,运行数据,并输出主要统计分析图 -
|
||||
# -----------------------------------------------------------------------------------------------------------------------------
|
||||
# 7-1 挑选某一天进行运行主要程序
|
||||
def render_saber(file_path, mode, mode_args=None):
|
||||
# day_read=2018113,
|
||||
day_read = 2018094
|
||||
# 初始化某一天、某个纬度、高度范围等参数
|
||||
latitude_min = 30.0
|
||||
latitude_max = 50.0
|
||||
altitude_min = 30.0
|
||||
altitude_max = 90.0
|
||||
lamda_low = 2
|
||||
lamda_high = 15
|
||||
lvboin = True
|
||||
|
||||
(ktemp_cycles, altitude_cycles,
|
||||
ktemp_wn0,
|
||||
ktemp_fit_wn1, ktemp_wn1,
|
||||
ktemp_fit_wn2, ktemp_wn2,
|
||||
ktemp_fit_wn3, ktemp_wn3,
|
||||
ktemp_fit_wn4, ktemp_wn4,
|
||||
ktemp_fit_wn5, ktemp_wn5,
|
||||
ktemp_fft, ktemp_fft_lvbo, ktemp_ifft,
|
||||
ktemp_Nz, ktemp_Ptz) = day_process_main(
|
||||
file_path,
|
||||
# day_read=2018113,
|
||||
day_read,
|
||||
# 初始化某一天、某个纬度、高度范围等参数
|
||||
latitude_min,
|
||||
latitude_max,
|
||||
altitude_min,
|
||||
altitude_max,
|
||||
lamda_low, lamda_high, lvboin)
|
||||
# -----------------------------------------------------------------------------------------------------------------------------
|
||||
# 7-2 挑选某一个月进行运行主要程序
|
||||
(date_time, ktemp_cycles_mon, altitude_cycles_mon, # 某月份 逐日时间 每日不同循环周期温度 不同循环周期高度数据
|
||||
# 距平扰动温度
|
||||
ktemp_wn0_mon,
|
||||
# 波数为1的拟合温度 滤波后的扰动温度
|
||||
ktemp_fit_wn1_mon, ktemp_wn1_mon,
|
||||
# 波数为2的拟合温度 滤波后的扰动温度
|
||||
ktemp_fit_wn2_mon, ktemp_wn2_mon,
|
||||
# 波数为3的拟合温度 滤波后的扰动温度
|
||||
ktemp_fit_wn3_mon, ktemp_wn3_mon,
|
||||
# 波数为4的拟合温度 滤波后的扰动温度
|
||||
ktemp_fit_wn4_mon, ktemp_wn4_mon,
|
||||
# 波数为5的拟合温度 滤波后的扰动温度
|
||||
ktemp_fit_wn5_mon, ktemp_wn5_mon,
|
||||
# 滤波0-5后扰动温度傅里叶频谱分析 滤除波长为2km-15km内后频谱分析 滤波后的扰动温度
|
||||
ktemp_fft_mon, ktemp_fft_lvbo_mon, ktemp_ifft_mon,
|
||||
# 滤波后NZ、PTZ重力波势能指标计算
|
||||
ktemp_Nz_mon, ktemp_Ptz_mon) = mon_process_main(
|
||||
file_path,
|
||||
# 初始化某一天、某个纬度、高度范围等参数
|
||||
latitude_min,
|
||||
latitude_max,
|
||||
altitude_min,
|
||||
altitude_max,
|
||||
lamda_low, lamda_high, lvboin)
|
||||
|
||||
# -----------------------------------------------------------------------------------------------------------------------------
|
||||
# 7-3 绘制不同截过图
|
||||
|
||||
if mode == "day_fit_wave_plot":
|
||||
|
||||
height_no = 1
|
||||
# day_fit_wave_plotg(height_no, ktemp_wn0, ktemp_fit_wn1, ktemp_wn1, ktemp_fit_wn2, ktemp_wn2,
|
||||
# ktemp_fit_wn3, ktemp_wn3, ktemp_fit_wn4, ktemp_wn4, ktemp_fit_wn5, ktemp_wn5)
|
||||
day_fit_wave_plot(height_no, ktemp_wn0, ktemp_fit_wn1, ktemp_wn1, ktemp_fit_wn2, ktemp_wn2,
|
||||
ktemp_fit_wn3, ktemp_wn3, ktemp_fit_wn4, ktemp_wn4, ktemp_fit_wn5, ktemp_wn5, mode_args)
|
||||
if mode == "day_fft_ifft_plot":
|
||||
cycle_no = 1
|
||||
day_fft_ifft_plotg(cycle_no, ktemp_wn5, ktemp_fft, ktemp_ifft,
|
||||
altitude_min, altitude_max, lamda_low, lamda_high, mode_args)
|
||||
if mode == "day_cycle_power_wave_plot":
|
||||
|
||||
day_cycle_power_wave_plot(cycle_no, altitude_min,
|
||||
altitude_max, ktemp_Nz, ktemp_Ptz)
|
||||
if mode == "day_power_wave_plot":
|
||||
day_power_wave_plot(altitude_min, altitude_max, ktemp_Nz, ktemp_Ptz)
|
||||
|
||||
buffer = BytesIO()
|
||||
plt.savefig(buffer, format='png')
|
||||
buffer.seek(0)
|
||||
return buffer
|
||||
|
||||
|
||||
ALL_RENDER_MODES = ["day_fit_wave_plot", "day_fft_ifft_plot",
|
||||
"day_cycle_power_wave_plot", "day_power_wave_plot"]
|
||||
245
saber/process.py
Normal file
245
saber/process.py
Normal file
@ -0,0 +1,245 @@
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
from typing import Dict, List, Optional, Tuple
|
||||
import numpy as np
|
||||
from saber.utils import *
|
||||
|
||||
# lat_range=(latitude_min, latitude_max),
|
||||
# alt_range=(altitude_min, altitude_max),
|
||||
# lambda_range=(lamda_low, lamda_high),
|
||||
|
||||
|
||||
@dataclass
|
||||
class WaveData:
|
||||
cycles: Optional[np.ndarray] = None
|
||||
wn0: Optional[np.ndarray] = None
|
||||
fit_wn: List[Optional[np.ndarray]] = None # 存储wn1-wn5的拟合结果
|
||||
wn: List[Optional[np.ndarray]] = None # 存储wn1-wn5
|
||||
fft: Optional[np.ndarray] = None
|
||||
fft_filtered: Optional[np.ndarray] = None
|
||||
ifft: Optional[np.ndarray] = None
|
||||
Nz: Optional[float] = None
|
||||
Ptz: Optional[float] = None
|
||||
lat_range: Tuple[float, float] = None
|
||||
alt_range: Tuple[float, float] = None
|
||||
lambda_range: Tuple[float, float] = None
|
||||
|
||||
# dump to dict
|
||||
def to_dict(self) -> Dict:
|
||||
"""将数据转换为字典格式,与原代码保持兼容"""
|
||||
result = {
|
||||
"ktemp_cycles_mon_list": self.cycles,
|
||||
"ktemp_wn0_mon_list": self.wn0,
|
||||
"ktemp_fit_wn1_mon_list": self.fit_wn[0],
|
||||
"ktemp_wn1_mon_list": self.wn[0],
|
||||
"ktemp_fit_wn2_mon_list": self.fit_wn[1],
|
||||
"ktemp_wn2_mon_list": self.wn[1],
|
||||
"ktemp_fit_wn3_mon_list": self.fit_wn[2],
|
||||
"ktemp_wn3_mon_list": self.wn[2],
|
||||
"ktemp_fit_wn4_mon_list": self.fit_wn[3],
|
||||
"ktemp_wn4_mon_list": self.wn[3],
|
||||
"ktemp_fit_wn5_mon_list": self.fit_wn[4],
|
||||
"ktemp_wn5_mon_list": self.wn[4],
|
||||
"ktemp_fft_mon_list": self.fft,
|
||||
"ktemp_fft_lvbo_mon_list": self.fft_filtered,
|
||||
"ktemp_ifft_mon_list": self.ifft,
|
||||
"ktemp_Nz_mon_list": self.Nz,
|
||||
"ktemp_Ptz_mon_list": self.Ptz,
|
||||
"lat_range": self.lat_range,
|
||||
"alt_range": self.alt_range,
|
||||
"lambda_range": self.lambda_range
|
||||
}
|
||||
return result
|
||||
|
||||
# load from dict
|
||||
@classmethod
|
||||
def from_dict(cls, data: Dict):
|
||||
wave_data = cls()
|
||||
wave_data.cycles = data["ktemp_cycles_mon_list"]
|
||||
wave_data.wn0 = data["ktemp_wn0_mon_list"]
|
||||
wave_data.fit_wn = [
|
||||
data[f"ktemp_fit_wn{i+1}_mon_list"] for i in range(5)]
|
||||
wave_data.wn = [data[f"ktemp_wn{i+1}_mon_list"] for i in range(5)]
|
||||
wave_data.fft = data["ktemp_fft_mon_list"]
|
||||
wave_data.fft_filtered = data["ktemp_fft_lvbo_mon_list"]
|
||||
wave_data.ifft = data["ktemp_ifft_mon_list"]
|
||||
wave_data.Nz = data["ktemp_Nz_mon_list"]
|
||||
wave_data.Ptz = data["ktemp_Ptz_mon_list"]
|
||||
wave_data.lat_range = data["lat_range"]
|
||||
wave_data.alt_range = data["alt_range"]
|
||||
wave_data.lambda_range = data["lambda_range"]
|
||||
return wave_data
|
||||
|
||||
|
||||
@dataclass
|
||||
class YearlyData:
|
||||
date_times: List[datetime]
|
||||
wave_data: List[WaveData]
|
||||
|
||||
def to_dict(self) -> Dict:
|
||||
"""将数据转换为字典格式,与原代码保持兼容"""
|
||||
result = {
|
||||
"date_time_list": self.date_times,
|
||||
"ktemp_cycles_mon_list": [],
|
||||
"altitude_cycles_mon_list": [],
|
||||
"ktemp_wn0_mon_list": [],
|
||||
"ktemp_fit_wn1_mon_list": [],
|
||||
"ktemp_wn1_mon_list": [],
|
||||
"ktemp_fit_wn2_mon_list": [],
|
||||
"ktemp_wn2_mon_list": [],
|
||||
"ktemp_fit_wn3_mon_list": [],
|
||||
"ktemp_wn3_mon_list": [],
|
||||
"ktemp_fit_wn4_mon_list": [],
|
||||
"ktemp_wn4_mon_list": [],
|
||||
"ktemp_fit_wn5_mon_list": [],
|
||||
"ktemp_wn5_mon_list": [],
|
||||
"ktemp_fft_mon_list": [],
|
||||
"ktemp_fft_lvbo_mon_list": [],
|
||||
"ktemp_ifft_mon_list": [],
|
||||
"ktemp_Nz_mon_list": [],
|
||||
"ktemp_Ptz_mon_list": [],
|
||||
"alt_range": [],
|
||||
}
|
||||
|
||||
for data in self.wave_data:
|
||||
result["ktemp_cycles_mon_list"].append(data.cycles)
|
||||
result["ktemp_wn0_mon_list"].append(data.wn0)
|
||||
for i in range(5):
|
||||
result[f"ktemp_fit_wn{i+1}_mon_list"].append(data.fit_wn[i])
|
||||
result[f"ktemp_wn{i+1}_mon_list"].append(data.wn[i])
|
||||
result["ktemp_fft_mon_list"].append(data.fft)
|
||||
result["ktemp_fft_lvbo_mon_list"].append(data.fft_filtered)
|
||||
result["ktemp_ifft_mon_list"].append(data.ifft)
|
||||
result["ktemp_Nz_mon_list"].append(data.Nz)
|
||||
result["ktemp_Ptz_mon_list"].append(data.Ptz)
|
||||
result["alt_range"].append(data.alt_range)
|
||||
|
||||
return result
|
||||
|
||||
# load from dict
|
||||
@classmethod
|
||||
def from_dict(cls, data: Dict):
|
||||
wave_data = []
|
||||
for i in range(len(data["date_time_list"])):
|
||||
wave_data.append(WaveData())
|
||||
wave_data[-1].cycles = data["ktemp_cycles_mon_list"][i]
|
||||
wave_data[-1].wn0 = data["ktemp_wn0_mon_list"][i]
|
||||
wave_data[-1].fit_wn = [
|
||||
data[f"ktemp_fit_wn{i+1}_mon_list"][i] for i in range(5)]
|
||||
wave_data[-1].wn = [data[f"ktemp_wn{i+1}_mon_list"][i]
|
||||
for i in range(5)]
|
||||
wave_data[-1].fft = data["ktemp_fft_mon_list"][i]
|
||||
wave_data[-1].fft_filtered = data["ktemp_fft_lvbo_mon_list"][i]
|
||||
wave_data[-1].ifft = data["ktemp_ifft_mon_list"][i]
|
||||
wave_data[-1].Nz = data["ktemp_Nz_mon_list"][i]
|
||||
wave_data[-1].Ptz = data["ktemp_Ptz_mon_list"][i]
|
||||
|
||||
return cls(date_times=data["date_time_list"], wave_data=wave_data)
|
||||
|
||||
|
||||
class DataProcessor:
|
||||
def __init__(self, lat_range: Tuple[float, float],
|
||||
alt_range: Tuple[float, float],
|
||||
lambda_range: Tuple[float, float],
|
||||
lvboin: float):
|
||||
self.lat_min, self.lat_max = lat_range
|
||||
self.alt_min, self.alt_max = alt_range
|
||||
self.lambda_low, self.lambda_high = lambda_range
|
||||
self.lvboin = lvboin
|
||||
|
||||
def process_day(self, ncdata, day_read) -> Optional[WaveData]:
|
||||
# 读取数据
|
||||
dataset, tplatitude, tplongitude, tpaltitude, ktemp, time, date, date_time = ncdata
|
||||
df = day_data_read(date, day_read, tplatitude)
|
||||
cycles = data_cycle_identify(df, self.lat_min, self.lat_max)
|
||||
|
||||
if not cycles:
|
||||
return None
|
||||
|
||||
# 生成周期数据
|
||||
ktemp_cycles, alt_cycles = data_cycle_generate(
|
||||
cycles, ktemp, tpaltitude, self.alt_min, self.alt_max)
|
||||
|
||||
if ktemp_cycles is None or alt_cycles is None:
|
||||
return None
|
||||
|
||||
wave_data = WaveData()
|
||||
wave_data.cycles = ktemp_cycles
|
||||
|
||||
# 计算波数分析
|
||||
wave_data.wn0 = ktemp_cycles - np.mean(ktemp_cycles, axis=0)
|
||||
|
||||
wave_data.fit_wn = []
|
||||
wave_data.wn = []
|
||||
temp = wave_data.wn0
|
||||
|
||||
# 循环计算波数1-5
|
||||
for i in range(1, 6):
|
||||
fit, residual = fit_wave(temp, i)
|
||||
wave_data.fit_wn.append(fit)
|
||||
wave_data.wn.append(residual)
|
||||
temp = residual
|
||||
|
||||
# FFT分析
|
||||
wave_data.fft, wave_data.fft_filtered, wave_data.ifft = fft_ifft_wave(
|
||||
wave_data.wn[-1], self.lambda_low, self.lambda_high,
|
||||
self.alt_min, self.alt_max, self.lvboin)
|
||||
|
||||
# 计算指数
|
||||
wave_data.Nz, wave_data.Ptz = power_indices(
|
||||
wave_data.cycles, wave_data.wn[-1], wave_data.ifft,
|
||||
self.alt_min, self.alt_max)
|
||||
|
||||
# ranges for
|
||||
wave_data.lat_range = (self.lat_min, self.lat_max)
|
||||
wave_data.alt_range = (self.alt_min, self.alt_max)
|
||||
wave_data.lambda_range = (self.lambda_low, self.lambda_high)
|
||||
|
||||
return wave_data
|
||||
|
||||
def process_month(self, ncdata: NcData) -> List[Optional[WaveData]]:
|
||||
|
||||
monthly_data = []
|
||||
for day_read in ncdata.date_time:
|
||||
print(f"Processing date: {day_read}")
|
||||
day_result = self.process_day(
|
||||
ncdata, day_read)
|
||||
|
||||
if day_result is not None:
|
||||
monthly_data.append(day_result)
|
||||
|
||||
return monthly_data
|
||||
|
||||
def process_year(self, ncdata: List[NcData]) -> YearlyData:
|
||||
date_times = []
|
||||
wave_data = []
|
||||
for ncfile in ncdata:
|
||||
print(f"Processing file: {ncfile.path}")
|
||||
monthly_results = self.process_month(ncfile)
|
||||
date_times.extend(ncfile.date_time)
|
||||
wave_data.extend(monthly_results)
|
||||
|
||||
return YearlyData(date_times=date_times, wave_data=wave_data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
year = 2018
|
||||
path = "./saber/data/2012/SABER_Temp_O3_April2012_v2.0.nc"
|
||||
# 初始化某一天、某个纬度、高度范围等参数
|
||||
latitude_min = 30.0
|
||||
latitude_max = 40.0
|
||||
altitude_min = 20.0
|
||||
altitude_max = 105.0
|
||||
lamda_low = 2
|
||||
lamda_high = 15
|
||||
lvboin = True
|
||||
processor = DataProcessor(
|
||||
lat_range=(latitude_min, latitude_max),
|
||||
alt_range=(altitude_min, altitude_max),
|
||||
lambda_range=(lamda_low, lamda_high),
|
||||
lvboin=lvboin
|
||||
)
|
||||
|
||||
# 处理月度数据
|
||||
monthly_results = processor.process_month(path)
|
||||
print(monthly_results)
|
||||
296
saber/render.py
Normal file
296
saber/render.py
Normal file
@ -0,0 +1,296 @@
|
||||
from dataclasses import dataclass
|
||||
from typing import Optional, Tuple, List
|
||||
import matplotlib.pyplot as plt
|
||||
import numpy as np
|
||||
from matplotlib.figure import Figure
|
||||
from matplotlib.axes import Axes
|
||||
import matplotlib.dates as mdates
|
||||
|
||||
from saber.process import DataProcessor, WaveData, YearlyData
|
||||
|
||||
|
||||
@dataclass
|
||||
class PlotConfig:
|
||||
"""绘图配置类"""
|
||||
figsize: Tuple[int, int] = (16, 10)
|
||||
dpi: int = 100
|
||||
cmap: str = 'viridis'
|
||||
title_fontsize: int = 12
|
||||
label_fontsize: int = 10
|
||||
legend_fontsize: int = 8
|
||||
tick_fontsize: int = 8
|
||||
|
||||
|
||||
class Renderer:
|
||||
def __init__(self, config: Optional[PlotConfig] = None):
|
||||
self.config = config or PlotConfig()
|
||||
plt.rcParams['font.family'] = 'SimHei' # 设置为黑体(需要你的环境中有该字体)
|
||||
plt.rcParams['axes.unicode_minus'] = False # 解决负号'-'显示为方块的问题
|
||||
|
||||
def _create_figure(self, rows: int, cols: int) -> Tuple[Figure, List[Axes]]:
|
||||
"""创建图形和轴对象"""
|
||||
fig, axes = plt.subplots(
|
||||
rows, cols, figsize=self.config.figsize, dpi=self.config.dpi)
|
||||
return fig, axes.flatten() if isinstance(axes, np.ndarray) else [axes]
|
||||
|
||||
def _setup_subplot(self, ax: Axes, x: np.ndarray, y1: np.ndarray,
|
||||
y2: Optional[np.ndarray], title: str, y_limits: Tuple[float, float]):
|
||||
"""设置单个子图的样式和数据"""
|
||||
ax.plot(x, y1, label='原始信号', linewidth=1.5)
|
||||
if y2 is not None:
|
||||
ax.plot(x, y2, label='拟合信号', linestyle='--', linewidth=1.5)
|
||||
|
||||
ax.set_title(title, fontsize=self.config.title_fontsize)
|
||||
ax.set_xlabel('Cycles', labelpad=10,
|
||||
fontsize=self.config.label_fontsize)
|
||||
ax.set_ylabel('温度 (K)', labelpad=10,
|
||||
fontsize=self.config.label_fontsize)
|
||||
ax.legend(fontsize=self.config.legend_fontsize)
|
||||
ax.tick_params(axis='both', labelsize=self.config.tick_fontsize)
|
||||
ax.set_ylim(y_limits)
|
||||
ax.grid(True, linestyle='--', alpha=0.7)
|
||||
|
||||
def plot_wave_fitting(self, wave_data: WaveData, height_no: int):
|
||||
"""绘制波数拟合结果"""
|
||||
fig, axes = self._create_figure(2, 3)
|
||||
|
||||
# 准备数据
|
||||
N = len(wave_data.wn0[:, height_no])
|
||||
x = np.arange(N)
|
||||
|
||||
data_pairs = [
|
||||
(wave_data.wn0[:, height_no], wave_data.fit_wn[0][:, height_no]),
|
||||
(wave_data.wn[0][:, height_no], wave_data.fit_wn[1][:, height_no]),
|
||||
(wave_data.wn[1][:, height_no], wave_data.fit_wn[2][:, height_no]),
|
||||
(wave_data.wn[2][:, height_no], wave_data.fit_wn[3][:, height_no]),
|
||||
(wave_data.wn[3][:, height_no], wave_data.fit_wn[4][:, height_no]),
|
||||
(wave_data.wn[4][:, height_no], None)
|
||||
]
|
||||
|
||||
# 计算统一的y轴范围
|
||||
all_values = [
|
||||
val for pair in data_pairs for val in pair if val is not None]
|
||||
y_limits = (np.min(all_values), np.max(all_values))
|
||||
|
||||
# 绘制子图
|
||||
for i, (y1, y2) in enumerate(data_pairs):
|
||||
title = f'({"abcdef"[i]})波数k={i + 1 if i < 5 else "滤波1-5后信号"}'
|
||||
self._setup_subplot(axes[i], x, y1, y2, title, y_limits)
|
||||
|
||||
# 调整布局
|
||||
plt.tight_layout()
|
||||
plt.subplots_adjust(top=0.9, bottom=0.1, left=0.1, right=0.9,
|
||||
hspace=0.3, wspace=0.3)
|
||||
|
||||
def day_fft_ifft_plot(self, cycle_no, wave_data: WaveData):
|
||||
|
||||
ktemp_wn5 = wave_data.wn[4]
|
||||
ktemp_fft = wave_data.fft
|
||||
ktemp_fft_lvbo = wave_data.fft_filtered
|
||||
ktemp_ifft = wave_data.ifft
|
||||
|
||||
altitude_min, altitude_max = wave_data.alt_range
|
||||
lamda_low, lamda_high = wave_data.lambda_range
|
||||
|
||||
N = len(ktemp_wn5[cycle_no, :])
|
||||
# 采样时间间隔,其倒数等于采用频率,以1km为标准尺度等同于1s,假设波的速度为1km/s
|
||||
dt = (altitude_max-altitude_min)/(N-1)
|
||||
# 时间序列索引
|
||||
n = np.arange(N)
|
||||
f = n / (N * dt)
|
||||
t = np.round(np.linspace(altitude_min, altitude_max, N), 2)
|
||||
|
||||
# 原始扰动温度
|
||||
x = ktemp_wn5[cycle_no, :]
|
||||
# 傅里叶变换频谱分析
|
||||
y = ktemp_fft[cycle_no, :]
|
||||
# 滤波后的傅里叶变换频谱分析
|
||||
yy = ktemp_fft_lvbo[cycle_no, :]
|
||||
# 傅里叶逆变换后的扰动温度
|
||||
yyy = ktemp_ifft[cycle_no, :]
|
||||
|
||||
plt.figure(figsize=(15, 10)) # 调整图形大小
|
||||
# 原始信号的时间序列
|
||||
plt.subplot(2, 2, 1)
|
||||
plt.plot(t, x)
|
||||
plt.title('(a)原始信号')
|
||||
plt.xlabel('高度 (km)', labelpad=10) # 增加标签间距
|
||||
plt.ylabel('温度 (K)', labelpad=10) # 增加标签间距
|
||||
# 原始振幅谱
|
||||
plt.subplot(2, 2, 2)
|
||||
plt.plot(f, np.abs(y) * 2 / N)
|
||||
plt.title('(b))原始振幅谱')
|
||||
plt.xlabel('频率/Hz', labelpad=10) # 增加标签间距
|
||||
plt.ylabel('振幅', labelpad=10) # 增加标签间距
|
||||
|
||||
# 通过IFFT回到时间域
|
||||
plt.subplot(2, 2, 3)
|
||||
plt.plot(t, yyy)
|
||||
plt.title('(c))傅里叶逆变换')
|
||||
plt.xlabel('高度 (km)', labelpad=10) # 增加标签间距
|
||||
plt.ylabel('温度 (K)', labelpad=10) # 增加标签间距
|
||||
|
||||
# 滤波后的振幅谱
|
||||
plt.subplot(2, 2, 4)
|
||||
plt.plot(f, np.abs(yy) * 2 / N)
|
||||
plt.title(f'(d)滤除波长 < {lamda_low} km, > {lamda_high} km的波')
|
||||
plt.xlabel('频率/Hz', labelpad=10) # 增加标签间距
|
||||
plt.ylabel('振幅', labelpad=10) # 增加标签间距
|
||||
|
||||
# 调整子图之间的边距
|
||||
plt.subplots_adjust(top=0.8, bottom=0.2, left=0.2,
|
||||
right=0.8, hspace=0.3, wspace=0.2)
|
||||
|
||||
def day_cycle_power_wave_plot(self, cycle_no, wave_data: WaveData):
|
||||
ktemp_Nz = wave_data.Nz
|
||||
ktemp_Ptz = wave_data.Ptz
|
||||
|
||||
altitude_min, altitude_max = wave_data.alt_range
|
||||
|
||||
N = len(ktemp_Nz[cycle_no, :])
|
||||
y = np.round(np.linspace(altitude_min, altitude_max, N), 2)
|
||||
x1 = ktemp_Nz[cycle_no, :]
|
||||
x2 = ktemp_Ptz[cycle_no, :]
|
||||
|
||||
plt.figure(figsize=(12, 10)) # 调整图形大小
|
||||
# 原始信号的时间序列
|
||||
plt.subplot(1, 2, 1)
|
||||
plt.plot(x1[::-1], y, label='原始信号')
|
||||
plt.title('(a)Nz')
|
||||
plt.xlabel('Nz', labelpad=10) # 增加标签间距
|
||||
plt.ylabel('高度 (km)', labelpad=10) # 增加标签间距
|
||||
|
||||
# 原始信号的时间序列
|
||||
plt.subplot(1, 2, 2)
|
||||
plt.plot(x2[::-1], y, label='原始信号')
|
||||
plt.title('(b)Ptz')
|
||||
plt.xlabel('Ptz', labelpad=10) # 增加标签间距
|
||||
plt.ylabel('高度 (km)', labelpad=10) # 增加标签间距
|
||||
|
||||
# 调整子图之间的边距
|
||||
plt.subplots_adjust(top=0.8, bottom=0.2, left=0.1,
|
||||
right=0.8, hspace=0.3, wspace=0.2)
|
||||
plt.tight_layout() # 调整子图参数以适应图形区域
|
||||
|
||||
def month_power_wave_plot(self, wave_data: List[WaveData], date_time):
|
||||
ktemp_Nz_mon = [data.Nz for data in wave_data]
|
||||
ktemp_Ptz_mon = [data.Ptz for data in wave_data]
|
||||
|
||||
altitude_min, altitude_max = wave_data[0].alt_range
|
||||
|
||||
if ktemp_Nz_mon and ktemp_Nz_mon[0] is not None:
|
||||
nz_shape = np.array(ktemp_Nz_mon[0]).shape
|
||||
else:
|
||||
nz_shape = (15, 157)
|
||||
if ktemp_Ptz_mon and ktemp_Ptz_mon[0] is not None:
|
||||
ptz_shape = np.array(ktemp_Ptz_mon[0]).shape
|
||||
else:
|
||||
ptz_shape = (15, 157)
|
||||
y = np.round(np.linspace(altitude_min, altitude_max, nz_shape[1]), 2)
|
||||
x = np.arange(len(date_time.data))
|
||||
# 处理 ktemp_Nz_mon
|
||||
ktemp_Nz_plot = np.array([np.mean(day_data if day_data is not None else np.zeros(
|
||||
nz_shape), axis=0) for day_data in ktemp_Nz_mon])
|
||||
ktemp_Ptz_plot = np.array(
|
||||
[np.mean(day_data if day_data is not None else np.zeros(nz_shape), axis=0) for day_data in ktemp_Ptz_mon])
|
||||
# 处理 ktemp_Ptz_mon(以100为界剔除异常值)
|
||||
# ktemp_Ptz_plot = np.array([np.mean(day_data if day_data is not None and np.all(day_data <= 100) else np.zeros(ptz_shape), axis=0) for day_data in ktemp_Ptz_mon])
|
||||
# 创建一个图形,并指定两个子图
|
||||
fig, axs = plt.subplots(1, 2, figsize=(15, 10))
|
||||
|
||||
# 第一幅图 (a) NZ
|
||||
cax1 = axs[0].imshow(ktemp_Nz_plot.T[::-1], aspect='auto', cmap='rainbow', origin='lower',
|
||||
extent=[x[0], x[-1], y[0], y[-1]])
|
||||
fig.colorbar(cax1, ax=axs[0]) # 为第一幅图添加颜色条
|
||||
axs[0].set_title('(a) NZ')
|
||||
axs[0].set_xlabel('Time')
|
||||
axs[0].set_ylabel('Height')
|
||||
axs[0].set_yticks(np.linspace(30, 100, 8))
|
||||
axs[0].set_yticklabels(np.round(np.linspace(30, 100, 8), 1))
|
||||
axs[0].set_xticks(x)
|
||||
axs[0].set_xticklabels(x)
|
||||
|
||||
# 第二幅图 (b) PTZ
|
||||
cax2 = axs[1].imshow(np.log(ktemp_Ptz_plot.T[::-1]), aspect='auto',
|
||||
cmap='rainbow', origin='lower', extent=[x[0], x[-1], y[0], y[-1]])
|
||||
fig.colorbar(cax2, ax=axs[1]) # 为第二幅图添加颜色条
|
||||
axs[1].set_title('(b) PTZ')
|
||||
axs[1].set_xlabel('Time')
|
||||
axs[1].set_ylabel('Height')
|
||||
axs[1].set_yticks(np.linspace(30, 100, 8))
|
||||
axs[1].set_yticklabels(np.round(np.linspace(30, 100, 8), 1))
|
||||
axs[1].set_xticks(x)
|
||||
axs[1].set_xticklabels(x)
|
||||
|
||||
# 调整子图之间的边距
|
||||
plt.subplots_adjust(top=0.9, bottom=0.1, left=0.05,
|
||||
right=0.95, hspace=0.3, wspace=0.3)
|
||||
plt.tight_layout() # 调整布局以避免重叠
|
||||
|
||||
def year_power_wave_plot(self, year_wave: YearlyData):
|
||||
# 假设我们已经从process_yearly_data函数中获取了一年的Nz和Ptz数据
|
||||
results = year_wave.to_dict()
|
||||
altitude_min, altitude_max = results["alt_range"][0]
|
||||
|
||||
ktemp_Nz_mon_list = results["ktemp_Nz_mon_list"]
|
||||
ktemp_Ptz_mon_list = results["ktemp_Ptz_mon_list"]
|
||||
ktemp_Ptz_mon_list.pop(0)
|
||||
ktemp_Nz_mon_list.pop(0)
|
||||
|
||||
# 准备日期数据,这里假设date_time_list是一年中的所有日期
|
||||
date_time_list = results["date_time_list"]
|
||||
date_time_list.pop(0)
|
||||
# 将日期转换为matplotlib可以理解的数字格式
|
||||
date_nums = mdates.date2num(date_time_list)
|
||||
|
||||
# 获取date_time_list长度作为横坐标新的依据
|
||||
x_ticks_length = len(date_time_list)
|
||||
x_ticks = np.arange(0, x_ticks_length, 30)
|
||||
x_labels = [date_time_list[i] if i < len(
|
||||
date_time_list) else "" for i in x_ticks]
|
||||
|
||||
# 准备高度数据
|
||||
# 假设高度数据有157个点
|
||||
y = np.round(np.linspace(altitude_min, altitude_max, 157), 2)
|
||||
|
||||
# 创建一个图形,并指定两个子图
|
||||
fig, axs = plt.subplots(1, 2, figsize=(20, 10))
|
||||
|
||||
# 处理 ktemp_Nz_mon
|
||||
ktemp_Nz_plot = np.array(
|
||||
[np.mean(day_data if day_data is not None else np.zeros((15, 157)), axis=0) for day_data in ktemp_Nz_mon_list])
|
||||
# 处理 ktemp_Ptz_mon
|
||||
ktemp_Ptz_plot = np.array(
|
||||
[np.mean(day_data if day_data is not None else np.zeros((15, 157)), axis=0) for day_data in ktemp_Ptz_mon_list])
|
||||
# ktemp_Ptz_plot = np.array(
|
||||
# [np.mean(day_data if day_data is not None and np.all(day_data <= 100) else np.zeros((15, 157)), axis=0) for
|
||||
# day_data in ktemp_Ptz_mon_list])
|
||||
|
||||
# 第一幅图 (a) NZ
|
||||
cax1 = axs[0].imshow(ktemp_Nz_plot.T[::-1], aspect='auto', cmap='rainbow', origin='lower',
|
||||
extent=[0, x_ticks_length - 1, y[0], y[-1]])
|
||||
fig.colorbar(cax1, ax=axs[0]) # 为第一幅图添加颜色条
|
||||
axs[0].set_title('(a) NZ')
|
||||
axs[0].set_xlabel('Time')
|
||||
axs[0].set_ylabel('Height')
|
||||
axs[0].set_yticks(np.linspace(30, 100, 8))
|
||||
axs[0].set_yticklabels(np.round(np.linspace(30, 100, 8), 1))
|
||||
axs[0].set_xticks(x_ticks) # 设置新的横坐标刻度
|
||||
axs[0].set_xticklabels(x_labels, rotation=45)
|
||||
|
||||
# 第二幅图 (b) PTZ
|
||||
cax2 = axs[1].imshow(np.log(ktemp_Ptz_plot.T[::-1]), aspect='auto', cmap='rainbow', origin='lower',
|
||||
extent=[0, x_ticks_length - 1, y[0], y[-1]])
|
||||
fig.colorbar(cax2, ax=axs[1]) # 为第二幅图添加颜色条
|
||||
axs[1].set_title('(b) PTZ')
|
||||
axs[1].set_xlabel('Time')
|
||||
axs[1].set_ylabel('Height')
|
||||
axs[1].set_yticks(np.linspace(30, 100, 8))
|
||||
axs[1].set_yticklabels(np.round(np.linspace(30, 100, 8), 1))
|
||||
axs[1].set_xticks(x_ticks) # 设置新的横坐标刻度
|
||||
axs[1].set_xticklabels(x_labels, rotation=45)
|
||||
|
||||
# 调整子图之间的边距
|
||||
plt.subplots_adjust(top=0.9, bottom=0.1, left=0.05,
|
||||
right=0.95, hspace=0.3, wspace=0.3)
|
||||
plt.tight_layout() # 调整布局以避免重叠
|
||||
278
saber/utils.py
Normal file
278
saber/utils.py
Normal file
@ -0,0 +1,278 @@
|
||||
from dataclasses import dataclass
|
||||
from os import path
|
||||
import numpy as np
|
||||
import netCDF4 as nc
|
||||
import pandas as pd
|
||||
from scipy.optimize import curve_fit
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
# 5---同周期下不同高度数据的BT_z背景位等指标计算 -
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
# ----------------------------------------------------------------------------------
|
||||
|
||||
|
||||
def power_indices(ktemp_cycles, ktemp_wn5, ktemp_ifft, altitude_min, altitude_max):
|
||||
|
||||
# 定义Brunt-Väisälä频率计算函数
|
||||
def brunt_vaisala_frequency(g, BT_z, c_p, height):
|
||||
# 计算位温随高度的变化率
|
||||
dBT_z_dz = np.gradient(BT_z, height)
|
||||
# 计算 Brunt-Väisälä 频率
|
||||
return np.sqrt((g / BT_z) * ((g / c_p) + dBT_z_dz))
|
||||
|
||||
# 定义势能计算函数
|
||||
def calculate_gravitational_potential_energy(g, BT_z, N_z, PT_z):
|
||||
return 0.5 * ((g / N_z) ** 2) * ((PT_z / BT_z) ** 2)
|
||||
|
||||
# 重力加速度
|
||||
g = 9.81
|
||||
# 空气比热容
|
||||
c_p = 1004.5
|
||||
|
||||
height = np.linspace(altitude_min, altitude_max,
|
||||
ktemp_cycles.shape[1]) * 1000 # 高度
|
||||
background = ktemp_cycles - ktemp_wn5
|
||||
|
||||
# 初始化结果矩阵
|
||||
N_z_matrix = []
|
||||
# 初始化结果矩阵
|
||||
PT_z_matrix = []
|
||||
|
||||
# 循环处理background和filtered_perturbation所有行
|
||||
for i in range(background.shape[0]):
|
||||
BT_z = np.array(background[i])
|
||||
# 滤波后的扰动
|
||||
PT_z = np.array(ktemp_ifft[i])
|
||||
|
||||
# 调用Brunt-Väisälä频率函数
|
||||
N_z = brunt_vaisala_frequency(g, BT_z, c_p, height)
|
||||
PT_z = calculate_gravitational_potential_energy(
|
||||
g, BT_z, N_z, PT_z) # 调用势能函数
|
||||
N_z_matrix.append(N_z)
|
||||
PT_z_matrix.append(PT_z)
|
||||
|
||||
ktemp_Nz = np.vstack(N_z_matrix)
|
||||
ktemp_Ptz = np.vstack(PT_z_matrix)
|
||||
return ktemp_Nz, ktemp_Ptz
|
||||
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
# 1---打开文件并读取不同变量数据 -
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
@dataclass
|
||||
class NcData:
|
||||
dataset: nc.Dataset
|
||||
tplatitude: np.ndarray
|
||||
tplongitude: np.ndarray
|
||||
tpaltitude: np.ndarray
|
||||
ktemp: np.ndarray
|
||||
time: np.ndarray
|
||||
date: np.ndarray
|
||||
date_time: np.ndarray
|
||||
|
||||
path: str = None
|
||||
|
||||
# 兼容旧代码,老的解构方式也能用
|
||||
def __iter__(self):
|
||||
return iter([self.dataset, self.tplatitude, self.tplongitude, self.tpaltitude, self.ktemp, self.time, self.date, self.date_time])
|
||||
|
||||
|
||||
def data_nc_load(file_path):
|
||||
|
||||
dataset = nc.Dataset(file_path, 'r')
|
||||
|
||||
# 纬度数据,二维数组形状为(42820,379) 42820为事件,379则为不同高度
|
||||
tplatitude = dataset.variables['tplatitude'][:, :]
|
||||
tplongitude = dataset.variables['tplongitude'][:, :] # 经度数据
|
||||
tpaltitude = dataset.variables['tpaltitude'][:, :] # 高度,二维数组形状为(42820,379)
|
||||
time = dataset.variables['time'][:, :] # 二维数组形状为(42820,379)
|
||||
date = dataset.variables['date'][:]
|
||||
date_time = np.unique(date) # 输出数据时间信息
|
||||
ktemp = dataset.variables['ktemp'][:, :] # 温度数据,二维数组形状为(42820,379)
|
||||
|
||||
return NcData(dataset, tplatitude, tplongitude, tpaltitude, ktemp, time, date, date_time, path=file_path)
|
||||
# return dataset, tplatitude, tplongitude, tpaltitude, ktemp, time, date, date_time
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
# 2---筛选某一天、某个纬度和高度范围15个不同cycle的温度数据 -
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
# 2-1 读取某一天的所有事件及其对应的纬度数据
|
||||
|
||||
|
||||
def day_data_read(date, day_read, tplatitude):
|
||||
|
||||
events = np.where(date == day_read)[0] # 读取筛选天的事件编号 4294-5714位置,从0开始编号
|
||||
time_events = date[date == day_read] # 读取筛选天的事件编号 4294-5714位置,从0开始编号
|
||||
latitudes = tplatitude[events, 189] # 输出每个事件中间位置 即第189个经纬度
|
||||
df = pd.DataFrame([ # 创建一个包含事件编号、纬度的列表,共1421个事件
|
||||
{'time': time, 'event': event, 'latitude': lat}
|
||||
for time, event, lat in zip(time_events, events, latitudes)])
|
||||
|
||||
# print(df.head()) # 打印前几行数据以检查
|
||||
|
||||
return df
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
# 2-2 将事件按照卫星轨迹周期进行输出处理,并输出落在纬度范围内的每个周期的事件的行号
|
||||
|
||||
|
||||
def data_cycle_identify(df, latitude_min, latitude_max):
|
||||
|
||||
cycles = [] # 存储每个周期的事件编号列表
|
||||
# 存储当前周期的事件编号
|
||||
current_cycle_events = []
|
||||
prev_latitude = None
|
||||
|
||||
# 遍历DataFrame中的每一行以识别周期和筛选事件
|
||||
for index, row in df.iterrows():
|
||||
current_event = int(row['event'])
|
||||
current_latitude = row['latitude']
|
||||
|
||||
if prev_latitude is not None and prev_latitude < 0 and current_latitude >= 0: # 检查是否是新周期的开始(纬度从负变正,且首次变正)
|
||||
# 重置当前周期的事件编号列表
|
||||
current_cycle_events = []
|
||||
|
||||
if latitude_min <= current_latitude <= latitude_max: # 如果事件的纬度在指定范围内,添加到当前周期的事件编号列表
|
||||
current_cycle_events.append(current_event)
|
||||
|
||||
if prev_latitude is not None and prev_latitude >= 0 and current_latitude < 0: # 检查是否是周期的结束(纬度从正变负)
|
||||
|
||||
# 添加当前周期的事件编号列表到周期列表
|
||||
if current_cycle_events: # 确保当前周期有事件编号
|
||||
cycles.append(current_cycle_events)
|
||||
current_cycle_events = [] # 重置当前周期的事件编号列表
|
||||
prev_latitude = current_latitude
|
||||
|
||||
if current_cycle_events: # 处理最后一个周期,如果存在的话
|
||||
cycles.append(current_cycle_events)
|
||||
|
||||
print(f"一天周期为 {len(cycles)}")
|
||||
for cycle_index, cycle in enumerate(cycles, start=0):
|
||||
# 屏幕显示每个循环周期的事件
|
||||
print(f"周期 {cycle_index} 包含事件个数: {len(cycle)} 具体事件为: {cycle} ")
|
||||
|
||||
return cycles
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
# 2-3---按照循环周期合并同周期数据,并输出处理后的温度数据、对应的高度数据
|
||||
|
||||
|
||||
def data_cycle_generate(cycles, ktemp, tpaltitude, altitude_min, altitude_max):
|
||||
if not cycles: # 如果周期列表为空,跳过当前迭代
|
||||
return None, None
|
||||
|
||||
ktemp_cycles = [] # 初始化列表存储每个周期的温度
|
||||
altitude_cycles = [] # 初始化每个循环周期的高度数据
|
||||
for event in cycles:
|
||||
ktemp_cycles_events = np.array(ktemp[event, :]) # 获取每个周期各个事件的ktemp数据
|
||||
ktemp_cycles_events[
|
||||
np.logical_or(ktemp_cycles_events == -999, ktemp_cycles_events > 999)] = np.nan # 缺失值处理,避免影响结果
|
||||
ktemp_cycles_mean = np.nanmean(
|
||||
ktemp_cycles_events, axis=0) # 对所有周期的 ktemp 数据取均值
|
||||
|
||||
altitude_cycles_mean = tpaltitude[event[0], :] # 使用第一个的高度来表征所有的
|
||||
altitude_indices = np.where((altitude_cycles_mean >= altitude_min) & (
|
||||
altitude_cycles_mean <= altitude_max))[0]
|
||||
|
||||
ktemp_cycles.append(np.array(ktemp_cycles_mean[altitude_indices]))
|
||||
altitude_cycles.append(
|
||||
np.array(altitude_cycles_mean[altitude_indices]))
|
||||
|
||||
min_length = 157 # min(len(arr) for arr in ktemp_cycles) # 找到最短列表的长度
|
||||
ktemp_cycles = np.vstack([arr[:min_length]
|
||||
for arr in ktemp_cycles]) # 创建新的列表,将每个子列表截断为最短长度
|
||||
altitude_cycles = np.vstack([arr[:min_length] for arr in altitude_cycles])
|
||||
|
||||
return ktemp_cycles, altitude_cycles
|
||||
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
# 4---高度相同下不同循环周期数据的波拟合和滤波处理 -
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# 对输入数据按照行(即纬向)进行波数为k的滤波,数据为15*157
|
||||
def fit_wave(ktemp_wn0, k):
|
||||
|
||||
wave_fit = []
|
||||
|
||||
def single_harmonic(x, A, phi, C, k):
|
||||
return A * np.sin(2 * np.pi / (15/k) * x + phi) + C
|
||||
|
||||
# 数据转置并对每行进行操作,按照同高度数据进行处理
|
||||
for rtemp in ktemp_wn0.T:
|
||||
# 为当前高度层创建索引数组
|
||||
indices = np.arange(rtemp.size)
|
||||
def fit_temp(x, A, phi, C): return single_harmonic(
|
||||
x, A, phi, C, k) # 定义只拟合 A, phi, C 的 lambda 函数,k 固定
|
||||
params, params_covariance = curve_fit(
|
||||
fit_temp, indices, rtemp) # 使用 curve_fit 进行拟合
|
||||
# 提取拟合参数 A, phi, C
|
||||
A, phi, C = params
|
||||
# 使用拟合参数计算 wn3
|
||||
rtemp1 = single_harmonic(indices, A, phi, C, k)
|
||||
# 存储拟合参数和拟合曲线
|
||||
wave_fit.append(np.array(rtemp1))
|
||||
wave_fit = np.vstack(wave_fit)
|
||||
|
||||
wave_fit = wave_fit.T
|
||||
wave_wn = ktemp_wn0-wave_fit
|
||||
|
||||
return wave_fit, wave_wn
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
# 4---同周期下不同高度数据的波拟合和滤波处理 -
|
||||
# ----------------------------------------------------------------------------------------------------------------------------
|
||||
# 对输入数据按照列(即纬向)进行滤波,滤除波长2和15km以内的波, 数据为15*157
|
||||
def fft_ifft_wave(ktemp_wn5, lamda_low, lamda_high, altitude_min, altitude_max, lvboin):
|
||||
|
||||
ktemp_fft = []
|
||||
ktemp_ifft = []
|
||||
ktemp_fft_lvbo = []
|
||||
|
||||
for rtemp in ktemp_wn5:
|
||||
# 采样点数或长度
|
||||
N = len(rtemp)
|
||||
# 采样时间间隔,其倒数等于采用频率,以1km为标准尺度等同于1s,假设波的速度为1km/s
|
||||
dt = (altitude_max-altitude_min)/(N-1)
|
||||
# 时间序列索引
|
||||
n = np.arange(N)
|
||||
# # t = altitude_min + n * dt # 时间向量
|
||||
# t = np.round(np.linspace(altitude_min, altitude_max, N),2)
|
||||
# 频率索引向量
|
||||
f = n / (N * dt)
|
||||
# 对输入信号进行傅里叶变换
|
||||
y = np.fft.fft(rtemp)
|
||||
|
||||
# 定义波长滤波范围(以频率计算) # 高频截止频率
|
||||
f_low = 2*np.pi / lamda_high
|
||||
f_high = 2*np.pi / lamda_low
|
||||
|
||||
# f_low = 1 / lamda_high # 定义波长滤波范围(以频率计算) # 高频截止频率
|
||||
# f_high = 1 / lamda_low # 低频截止频率
|
||||
|
||||
# 创建滤波后的频域信号
|
||||
yy = y.copy()
|
||||
|
||||
# 使用逻辑索引过滤特定频段(未确定)
|
||||
if lvboin:
|
||||
freq_filter = (f > f_low) & (f < f_high) # 创建逻辑掩码
|
||||
else:
|
||||
freq_filter = (f < f_low) | (f > f_high) # 创建逻辑掩码
|
||||
|
||||
yy[freq_filter] = 0 # 过滤掉指定频段
|
||||
yy_ifft = np.real(np.fft.ifft(yy))
|
||||
|
||||
ktemp_fft.append(y)
|
||||
# 存储拟合参数和拟合曲线
|
||||
ktemp_ifft.append(np.array(yy_ifft))
|
||||
ktemp_fft_lvbo.append(yy)
|
||||
|
||||
ktemp_fft = np.vstack(ktemp_fft)
|
||||
ktemp_ifft = np.vstack(ktemp_ifft)
|
||||
ktemp_fft_lvbo = np.vstack(ktemp_fft_lvbo)
|
||||
|
||||
return ktemp_fft, ktemp_fft_lvbo, ktemp_ifft
|
||||
878
tidi/plot.py
Normal file
878
tidi/plot.py
Normal file
@ -0,0 +1,878 @@
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
from scipy.io import loadmat
|
||||
from scipy.optimize import curve_fit
|
||||
import matplotlib.pyplot as plt
|
||||
import seaborn as sns
|
||||
# ---------------------------------------------------------------------------------------
|
||||
# -----vzonal----------------------------------------------------------------------------
|
||||
|
||||
|
||||
def process_vzonal_day(day, year, ):
|
||||
try:
|
||||
# 读取数据
|
||||
height_data = loadmat(rf"./tidi/data\{year}\{day:03d}_Height.mat")
|
||||
lat_data = loadmat(rf"./tidi/data\{year}\{day:03d}_Lat.mat")
|
||||
lon_data = loadmat(rf"./tidi/data\{year}\{day:03d}_Lon.mat")
|
||||
vmeridional_data = loadmat(
|
||||
rf"./tidi/data\{year}\{day:03d}_VMerdional.mat")
|
||||
vzonal_data = loadmat(rf"./tidi/data\{year}\{day:03d}_Vzonal.mat")
|
||||
|
||||
# 将数据转换为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'])
|
||||
# 将经纬度拼接为两列并添加到对应的DataFrame中
|
||||
lon_lat_df = pd.concat([lon_df, lat_df], axis=1)
|
||||
lon_lat_df.columns = ['Longitude', 'Latitude']
|
||||
# 筛选出10到30度纬度范围的数据
|
||||
lat_filter = (lat_df.values >= 0) & (lat_df.values <= 20)
|
||||
# 使用纬度范围过滤数据
|
||||
vmeridional_filtered = vmeridional_df.iloc[:, lat_filter.flatten()]
|
||||
vzonal_filtered = vzonal_df.iloc[:, lat_filter.flatten()]
|
||||
lon_lat_filtered = lon_lat_df.iloc[lat_filter.flatten(), :]
|
||||
# 接着对lon_lat_filtered的经度进行分组,0到360度每30度一个区间
|
||||
bins = range(0, 361, 30)
|
||||
group_labels = [f"{i}-{i + 29}" for i in range(0, 360, 30)]
|
||||
|
||||
lon_lat_filtered['Longitude_Group'] = pd.cut(
|
||||
lon_lat_filtered['Longitude'], bins=bins, labels=group_labels)
|
||||
|
||||
# 获取所有唯一的经度分组标签并按照数值顺序排序
|
||||
unique_groups = sorted(lon_lat_filtered['Longitude_Group'].unique(
|
||||
), key=lambda x: int(x.split('-')[0]))
|
||||
# 按照经度分组获取每个区间对应的vzonal_filtered、vmeridional_filtered数据
|
||||
grouped_data = {}
|
||||
insufficient_data_count = 0 # 用于计数数据不足的组数
|
||||
|
||||
for group in unique_groups:
|
||||
mask = lon_lat_filtered['Longitude_Group'] == group
|
||||
grouped_data[group] = {
|
||||
'vzonal_filtered': vzonal_filtered.loc[:, mask],
|
||||
'vmeridional_filtered': vmeridional_filtered.loc[:, mask],
|
||||
'lon_lat_filtered': lon_lat_filtered.loc[mask]
|
||||
}
|
||||
|
||||
# 计算有效值数量
|
||||
vzonal_count = grouped_data[group]['vzonal_filtered'].notna(
|
||||
).sum().sum()
|
||||
vmeridional_count = grouped_data[group]['vmeridional_filtered'].notna(
|
||||
).sum().sum()
|
||||
|
||||
if vzonal_count <= 20 or vmeridional_count <= 20:
|
||||
insufficient_data_count += 1
|
||||
|
||||
# 如果超过6组数据不足,则抛出错误
|
||||
if insufficient_data_count > 6:
|
||||
expected_length = 21
|
||||
return pd.Series(np.nan, index=range(expected_length))
|
||||
|
||||
# 如果代码运行到这里,说明所有分组的数据量都足够或者不足的组数不超过6
|
||||
print("所有分组的数据量都足够")
|
||||
# -----------计算w0------------------------------------------------------------------------------------------
|
||||
# 定义期望的12个区间的分组标签
|
||||
expected_groups = [f"{i}-{i + 29}" for i in range(0, 360, 30)]
|
||||
# 初始化一个空DataFrame来存储所有区间的均值廓线,列名设置为期望的分组标签
|
||||
W0_profiles_df = pd.DataFrame(columns=expected_groups)
|
||||
|
||||
# 遍历grouped_data字典中的每个组
|
||||
for group, data in grouped_data.items():
|
||||
# 提取当前组的vzonal_filtered数据
|
||||
vzonal_filtered = data['vzonal_filtered']
|
||||
# 计算有效数据的均值廓线,跳过NaN值
|
||||
mean_profile = vzonal_filtered.mean(axis=1, skipna=True)
|
||||
# 将当前组的均值廓线作为一列添加到W0_profiles_df DataFrame中
|
||||
W0_profiles_df[group] = mean_profile
|
||||
|
||||
# 检查并填充缺失的区间列,将缺失的列添加并填充为NaN
|
||||
for group in expected_groups:
|
||||
if group not in W0_profiles_df.columns:
|
||||
W0_profiles_df[group] = pd.Series(
|
||||
[float('NaN')] * len(W0_profiles_df))
|
||||
|
||||
# 打印拼接后的DataFrame以验证
|
||||
print("Concatenated mean profiles for all longitude groups:\n", W0_profiles_df)
|
||||
# 计算每个高度的均值
|
||||
height_mean_profiles = W0_profiles_df.mean(axis=1)
|
||||
# 将每个高度的均值作为新的一行添加到DataFrame中,All_Heights_Mean就是wn0
|
||||
W0_profiles_df['All_Heights_Mean'] = height_mean_profiles
|
||||
wn0_df = W0_profiles_df['All_Heights_Mean']
|
||||
# -------计算残余量--------------------------------------------------------------------------------------
|
||||
# 计算每个经度区间的残余值 (即每个区间的值减去该高度的All_Heights_Mean)
|
||||
residuals_df = W0_profiles_df.drop(columns='All_Heights_Mean').subtract(
|
||||
W0_profiles_df['All_Heights_Mean'], axis=0)
|
||||
|
||||
# --------wn1-------------------------
|
||||
def single_harmonic(x, A, phi, C):
|
||||
return A * np.sin(2 * np.pi / 12 * x + phi) + C
|
||||
# 用于存储每个高度拟合后的参数结果
|
||||
fit_results = []
|
||||
for index, row in residuals_df.iterrows():
|
||||
# 检查该行是否存在NaN值,如果有则跳过拟合,直接将参数设为0
|
||||
if row.isnull().any():
|
||||
fit_results.append((0, 0, 0))
|
||||
continue
|
||||
x = np.arange(12) # 对应12个位置作为自变量
|
||||
y = row.values
|
||||
try:
|
||||
# 进行曲线拟合
|
||||
popt, _ = curve_fit(single_harmonic, x, y)
|
||||
fit_results.append(popt)
|
||||
except RuntimeError:
|
||||
# 如果拟合过程出现问题(例如无法收敛等),也将参数设为0
|
||||
fit_results.append((0, 0, 0))
|
||||
# 将拟合结果转换为DataFrame
|
||||
fit_results_df = pd.DataFrame(fit_results, columns=['A', 'phi', 'C'])
|
||||
print(fit_results_df)
|
||||
# 用于存储每个高度的拟合值
|
||||
wn1_values = []
|
||||
for index, row in fit_results_df.iterrows():
|
||||
A, phi, C = row
|
||||
x = np.arange(12) # 同样对应12个位置作为自变量
|
||||
wn1 = single_harmonic(x, A, phi, C)
|
||||
wn1_values.append(wn1)
|
||||
# 将拟合值转换为DataFrame
|
||||
wn1_df = pd.DataFrame(wn1_values, columns=[
|
||||
f'wn1_{i}' for i in range(12)])
|
||||
print(wn1_df)
|
||||
# 如果wn1_df全为0,则跳过下面的计算,直接令该天的day_log_gwresult全部为NaN
|
||||
if (wn1_df == 0).all().all():
|
||||
return pd.Series(np.nan, index=range(21))
|
||||
# ------------计算temp-wn0-wn1---------------------------------------------------------
|
||||
temp_wn0_wn1 = residuals_df.values - wn1_df.values
|
||||
# 将结果转为 DataFrame
|
||||
temp_wn0_wn1_df = pd.DataFrame(
|
||||
temp_wn0_wn1, columns=residuals_df.columns, index=residuals_df.index)
|
||||
|
||||
# -------wn2--------------------------------------------------------------------------
|
||||
def single_harmonic(x, A, phi, C):
|
||||
return A * np.sin(2 * np.pi / 6 * x + phi) + C
|
||||
|
||||
# 用于存储每个高度拟合后的参数结果
|
||||
fit_results2 = []
|
||||
for index, row in temp_wn0_wn1_df.iterrows():
|
||||
# 检查该行是否存在NaN值,如果有则跳过拟合,直接将参数设为0
|
||||
if row.isnull().any():
|
||||
fit_results2.append((0, 0, 0))
|
||||
continue
|
||||
x = np.arange(12) # 对应12个位置作为自变量
|
||||
y = row.values
|
||||
try:
|
||||
# 进行曲线拟合
|
||||
popt, _ = curve_fit(single_harmonic, x, y)
|
||||
fit_results2.append(popt)
|
||||
except RuntimeError:
|
||||
# 如果拟合过程出现问题(例如无法收敛等),也将参数设为0
|
||||
fit_results2.append((0, 0, 0))
|
||||
# 将拟合结果转换为DataFrame
|
||||
fit_results2_df = pd.DataFrame(fit_results2, columns=['A', 'phi', 'C'])
|
||||
print(fit_results2_df)
|
||||
# 用于存储每个高度的拟合值
|
||||
wn2_values = []
|
||||
for index, row in fit_results2_df.iterrows():
|
||||
A, phi, C = row
|
||||
x = np.arange(12) # 同样对应12个位置作为自变量
|
||||
wn2 = single_harmonic(x, A, phi, C)
|
||||
wn2_values.append(wn2)
|
||||
# 将拟合值转换为DataFrame
|
||||
wn2_df = pd.DataFrame(wn2_values, columns=[
|
||||
f'wn2_{i}' for i in range(12)])
|
||||
print(wn2_df)
|
||||
# ---------计算temp-wn0-wn1-wn2------------------------------------------------------
|
||||
temp_wn0_wn1_wn2 = temp_wn0_wn1_df.values - wn2_df.values
|
||||
# 转换为 DataFrame
|
||||
temp_wn0_wn1_wn2_df = pd.DataFrame(
|
||||
temp_wn0_wn1_wn2, columns=temp_wn0_wn1_df.columns)
|
||||
|
||||
# -------wn3-----------------------------------------------------------------------
|
||||
def single_harmonic(x, A, phi, C):
|
||||
return A * np.sin(2 * np.pi / 4 * x + phi) + C
|
||||
|
||||
# 用于存储每个高度拟合后的参数结果
|
||||
fit_results3 = []
|
||||
for index, row in temp_wn0_wn1_wn2_df.iterrows():
|
||||
# 检查该行是否存在NaN值,如果有则跳过拟合,直接将参数设为0
|
||||
if row.isnull().any():
|
||||
fit_results3.append((0, 0, 0))
|
||||
continue
|
||||
x = np.arange(12) # 对应12个位置作为自变量
|
||||
y = row.values
|
||||
try:
|
||||
# 进行曲线拟合
|
||||
popt, _ = curve_fit(single_harmonic, x, y)
|
||||
fit_results3.append(popt)
|
||||
except RuntimeError:
|
||||
# 如果拟合过程出现问题(例如无法收敛等),也将参数设为0
|
||||
fit_results3.append((0, 0, 0))
|
||||
# 将拟合结果转换为DataFrame
|
||||
fit_results3_df = pd.DataFrame(fit_results3, columns=['A', 'phi', 'C'])
|
||||
print(fit_results3_df)
|
||||
# 用于存储每个高度的拟合值
|
||||
wn3_values = []
|
||||
for index, row in fit_results3_df.iterrows():
|
||||
A, phi, C = row
|
||||
x = np.arange(12) # 同样对应12个位置作为自变量
|
||||
wn3 = single_harmonic(x, A, phi, C)
|
||||
wn3_values.append(wn3)
|
||||
# 将拟合值转换为DataFrame
|
||||
wn3_df = pd.DataFrame(wn3_values, columns=[
|
||||
f'wn3_{i}' for i in range(12)])
|
||||
print(wn3_df)
|
||||
# ---------计算temp-wn0-wn1-wn2-wn3------------------------------------------------------
|
||||
temp_wn0_wn1_wn2_wn3 = temp_wn0_wn1_wn2_df.values - wn3_df.values
|
||||
# 转换为 DataFrame
|
||||
temp_wn0_wn1_wn2_wn3_df = pd.DataFrame(
|
||||
temp_wn0_wn1_wn2_wn3, columns=temp_wn0_wn1_wn2_df.columns)
|
||||
# -------wn4 - ----------------------------------------------------------------------
|
||||
|
||||
def single_harmonic(x, A, phi, C):
|
||||
return A * np.sin(2 * np.pi / 3 * x + phi) + C
|
||||
|
||||
# 用于存储每个高度拟合后的参数结果
|
||||
fit_results4 = []
|
||||
for index, row in temp_wn0_wn1_wn2_wn3_df.iterrows():
|
||||
# 检查该行是否存在NaN值,如果有则跳过拟合,直接将参数设为0
|
||||
if row.isnull().any():
|
||||
fit_results4.append((0, 0, 0))
|
||||
continue
|
||||
x = np.arange(12) # 对应12个位置作为自变量
|
||||
y = row.values
|
||||
try:
|
||||
# 进行曲线拟合
|
||||
popt, _ = curve_fit(single_harmonic, x, y)
|
||||
fit_results4.append(popt)
|
||||
except RuntimeError:
|
||||
# 如果拟合过程出现问题(例如无法收敛等),也将参数设为0
|
||||
fit_results4.append((0, 0, 0))
|
||||
|
||||
fit_results4_df = pd.DataFrame(fit_results4, columns=['A', 'phi', 'C'])
|
||||
print(fit_results4_df)
|
||||
# 用于存储每个高度的拟合值
|
||||
wn4_values = []
|
||||
for index, row in fit_results4_df.iterrows():
|
||||
A, phi, C = row
|
||||
x = np.arange(12) # 同样对应12个位置作为自变量
|
||||
wn4 = single_harmonic(x, A, phi, C)
|
||||
wn4_values.append(wn4)
|
||||
# 将拟合值转换为DataFrame
|
||||
wn4_df = pd.DataFrame(wn4_values, columns=[
|
||||
f'wn4_{i}' for i in range(12)])
|
||||
print(wn4_df)
|
||||
# ---------计算temp-wn0-wn1-wn2-wn3------------------------------------------------------
|
||||
temp_wn0_wn1_wn2_wn3_wn4 = temp_wn0_wn1_wn2_wn3_df.values - wn4_df.values
|
||||
# 转换为 DataFrame
|
||||
temp_wn0_wn1_wn2_wn3_wn4_df = pd.DataFrame(
|
||||
temp_wn0_wn1_wn2_wn3_wn4, columns=temp_wn0_wn1_wn2_wn3_df.columns)
|
||||
|
||||
# -------wn5-----------------------------------------------------------------------
|
||||
def single_harmonic(x, A, phi, C):
|
||||
return A * np.sin(2 * np.pi / 2.4 * x + phi) + C
|
||||
|
||||
# 用于存储每个高度拟合后的参数结果
|
||||
fit_results5 = []
|
||||
for index, row in temp_wn0_wn1_wn2_wn3_wn4_df.iterrows():
|
||||
# 检查该行是否存在NaN值,如果有则跳过拟合,直接将参数设为0
|
||||
if row.isnull().any():
|
||||
fit_results5.append((0, 0, 0))
|
||||
continue
|
||||
x = np.arange(12) # 对应12个位置作为自变量
|
||||
y = row.values
|
||||
try:
|
||||
# 进行曲线拟合
|
||||
popt, _ = curve_fit(single_harmonic, x, y)
|
||||
fit_results5.append(popt)
|
||||
except RuntimeError:
|
||||
# 如果拟合过程出现问题(例如无法收敛等),也将参数设为0
|
||||
fit_results5.append((0, 0, 0))
|
||||
# 将拟合结果转换为DataFrame
|
||||
fit_results5_df = pd.DataFrame(fit_results5, columns=['A', 'phi', 'C'])
|
||||
print(fit_results5_df)
|
||||
# 用于存储每个高度的拟合值
|
||||
wn5_values = []
|
||||
for index, row in fit_results5_df.iterrows():
|
||||
A, phi, C = row
|
||||
x = np.arange(12) # 同样对应12个位置作为自变量
|
||||
wn5 = single_harmonic(x, A, phi, C)
|
||||
wn5_values.append(wn5)
|
||||
# 将拟合值转换为DataFrame
|
||||
wn5_df = pd.DataFrame(wn5_values, columns=[
|
||||
f'wn5_{i}' for i in range(12)])
|
||||
print(wn5_df)
|
||||
# ---------计算temp-wn0-wn1-wn2-wn3------------------------------------------------------
|
||||
temp_wn0_wn1_wn2_wn3_wn4_wn5 = temp_wn0_wn1_wn2_wn3_wn4_df.values - wn5_df.values
|
||||
# 转换为 DataFrame
|
||||
temp_wn0_wn1_wn2_wn3_wn4_wn5_df = pd.DataFrame(temp_wn0_wn1_wn2_wn3_wn4_wn5,
|
||||
columns=temp_wn0_wn1_wn2_wn3_wn4_df.columns)
|
||||
|
||||
# ------计算背景温度=wn0+wn1+wn2+wn3+wn4+wn5---------------------------------------------------
|
||||
background = wn5_df.values + wn4_df.values + \
|
||||
wn3_df.values + wn2_df.values + wn1_df.values
|
||||
# wn0只有一列单独处理相加
|
||||
# 使用 np.isnan 和 np.where 来判断是否为 NaN 或 0,避免这些值参与相加
|
||||
for i in range(21):
|
||||
wn0_value = wn0_df.iloc[i]
|
||||
# 只有当 wn0_value 既不是 NaN 也不是 0 时才加到 background 上
|
||||
if not np.isnan(wn0_value) and wn0_value != 0:
|
||||
background[i, :] += wn0_value
|
||||
# 扰动
|
||||
perturbation = temp_wn0_wn1_wn2_wn3_wn4_wn5_df
|
||||
# ---------傅里叶变换----------------------------------------------------------------------
|
||||
# 初始化一个新的DataFrame来保存处理结果
|
||||
result = pd.DataFrame(
|
||||
np.nan, index=perturbation.index, columns=perturbation.columns)
|
||||
# 定义滤波范围
|
||||
lambda_low = 2 # 2 km
|
||||
lambda_high = 15 # 15 km
|
||||
f_low = 2 * np.pi / lambda_high
|
||||
f_high = 2 * np.pi / lambda_low
|
||||
|
||||
# 循环处理perturbation中的每一列
|
||||
for col in perturbation.columns:
|
||||
x = perturbation[col]
|
||||
# 提取有效值
|
||||
valid_values = x.dropna()
|
||||
N = len(valid_values) # 有效值的数量
|
||||
|
||||
# 找到第一个有效值的索引
|
||||
first_valid_index = valid_values.index[0] if not valid_values.index.empty else None
|
||||
height_value = height_df.loc[first_valid_index] if first_valid_index is not None else None
|
||||
|
||||
# 如果有效值为空,则跳过该列
|
||||
if N == 0 or height_value is None:
|
||||
continue
|
||||
|
||||
# 时间序列和频率
|
||||
dt = 0.25
|
||||
n = np.arange(N)
|
||||
t = height_value.values + n * dt
|
||||
f = n / (N * dt)
|
||||
|
||||
# 傅里叶变换
|
||||
y = np.fft.fft(valid_values.values)
|
||||
|
||||
# 频率滤波
|
||||
yy = y.copy()
|
||||
freq_filter = (f < f_low) | (f > f_high)
|
||||
yy[freq_filter] = 0 # 过滤掉指定频段
|
||||
|
||||
# 逆傅里叶变换
|
||||
perturbation_after = np.real(np.fft.ifft(yy))
|
||||
|
||||
# 将处理结果插回到result矩阵中
|
||||
result.loc[valid_values.index, col] = perturbation_after
|
||||
u2 = result ** 2
|
||||
u2 = u2.mean(axis=1)
|
||||
return u2
|
||||
except FileNotFoundError:
|
||||
# 如果文件不存在,返回全NaN的Series
|
||||
expected_length = 21
|
||||
return pd.Series(np.nan, index=range(expected_length))
|
||||
|
||||
|
||||
# -------------------------------------------------------------------------------------------
|
||||
# --------meridional-------------------------------------------------------------------------
|
||||
def process_vmeridional_day(day, year):
|
||||
try:
|
||||
# 读取数据
|
||||
height_data = loadmat(rf"./tidi/data\{year}\{day:03d}_Height.mat")
|
||||
lat_data = loadmat(rf"./tidi/data\{year}\{day:03d}_Lat.mat")
|
||||
lon_data = loadmat(rf"./tidi/data\{year}\{day:03d}_Lon.mat")
|
||||
vmeridional_data = loadmat(
|
||||
rf"./tidi/data\{year}\{day:03d}_VMerdional.mat")
|
||||
vzonal_data = loadmat(rf"./tidi/data\{year}\{day:03d}_Vzonal.mat")
|
||||
|
||||
# 将数据转换为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'])
|
||||
# 将经纬度拼接为两列并添加到对应的DataFrame中
|
||||
lon_lat_df = pd.concat([lon_df, lat_df], axis=1)
|
||||
lon_lat_df.columns = ['Longitude', 'Latitude']
|
||||
# 筛选出10到30度纬度范围的数据
|
||||
lat_filter = (lat_df.values >= 0) & (lat_df.values <= 20)
|
||||
# 使用纬度范围过滤数据
|
||||
vmeridional_filtered = vmeridional_df.iloc[:, lat_filter.flatten()]
|
||||
vzonal_filtered = vzonal_df.iloc[:, lat_filter.flatten()]
|
||||
lon_lat_filtered = lon_lat_df.iloc[lat_filter.flatten(), :]
|
||||
# 接着对lon_lat_filtered的经度进行分组,0到360度每30度一个区间
|
||||
bins = range(0, 361, 30)
|
||||
group_labels = [f"{i}-{i + 29}" for i in range(0, 360, 30)]
|
||||
|
||||
lon_lat_filtered['Longitude_Group'] = pd.cut(
|
||||
lon_lat_filtered['Longitude'], bins=bins, labels=group_labels)
|
||||
|
||||
# 获取所有唯一的经度分组标签并按照数值顺序排序
|
||||
unique_groups = sorted(lon_lat_filtered['Longitude_Group'].unique(
|
||||
), key=lambda x: int(x.split('-')[0]))
|
||||
# 按照经度分组获取每个区间对应的vzonal_filtered、vmeridional_filtered数据
|
||||
grouped_data = {}
|
||||
insufficient_data_count = 0 # 用于计数数据不足的组数
|
||||
|
||||
for group in unique_groups:
|
||||
mask = lon_lat_filtered['Longitude_Group'] == group
|
||||
grouped_data[group] = {
|
||||
'vzonal_filtered': vzonal_filtered.loc[:, mask],
|
||||
'vmeridional_filtered': vmeridional_filtered.loc[:, mask],
|
||||
'lon_lat_filtered': lon_lat_filtered.loc[mask]
|
||||
}
|
||||
|
||||
# 计算有效值数量
|
||||
vzonal_count = grouped_data[group]['vzonal_filtered'].notna(
|
||||
).sum().sum()
|
||||
vmeridional_count = grouped_data[group]['vmeridional_filtered'].notna(
|
||||
).sum().sum()
|
||||
|
||||
if vzonal_count <= 20 or vmeridional_count <= 20:
|
||||
insufficient_data_count += 1
|
||||
|
||||
# 如果超过6组数据不足,则抛出错误
|
||||
if insufficient_data_count > 6:
|
||||
expected_length = 21
|
||||
return pd.Series(np.nan, index=range(expected_length))
|
||||
|
||||
# 如果代码运行到这里,说明所有分组的数据量都足够或者不足的组数不超过6
|
||||
print("所有分组的数据量都足够")
|
||||
# -----------计算w0------------------------------------------------------------------------------------------
|
||||
# 定义期望的12个区间的分组标签
|
||||
expected_groups = [f"{i}-{i + 29}" for i in range(0, 360, 30)]
|
||||
# 初始化一个空DataFrame来存储所有区间的均值廓线,列名设置为期望的分组标签
|
||||
W0_profiles_df = pd.DataFrame(columns=expected_groups)
|
||||
|
||||
# 遍历grouped_data字典中的每个组
|
||||
for group, data in grouped_data.items():
|
||||
# 提取当前组的vzonal_filtered数据
|
||||
vmeridional_filtered = data['vmeridional_filtered']
|
||||
# 计算有效数据的均值廓线,跳过NaN值
|
||||
mean_profile = vmeridional_filtered.mean(axis=1, skipna=True)
|
||||
# 将当前组的均值廓线作为一列添加到W0_profiles_df DataFrame中
|
||||
W0_profiles_df[group] = mean_profile
|
||||
|
||||
# 检查并填充缺失的区间列,将缺失的列添加并填充为NaN
|
||||
for group in expected_groups:
|
||||
if group not in W0_profiles_df.columns:
|
||||
W0_profiles_df[group] = pd.Series(
|
||||
[float('NaN')] * len(W0_profiles_df))
|
||||
|
||||
# 打印拼接后的DataFrame以验证
|
||||
print("Concatenated mean profiles for all longitude groups:\n", W0_profiles_df)
|
||||
# 计算每个高度的均值
|
||||
height_mean_profiles = W0_profiles_df.mean(axis=1)
|
||||
# 将每个高度的均值作为新的一行添加到DataFrame中,All_Heights_Mean就是wn0
|
||||
W0_profiles_df['All_Heights_Mean'] = height_mean_profiles
|
||||
wn0_df = W0_profiles_df['All_Heights_Mean']
|
||||
# -------计算残余量--------------------------------------------------------------------------------------
|
||||
# 计算每个经度区间的残余值 (即每个区间的值减去该高度的All_Heights_Mean)
|
||||
residuals_df = W0_profiles_df.drop(columns='All_Heights_Mean').subtract(
|
||||
W0_profiles_df['All_Heights_Mean'], axis=0)
|
||||
|
||||
# --------wn1-------------------------
|
||||
def single_harmonic(x, A, phi, C):
|
||||
return A * np.sin(2 * np.pi / 12 * x + phi) + C
|
||||
# 用于存储每个高度拟合后的参数结果
|
||||
fit_results = []
|
||||
for index, row in residuals_df.iterrows():
|
||||
# 检查该行是否存在NaN值,如果有则跳过拟合,直接将参数设为0
|
||||
if row.isnull().any():
|
||||
fit_results.append((0, 0, 0))
|
||||
continue
|
||||
x = np.arange(12) # 对应12个位置作为自变量
|
||||
y = row.values
|
||||
try:
|
||||
# 进行曲线拟合
|
||||
popt, _ = curve_fit(single_harmonic, x, y)
|
||||
fit_results.append(popt)
|
||||
except RuntimeError:
|
||||
# 如果拟合过程出现问题(例如无法收敛等),也将参数设为0
|
||||
fit_results.append((0, 0, 0))
|
||||
# 将拟合结果转换为DataFrame
|
||||
fit_results_df = pd.DataFrame(fit_results, columns=['A', 'phi', 'C'])
|
||||
print(fit_results_df)
|
||||
# 用于存储每个高度的拟合值
|
||||
wn1_values = []
|
||||
for index, row in fit_results_df.iterrows():
|
||||
A, phi, C = row
|
||||
x = np.arange(12) # 同样对应12个位置作为自变量
|
||||
wn1 = single_harmonic(x, A, phi, C)
|
||||
wn1_values.append(wn1)
|
||||
# 将拟合值转换为DataFrame
|
||||
wn1_df = pd.DataFrame(wn1_values, columns=[
|
||||
f'wn1_{i}' for i in range(12)])
|
||||
print(wn1_df)
|
||||
# 如果wn1_df全为0,则跳过下面的计算,直接令该天的day_log_gwresult全部为NaN
|
||||
if (wn1_df == 0).all().all():
|
||||
return pd.Series(np.nan, index=range(21))
|
||||
# ------------计算temp-wn0-wn1---------------------------------------------------------
|
||||
temp_wn0_wn1 = residuals_df.values - wn1_df.values
|
||||
# 将结果转为 DataFrame
|
||||
temp_wn0_wn1_df = pd.DataFrame(
|
||||
temp_wn0_wn1, columns=residuals_df.columns, index=residuals_df.index)
|
||||
|
||||
# -------wn2--------------------------------------------------------------------------
|
||||
def single_harmonic(x, A, phi, C):
|
||||
return A * np.sin(2 * np.pi / 6 * x + phi) + C
|
||||
|
||||
# 用于存储每个高度拟合后的参数结果
|
||||
fit_results2 = []
|
||||
for index, row in temp_wn0_wn1_df.iterrows():
|
||||
# 检查该行是否存在NaN值,如果有则跳过拟合,直接将参数设为0
|
||||
if row.isnull().any():
|
||||
fit_results2.append((0, 0, 0))
|
||||
continue
|
||||
x = np.arange(12) # 对应12个位置作为自变量
|
||||
y = row.values
|
||||
try:
|
||||
# 进行曲线拟合
|
||||
popt, _ = curve_fit(single_harmonic, x, y)
|
||||
fit_results2.append(popt)
|
||||
except RuntimeError:
|
||||
# 如果拟合过程出现问题(例如无法收敛等),也将参数设为0
|
||||
fit_results2.append((0, 0, 0))
|
||||
# 将拟合结果转换为DataFrame
|
||||
fit_results2_df = pd.DataFrame(fit_results2, columns=['A', 'phi', 'C'])
|
||||
print(fit_results2_df)
|
||||
# 用于存储每个高度的拟合值
|
||||
wn2_values = []
|
||||
for index, row in fit_results2_df.iterrows():
|
||||
A, phi, C = row
|
||||
x = np.arange(12) # 同样对应12个位置作为自变量
|
||||
wn2 = single_harmonic(x, A, phi, C)
|
||||
wn2_values.append(wn2)
|
||||
# 将拟合值转换为DataFrame
|
||||
wn2_df = pd.DataFrame(wn2_values, columns=[
|
||||
f'wn2_{i}' for i in range(12)])
|
||||
print(wn2_df)
|
||||
# ---------计算temp-wn0-wn1-wn2------------------------------------------------------
|
||||
temp_wn0_wn1_wn2 = temp_wn0_wn1_df.values - wn2_df.values
|
||||
# 转换为 DataFrame
|
||||
temp_wn0_wn1_wn2_df = pd.DataFrame(
|
||||
temp_wn0_wn1_wn2, columns=temp_wn0_wn1_df.columns)
|
||||
|
||||
# -------wn3-----------------------------------------------------------------------
|
||||
def single_harmonic(x, A, phi, C):
|
||||
return A * np.sin(2 * np.pi / 4 * x + phi) + C
|
||||
|
||||
# 用于存储每个高度拟合后的参数结果
|
||||
fit_results3 = []
|
||||
for index, row in temp_wn0_wn1_wn2_df.iterrows():
|
||||
# 检查该行是否存在NaN值,如果有则跳过拟合,直接将参数设为0
|
||||
if row.isnull().any():
|
||||
fit_results3.append((0, 0, 0))
|
||||
continue
|
||||
x = np.arange(12) # 对应12个位置作为自变量
|
||||
y = row.values
|
||||
try:
|
||||
# 进行曲线拟合
|
||||
popt, _ = curve_fit(single_harmonic, x, y)
|
||||
fit_results3.append(popt)
|
||||
except RuntimeError:
|
||||
# 如果拟合过程出现问题(例如无法收敛等),也将参数设为0
|
||||
fit_results3.append((0, 0, 0))
|
||||
# 将拟合结果转换为DataFrame
|
||||
fit_results3_df = pd.DataFrame(fit_results3, columns=['A', 'phi', 'C'])
|
||||
print(fit_results3_df)
|
||||
# 用于存储每个高度的拟合值
|
||||
wn3_values = []
|
||||
for index, row in fit_results3_df.iterrows():
|
||||
A, phi, C = row
|
||||
x = np.arange(12) # 同样对应12个位置作为自变量
|
||||
wn3 = single_harmonic(x, A, phi, C)
|
||||
wn3_values.append(wn3)
|
||||
# 将拟合值转换为DataFrame
|
||||
wn3_df = pd.DataFrame(wn3_values, columns=[
|
||||
f'wn3_{i}' for i in range(12)])
|
||||
print(wn3_df)
|
||||
# ---------计算temp-wn0-wn1-wn2-wn3------------------------------------------------------
|
||||
temp_wn0_wn1_wn2_wn3 = temp_wn0_wn1_wn2_df.values - wn3_df.values
|
||||
# 转换为 DataFrame
|
||||
temp_wn0_wn1_wn2_wn3_df = pd.DataFrame(
|
||||
temp_wn0_wn1_wn2_wn3, columns=temp_wn0_wn1_wn2_df.columns)
|
||||
# -------wn4 - ----------------------------------------------------------------------
|
||||
|
||||
def single_harmonic(x, A, phi, C):
|
||||
return A * np.sin(2 * np.pi / 3 * x + phi) + C
|
||||
|
||||
# 用于存储每个高度拟合后的参数结果
|
||||
fit_results4 = []
|
||||
for index, row in temp_wn0_wn1_wn2_wn3_df.iterrows():
|
||||
# 检查该行是否存在NaN值,如果有则跳过拟合,直接将参数设为0
|
||||
if row.isnull().any():
|
||||
fit_results4.append((0, 0, 0))
|
||||
continue
|
||||
x = np.arange(12) # 对应12个位置作为自变量
|
||||
y = row.values
|
||||
try:
|
||||
# 进行曲线拟合
|
||||
popt, _ = curve_fit(single_harmonic, x, y)
|
||||
fit_results4.append(popt)
|
||||
except RuntimeError:
|
||||
# 如果拟合过程出现问题(例如无法收敛等),也将参数设为0
|
||||
fit_results4.append((0, 0, 0))
|
||||
|
||||
fit_results4_df = pd.DataFrame(fit_results4, columns=['A', 'phi', 'C'])
|
||||
print(fit_results4_df)
|
||||
# 用于存储每个高度的拟合值
|
||||
wn4_values = []
|
||||
for index, row in fit_results4_df.iterrows():
|
||||
A, phi, C = row
|
||||
x = np.arange(12) # 同样对应12个位置作为自变量
|
||||
wn4 = single_harmonic(x, A, phi, C)
|
||||
wn4_values.append(wn4)
|
||||
# 将拟合值转换为DataFrame
|
||||
wn4_df = pd.DataFrame(wn4_values, columns=[
|
||||
f'wn4_{i}' for i in range(12)])
|
||||
print(wn4_df)
|
||||
# ---------计算temp-wn0-wn1-wn2-wn3------------------------------------------------------
|
||||
temp_wn0_wn1_wn2_wn3_wn4 = temp_wn0_wn1_wn2_wn3_df.values - wn4_df.values
|
||||
# 转换为 DataFrame
|
||||
temp_wn0_wn1_wn2_wn3_wn4_df = pd.DataFrame(
|
||||
temp_wn0_wn1_wn2_wn3_wn4, columns=temp_wn0_wn1_wn2_wn3_df.columns)
|
||||
|
||||
# -------wn5-----------------------------------------------------------------------
|
||||
def single_harmonic(x, A, phi, C):
|
||||
return A * np.sin(2 * np.pi / 2.4 * x + phi) + C
|
||||
|
||||
# 用于存储每个高度拟合后的参数结果
|
||||
fit_results5 = []
|
||||
for index, row in temp_wn0_wn1_wn2_wn3_wn4_df.iterrows():
|
||||
# 检查该行是否存在NaN值,如果有则跳过拟合,直接将参数设为0
|
||||
if row.isnull().any():
|
||||
fit_results5.append((0, 0, 0))
|
||||
continue
|
||||
x = np.arange(12) # 对应12个位置作为自变量
|
||||
y = row.values
|
||||
try:
|
||||
# 进行曲线拟合
|
||||
popt, _ = curve_fit(single_harmonic, x, y)
|
||||
fit_results5.append(popt)
|
||||
except RuntimeError:
|
||||
# 如果拟合过程出现问题(例如无法收敛等),也将参数设为0
|
||||
fit_results5.append((0, 0, 0))
|
||||
# 将拟合结果转换为DataFrame
|
||||
fit_results5_df = pd.DataFrame(fit_results5, columns=['A', 'phi', 'C'])
|
||||
print(fit_results5_df)
|
||||
# 用于存储每个高度的拟合值
|
||||
wn5_values = []
|
||||
for index, row in fit_results5_df.iterrows():
|
||||
A, phi, C = row
|
||||
x = np.arange(12) # 同样对应12个位置作为自变量
|
||||
wn5 = single_harmonic(x, A, phi, C)
|
||||
wn5_values.append(wn5)
|
||||
# 将拟合值转换为DataFrame
|
||||
wn5_df = pd.DataFrame(wn5_values, columns=[
|
||||
f'wn5_{i}' for i in range(12)])
|
||||
print(wn5_df)
|
||||
# ---------计算temp-wn0-wn1-wn2-wn3------------------------------------------------------
|
||||
temp_wn0_wn1_wn2_wn3_wn4_wn5 = temp_wn0_wn1_wn2_wn3_wn4_df.values - wn5_df.values
|
||||
# 转换为 DataFrame
|
||||
temp_wn0_wn1_wn2_wn3_wn4_wn5_df = pd.DataFrame(temp_wn0_wn1_wn2_wn3_wn4_wn5,
|
||||
columns=temp_wn0_wn1_wn2_wn3_wn4_df.columns)
|
||||
|
||||
# ------计算背景温度=wn0+wn1+wn2+wn3+wn4+wn5---------------------------------------------------
|
||||
background = wn5_df.values + wn4_df.values + \
|
||||
wn3_df.values + wn2_df.values + wn1_df.values
|
||||
# wn0只有一列单独处理相加
|
||||
# 使用 np.isnan 和 np.where 来判断是否为 NaN 或 0,避免这些值参与相加
|
||||
for i in range(21):
|
||||
wn0_value = wn0_df.iloc[i]
|
||||
# 只有当 wn0_value 既不是 NaN 也不是 0 时才加到 background 上
|
||||
if not np.isnan(wn0_value) and wn0_value != 0:
|
||||
background[i, :] += wn0_value
|
||||
# 扰动
|
||||
perturbation = temp_wn0_wn1_wn2_wn3_wn4_wn5_df
|
||||
# ---------傅里叶变换----------------------------------------------------------------------
|
||||
# 初始化一个新的DataFrame来保存处理结果
|
||||
result = pd.DataFrame(
|
||||
np.nan, index=perturbation.index, columns=perturbation.columns)
|
||||
# 定义滤波范围
|
||||
lambda_low = 2 # 2 km
|
||||
lambda_high = 15 # 15 km
|
||||
f_low = 2 * np.pi / lambda_high
|
||||
f_high = 2 * np.pi / lambda_low
|
||||
|
||||
# 循环处理perturbation中的每一列
|
||||
for col in perturbation.columns:
|
||||
x = perturbation[col]
|
||||
# 提取有效值
|
||||
valid_values = x.dropna()
|
||||
N = len(valid_values) # 有效值的数量
|
||||
|
||||
# 找到第一个有效值的索引
|
||||
first_valid_index = valid_values.index[0] if not valid_values.index.empty else None
|
||||
height_value = height_df.loc[first_valid_index] if first_valid_index is not None else None
|
||||
|
||||
# 如果有效值为空,则跳过该列
|
||||
if N == 0 or height_value is None:
|
||||
continue
|
||||
|
||||
# 时间序列和频率
|
||||
dt = 0.25
|
||||
n = np.arange(N)
|
||||
t = height_value.values + n * dt
|
||||
f = n / (N * dt)
|
||||
|
||||
# 傅里叶变换
|
||||
y = np.fft.fft(valid_values.values)
|
||||
|
||||
# 频率滤波
|
||||
yy = y.copy()
|
||||
freq_filter = (f < f_low) | (f > f_high)
|
||||
yy[freq_filter] = 0 # 过滤掉指定频段
|
||||
|
||||
# 逆傅里叶变换
|
||||
perturbation_after = np.real(np.fft.ifft(yy))
|
||||
|
||||
# 将处理结果插回到result矩阵中
|
||||
result.loc[valid_values.index, col] = perturbation_after
|
||||
v2 = result ** 2
|
||||
v2 = v2.mean(axis=1)
|
||||
return v2
|
||||
except FileNotFoundError:
|
||||
# 如果文件不存在,返回全NaN的Series
|
||||
expected_length = 21
|
||||
return pd.Series(np.nan, index=range(expected_length))
|
||||
# 初始化一个空的DataFrame来存储所有天的结果
|
||||
|
||||
|
||||
# 初始化一个空的DataFrame来存储所有天的结果
|
||||
all_days_vzonal_results = pd.DataFrame()
|
||||
|
||||
# 循环处理每一天的数据
|
||||
for day in range(1, 365):
|
||||
u2 = process_vzonal_day(day, 2019)
|
||||
if u2 is not None:
|
||||
all_days_vzonal_results[rf"{day:02d}"] = u2
|
||||
|
||||
# 将结果按列拼接
|
||||
# all_days_vzonal_results.columns = [f"{day:02d}" for day in range(1, 365)]
|
||||
|
||||
all_days_vmeridional_results = pd.DataFrame()
|
||||
|
||||
# 循环处理每一天的数据
|
||||
for day in range(1, 365):
|
||||
v2 = process_vmeridional_day(day, 2019)
|
||||
if v2 is not None:
|
||||
all_days_vmeridional_results[rf"{day:02d}"] = v2
|
||||
|
||||
# 将结果按列拼接
|
||||
# all_days_vmeridional_results.columns = [f"{day:02d}" for day in range(1, 365)]
|
||||
|
||||
# ---------------------------------------------------------------------------------------------------
|
||||
# --------经纬向风平方和计算动能--------------------------------------------------------------------------------
|
||||
|
||||
# 使用numpy.where来检查两个表格中的对应元素是否都不是NaN
|
||||
sum_df = np.where(
|
||||
pd.notna(all_days_vmeridional_results) & pd.notna(all_days_vzonal_results),
|
||||
all_days_vmeridional_results + all_days_vzonal_results,
|
||||
np.nan
|
||||
)
|
||||
HP = 1/2*all_days_vmeridional_results+1/2*all_days_vzonal_results
|
||||
heights = [70.0, 72.5, 75.0, 77.5, 80.0, 82.5, 85.0, 87.5, 90.0, 92.5,
|
||||
95.0, 97.5, 100.0, 102.5, 105.0, 107.5, 110.0, 112.5, 115.0, 117.5, 120.0]
|
||||
HP.index = heights
|
||||
# # 将 DataFrame 保存为 Excel 文件
|
||||
# HP.to_excel('HP_data.xlsx')
|
||||
# ----------绘年统计图------------------------------------------------------------------------------------------------------------
|
||||
data = HP
|
||||
# 使用 reset_index() 方法将索引变为第一列
|
||||
data = data.reset_index()
|
||||
h = data.iloc[:, 0].copy() # 高度,保留作为纵坐标
|
||||
dates = list(range(1, data.shape[1])) # 日期,作为横坐标
|
||||
data0 = data.iloc[:, 1:].copy() # 绘图数据
|
||||
'''数据处理'''
|
||||
# 反转 h 以确保高度从下往上递增
|
||||
h_reversed = h[::-1].reset_index(drop=True)
|
||||
data0_reversed = data0[::-1].reset_index(drop=True)
|
||||
# 将数值大于20的数据点替换为nan
|
||||
data0_reversed[data0_reversed > 20] = float('nan')
|
||||
# 转换成月份,365天
|
||||
days_in_month = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
|
||||
# 将日期转换为英文月份
|
||||
|
||||
|
||||
def day_to_month(day):
|
||||
# 累积每个月的天数,找到对应的月份
|
||||
cumulative_days = 0
|
||||
for i, days in enumerate(days_in_month):
|
||||
cumulative_days += days
|
||||
if day <= cumulative_days:
|
||||
return f'{["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"][i]}'
|
||||
|
||||
|
||||
months = [day_to_month(day) for day in dates]
|
||||
|
||||
|
||||
'''绘图'''
|
||||
plt.rcParams['font.family'] = 'SimSun' # 宋体
|
||||
plt.rcParams['font.size'] = 12 # 中文字号
|
||||
plt.rcParams['axes.unicode_minus'] = False # 正确显示负号
|
||||
plt.rcParams['font.sans-serif'] = 'Times New Roman' # 新罗马
|
||||
plt.rcParams['axes.labelsize'] = 14 # 坐标轴标签字号
|
||||
plt.rcParams['xtick.labelsize'] = 12 # x轴刻度字号
|
||||
plt.rcParams['ytick.labelsize'] = 12 # y轴刻度字号
|
||||
plt.rcParams['legend.fontsize'] = 16 # 图例字号
|
||||
plt.rcParams['axes.unicode_minus'] = False # 正确显示负号
|
||||
plt.figure(figsize=(10, 6)) # 设置图像大小
|
||||
# 绘制热力图,设置 x 和 y 轴的标签
|
||||
sns.heatmap(data0_reversed, annot=False, cmap='YlGnBu', linewidths=0.5,
|
||||
yticklabels=h_reversed, xticklabels=months, cbar_kws={'label': ' Gravitational potential energy'})
|
||||
|
||||
# 横坐标过长,设置等间隔展示
|
||||
interval = 34 # 横坐标显示间隔
|
||||
plt.xticks(ticks=range(0, len(dates), interval),
|
||||
labels=months[::interval], rotation=45) # rotation旋转可不加
|
||||
|
||||
# 添加轴标签
|
||||
plt.xlabel('Month') # X轴标签
|
||||
plt.ylabel('Height') # Y轴标签
|
||||
|
||||
# 显示图形
|
||||
plt.show()
|
||||
# --------------绘制月统计图-------------------------------------------------------------------
|
||||
# 获取HP的列数
|
||||
num_cols = HP.shape[1]
|
||||
# 用于存储按要求计算出的均值列数据
|
||||
mean_cols = []
|
||||
start = 0
|
||||
while start < num_cols:
|
||||
end = start + 30
|
||||
if end > num_cols:
|
||||
end = num_cols
|
||||
# 提取每30列(或不满30列的剩余部分)的数据
|
||||
subset = HP.iloc[:, start:end]
|
||||
# 计算该部分数据每一行的均值,得到一个Series,作为新的均值列
|
||||
mean_series = subset.mean(axis=1)
|
||||
mean_cols.append(mean_series)
|
||||
start = end
|
||||
# 将所有的均值列合并成一个新的DataFrame
|
||||
result_df = pd.concat(mean_cols, axis=1)
|
||||
# 对result_df中的每一个元素取自然对数
|
||||
result_df_log = result_df.applymap(lambda x: np.log(x))
|
||||
# 通过drop方法删除第一行,axis=0表示按行操作,inplace=True表示直接在原DataFrame上修改(若不想修改原DataFrame可设置为False)
|
||||
result_df_log.drop(70, axis=0, inplace=True)
|
||||
# 计算每个月的平均值
|
||||
monthly_average = result_df_log.mean(axis=0)
|
||||
# 将结果转换为 (1, 12) 形状
|
||||
monthly_average = monthly_average.values.reshape(1, 12)
|
||||
monthly_average = monthly_average.ravel()
|
||||
|
||||
# 生成x轴的月份标签
|
||||
months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||||
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
|
||||
|
||||
# 绘制折线图
|
||||
plt.plot(months, monthly_average, marker='o', linestyle='-', color='b')
|
||||
|
||||
# 添加标题和标签
|
||||
plt.title("Monthly Average ENERGY(log)")
|
||||
plt.xlabel("Month")
|
||||
plt.ylabel("Average Energy")
|
||||
# 显示图表
|
||||
plt.xticks(rotation=45) # 让月份标签更清晰可读
|
||||
plt.grid(True)
|
||||
plt.tight_layout()
|
||||
# 显示图形
|
||||
plt.show()
|
||||
163
utils.py
Normal file
163
utils.py
Normal file
@ -0,0 +1,163 @@
|
||||
import glob
|
||||
import re
|
||||
|
||||
import pandas as pd
|
||||
|
||||
import balloon
|
||||
import time
|
||||
import logging
|
||||
|
||||
filter_columns = [
|
||||
"file_name",
|
||||
"c",
|
||||
"a",
|
||||
"b",
|
||||
"omega_upper",
|
||||
"w_f",
|
||||
"ver_wave_len",
|
||||
"hori_wave_len",
|
||||
"c_x",
|
||||
"c_y",
|
||||
"c_z",
|
||||
"Ek",
|
||||
"E_p",
|
||||
"MFu",
|
||||
"MFv",
|
||||
"u1",
|
||||
"v1",
|
||||
"T1",
|
||||
"zhou_qi",
|
||||
]
|
||||
|
||||
lat = 52.21
|
||||
g = 9.76
|
||||
|
||||
combos = {}
|
||||
comboType = [
|
||||
"探空气球",
|
||||
"流星雷达",
|
||||
"Saber",
|
||||
"TIDI",
|
||||
"COSMIC",
|
||||
]
|
||||
|
||||
comboMode = [
|
||||
["重力波单次", "重力波统计"],
|
||||
["重力波月统计", "潮汐波单次", "潮汐波月统计"],
|
||||
["行星波月统计", "重力波单次", "重力波月统计"],
|
||||
["行星波月统计"],
|
||||
["行星波月统计"],
|
||||
]
|
||||
|
||||
comboDate = [
|
||||
[["年", "时间"], ["起始年", "终止年"]],
|
||||
[["年", "月"], ["年", "日期"], ["年", "月"]],
|
||||
[["起始月", "-"], ["月", "日"], ["月", "-"]],
|
||||
[["起始月", "-"]],
|
||||
[["起始月", "-"]],
|
||||
]
|
||||
|
||||
|
||||
def get_ballon_files():
|
||||
try:
|
||||
data = glob.glob("data/探空气球/**/*.nc", recursive=True)
|
||||
except FileNotFoundError:
|
||||
return []
|
||||
return data
|
||||
|
||||
|
||||
all_ballon_files = get_ballon_files()
|
||||
|
||||
|
||||
def get_ballon_path_by_year(start_year, end_year):
|
||||
return list(filter(
|
||||
lambda x: any(f"LIN-{year}" in x for year in range(
|
||||
start_year, end_year + 1)),
|
||||
all_ballon_files
|
||||
))
|
||||
|
||||
|
||||
def get_ballon_full_df_by_year(start_year, end_year):
|
||||
|
||||
# Set up logging
|
||||
logging.basicConfig(level=logging.DEBUG,
|
||||
format='%(asctime)s %(levelname)s: %(message)s')
|
||||
|
||||
start_time = time.time()
|
||||
logging.debug(
|
||||
f"Starting get_ballon_full_df_by_year with start_year={start_year}, end_year={end_year}")
|
||||
|
||||
# Timing the path retrieval
|
||||
t0 = time.time()
|
||||
paths = get_ballon_path_by_year(start_year, end_year)
|
||||
t1 = time.time()
|
||||
logging.debug(f"Retrieved {len(paths)} paths in {t1 - t0:.2f} seconds")
|
||||
|
||||
# optimization: add cache. only select need to be reprocessed
|
||||
with open("./cache/ballon_lin_has_wave", "r") as f:
|
||||
cache_has_waves = f.readlines()
|
||||
cache_has_waves = [x.strip() for x in cache_has_waves]
|
||||
|
||||
year_df = pd.DataFrame()
|
||||
for idx, file in enumerate(paths, 1):
|
||||
if len(cache_has_waves) > 0 and file not in cache_has_waves:
|
||||
logging.debug(f"Skipping {file} as it has no wave data")
|
||||
continue
|
||||
file_start_time = time.time()
|
||||
logging.debug(f"Processing file {idx}/{len(paths)}: {file}")
|
||||
|
||||
# Read data
|
||||
data = balloon.read_data(file)
|
||||
read_time = time.time()
|
||||
logging.debug(
|
||||
f"Read data in {read_time - file_start_time:.2f} seconds")
|
||||
|
||||
# Extract wave
|
||||
try:
|
||||
wave = balloon.extract_wave(data, lat, g)
|
||||
extract_time = time.time()
|
||||
logging.debug(
|
||||
f"Extracted wave in {extract_time - read_time:.2f} seconds")
|
||||
except Exception as e:
|
||||
logging.error(f"Error extracting wave from {file}: {e}")
|
||||
wave = []
|
||||
extract_time = time.time()
|
||||
|
||||
if len(wave) == 0:
|
||||
logging.debug(f"No wave data in {file}, skipping")
|
||||
continue
|
||||
|
||||
# Determine terrain wave
|
||||
c = balloon.is_terrain_wave(data, lat, g)
|
||||
terrain_time = time.time()
|
||||
|
||||
year_pattern = r"products-RS92-GDP.2-LIN-(\d{4})"
|
||||
year = int(re.search(year_pattern, file).group(1))
|
||||
|
||||
logging.debug(
|
||||
f"Determined terrain wave in {terrain_time - extract_time:.2f} seconds")
|
||||
|
||||
# Build DataFrame line
|
||||
wave.insert(0, c)
|
||||
wave.insert(0, file)
|
||||
|
||||
line = pd.DataFrame([wave], columns=filter_columns)
|
||||
concat_start_time = time.time()
|
||||
|
||||
# Concatenate DataFrame
|
||||
year_df = pd.concat([year_df, line], ignore_index=True)
|
||||
concat_time = time.time()
|
||||
logging.debug(
|
||||
f"Concatenated DataFrame in {concat_time - concat_start_time:.2f} seconds")
|
||||
logging.debug(
|
||||
f"Total time for {file}: {concat_time - file_start_time:.2f} seconds")
|
||||
|
||||
total_time = time.time() - start_time
|
||||
logging.debug(
|
||||
f"Completed get_ballon_full_df_by_year in {total_time:.2f} seconds")
|
||||
return year_df
|
||||
|
||||
|
||||
def get_has_wave_data_by_year(start_year, end_year):
|
||||
df = get_ballon_full_df_by_year(start_year, end_year)
|
||||
return df[df["b"] == 1]
|
||||
Loading…
x
Reference in New Issue
Block a user