这是一个基于Python开发的降雨预测系统,使用机器学习算法对指定月份的降雨概率进行预测。该系统提供了友好的图形用户界面(GUI),支持数据可视化和交互式操作。
## 功能特点
– 📊 生成历史降雨数据(2015-2024年)
– 🤖 使用逻辑回归模型进行降雨预测
– 📈 可视化预测结果
– 🎯 显示每日降雨概率
– 💻 直观的图形用户界面
– 📝 详细的预测结果表格
– 🈚 完整的中文支持
## 环境要求
– Python 3.7+
– 必需的Python库:
– pandas:数据处理
– numpy:数值计算
– scikit-learn:机器学习模型
– matplotlib:数据可视化
– seaborn:增强可视化效果
– tkinter:GUI界面(Python标准库自带)
## 使用说明
1. 在控制面板中:
– 设置起始年份(默认2015)
– 设置当前年份(默认2024)
– 选择要预测的月份(1-12)
2. 操作步骤:
– 点击”生成数据”按钮生成历史数据
– 点击”训练模型”按钮训练预测模型
– 点击”预测结果”按钮查看预测结果
3. 查看结果:
– 图表区域显示降雨概率曲线
– 表格区域显示详细的每日预测数据
– 输出区域显示操作状态和模型评估结果
## 注意事项
1. 数据说明
– 历史数据为模拟生成,用于演示系统功能
– 预测结果仅供参考,不应用于实际天气预报
2. 性能优化
– 首次运行时,模型训练可能需要几秒钟
– 数据量较大时,建议增加起始年份以减少处理时间
降雨预测系统代码说明:
## 1. 导入库和初始化设置
“`python
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler
import matplotlib.pyplot as plt
import matplotlib.font_manager as fm
import seaborn as sns
import warnings
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
from datetime import datetime, timedelta
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import os
“`
### 导入库说明
– **数据处理**:pandas(pd), numpy(np)
– **机器学习**:sklearn (LogisticRegression, StandardScaler, train_test_split)
– **数据可视化**:matplotlib.pyplot, seaborn
– **GUI界面**:tkinter (tk, ttk, messagebox)
– **日期处理**:datetime
– **图表嵌入**:matplotlib.backends.backend_tkagg
### 中文字体设置
“`python
try:
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'SimSun', 'Arial Unicode MS']
plt.rcParams['axes.unicode_minus'] = False
# 检查字体可用性
font_names = [f.name for f in fm.fontManager.ttflist]
chinese_font_found = False
for font in ['SimHei', 'Microsoft YaHei', 'SimSun', 'Arial Unicode MS']:
if font in font_names:
print(f”找到中文字体: {font}”)
chinese_font_found = True
break
“`
– 设置matplotlib支持中文显示
– 按优先级尝试多个中文字体
– 检查系统中可用的中文字体
## 2. RainPredictor类
### 2.1 初始化
“`python
def __init__(self, start_year=2015, current_year=2024, month=3):
self.start_year = start_year
self.current_year = current_year
self.month = month
self.data = None
self.model = None
self.scaler = StandardScaler()
“`
– 初始化年份范围和预测月份
– 创建StandardScaler实例用于特征标准化
### 2.2 生成历史数据
“`python
def generate_historical_data(self):
“`
功能:
– 生成模拟的历史天气数据
– 考虑月份天数(闰年处理)
– 生成特征数据:
– 温度(temperature)
– 湿度(humidity)
– 气压(pressure)
– 降雨情况(rainfall)
– 添加日期特征(day_sin, day_cos)
### 2.3 训练模型
“`python
def train_model(self):
“`
功能:
– 准备特征数据
– 标准化特征
– 分割训练集和测试集
– 训练逻辑回归模型
– 计算模型评估指标
### 2.4 预测当前月份
“`python
def predict_current_month(self):
“`
功能:
– 生成预测月份的日期序列
– 生成预测所需的特征数据
– 使用训练好的模型预测降雨概率
### 2.5 绘制预测结果
“`python
def plot_predictions(self, dates, probabilities, figure):
“`
功能:
– 绘制降雨概率曲线
– 添加50%概率参考线
– 设置图表标题和标签
– 优化图表显示效果
## 3. RainPredictorGUI类
### 3.1 初始化
“`python
def __init__(self, root):
“`
功能:
– 设置主窗口属性
– 创建主框架
– 初始化界面组件
### 3.2 控制面板
“`python
def create_control_panel(self):
“`
功能:
– 创建年份输入框
– 创建月份选择下拉框
– 添加功能按钮:
– 生成数据
– 训练模型
– 预测结果
### 3.3 输出区域
“`python
def create_output_area(self):
“`
功能:
– 创建可滚动的文本输出区域
– 显示程序运行状态和结果
### 3.4 图表区域
“`python
def create_plot_area(self):
“`
功能:
– 创建matplotlib图表
– 嵌入tkinter界面
### 3.5 结果表格
“`python
def create_result_table(self):
“`
功能:
– 创建预测结果表格
– 设置表格列(日期、降雨概率)
– 添加滚动条
### 3.6 按钮回调函数
“`python
def generate_data(self)
def train_model(self)
def predict(self)
“`
功能:
– 处理用户界面交互
– 调用RainPredictor类的相应方法
– 更新界面显示
– 错误处理和提示
## 4. 主程序入口
“`python
def main():
root = tk.Tk()
app = RainPredictorGUI(root)
root.mainloop()
if __name__ == “__main__”:
main()
“`
功能:
– 创建主窗口
– 初始化GUI应用
– 启动事件循环
下面是完整代码:
import pandas as pd
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler
import matplotlib.pyplot as plt
import matplotlib.font_manager as fm
import seaborn as sns
import warnings
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox
from datetime import datetime, timedelta
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import os
warnings.filterwarnings('ignore')
# 设置中文字体
try:
# 尝试设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'SimSun', 'Arial Unicode MS']
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示问题
# 检查字体是否可用
font_names = [f.name for f in fm.fontManager.ttflist]
chinese_font_found = False
for font in ['SimHei', 'Microsoft YaHei', 'SimSun', 'Arial Unicode MS']:
if font in font_names:
print(f"找到中文字体: {font}")
chinese_font_found = True
break
if not chinese_font_found:
print("警告: 未找到中文字体,可能导致中文显示为方块")
except Exception as e:
print(f"设置中文字体时出错: {e}")
class RainPredictor:
def __init__(self, start_year=2015, current_year=2024, month=3):
"""
初始化降雨预测器
参数:
start_year: 历史数据开始年份
current_year: 当前年份
month: 要预测的月份
"""
self.start_year = start_year
self.current_year = current_year
self.month = month
self.data = None
self.model = None
self.scaler = StandardScaler()
def generate_historical_data(self):
"""
生成模拟的历史降雨数据
"""
# 创建日期范围
dates = []
rainfall = []
temperature = []
humidity = []
pressure = []
# 为每年的指定月份生成数据
for year in range(self.start_year, self.current_year):
# 获取该月的天数
if self.month == 2:
if year % 4 == 0 and (year % 100 != 0 or year % 400 == 0):
days_in_month = 29
else:
days_in_month = 28
elif self.month in [4, 6, 9, 11]:
days_in_month = 30
else:
days_in_month = 31
# 生成该月每天的数据
for day in range(1, days_in_month + 1):
date = datetime(year, self.month, day)
dates.append(date)
# 生成特征数据(使用一些模式来模拟真实情况)
# 基础概率随日期变化
base_prob = 0.3 + 0.2 * np.sin(2 * np.pi * day / days_in_month)
# 添加年际变化
year_effect = 0.1 * np.sin(2 * np.pi * (year - self.start_year) /
(self.current_year - self.start_year))
# 生成天气特征
temp = 20 + 5 * np.sin(2 * np.pi * day / days_in_month) + np.random.normal(0, 2)
hum = 60 + 20 * base_prob + np.random.normal(0, 5)
press = 1013 + np.random.normal(0, 2)
# 记录特征
temperature.append(temp)
humidity.append(hum)
pressure.append(press)
# 根据条件生成降雨情况
rain_prob = base_prob + year_effect
rain_prob = min(max(rain_prob, 0), 1) # 确保概率在[0,1]范围内
rainfall.append(1 if np.random.random() < rain_prob else 0)
# 创建DataFrame
self.data = pd.DataFrame({
'date': dates,
'temperature': temperature,
'humidity': humidity,
'pressure': pressure,
'rainfall': rainfall
})
# 添加日期特征
self.data['day'] = self.data['date'].dt.day
self.data['day_sin'] = np.sin(2 * np.pi * self.data['day'] / 31)
self.data['day_cos'] = np.cos(2 * np.pi * self.data['day'] / 31)
return "历史数据生成完成"
def train_model(self):
"""
训练降雨预测模型
"""
if self.data is None:
raise ValueError("请先生成或加载数据")
# 准备特征
features = ['temperature', 'humidity', 'pressure', 'day_sin', 'day_cos']
X = self.data[features]
y = self.data['rainfall']
# 标准化特征
X_scaled = self.scaler.fit_transform(X)
# 分割训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(
X_scaled, y, test_size=0.2, random_state=42
)
# 训练模型
self.model = LogisticRegression(random_state=42)
self.model.fit(X_train, y_train)
# 计算模型评估结果
train_score = self.model.score(X_train, y_train)
test_score = self.model.score(X_test, y_test)
return f"训练集准确率: {train_score:.3f}
测试集准确率: {test_score:.3f}"
def predict_current_month(self):
"""
预测当前年份指定月份的降雨概率
"""
if self.model is None:
raise ValueError("请先训练模型")
# 获取当月天数
if self.month == 2:
if self.current_year % 4 == 0 and (self.current_year % 100 != 0 or
self.current_year % 400 == 0):
days_in_month = 29
else:
days_in_month = 28
elif self.month in [4, 6, 9, 11]:
days_in_month = 30
else:
days_in_month = 31
# 生成预测数据
predict_dates = [datetime(self.current_year, self.month, day)
for day in range(1, days_in_month + 1)]
# 生成特征
predict_data = pd.DataFrame({
'date': predict_dates,
'temperature': [20 + 5 * np.sin(2 * np.pi * day / days_in_month)
for day in range(1, days_in_month + 1)],
'humidity': [60 + 20 * np.sin(2 * np.pi * day / days_in_month)
for day in range(1, days_in_month + 1)],
'pressure': [1013 + np.random.normal(0, 1)
for _ in range(days_in_month)],
'day': range(1, days_in_month + 1)
})
predict_data['day_sin'] = np.sin(2 * np.pi * predict_data['day'] / 31)
predict_data['day_cos'] = np.cos(2 * np.pi * predict_data['day'] / 31)
# 准备特征
features = ['temperature', 'humidity', 'pressure', 'day_sin', 'day_cos']
X_predict = predict_data[features]
# 标准化特征
X_predict_scaled = self.scaler.transform(X_predict)
# 预测降雨概率
rain_probabilities = self.model.predict_proba(X_predict_scaled)[:, 1]
return predict_dates, rain_probabilities
def plot_predictions(self, dates, probabilities, figure):
"""
可视化预测结果
"""
figure.clear()
ax = figure.add_subplot(111)
# 绘制降雨概率曲线
ax.plot(dates, probabilities, 'b-', label='降雨概率')
ax.fill_between(dates, probabilities, alpha=0.3)
ax.axhline(y=0.5, color='r', linestyle='--', label='50%概率线')
# 设置标题和标签
ax.set_title(f'{self.current_year}年{self.month}月降雨概率预测')
ax.set_xlabel('日期')
ax.set_ylabel('降雨概率')
# 设置网格
ax.grid(True, alpha=0.3)
ax.legend()
# 设置x轴刻度
plt.xticks(rotation=45)
plt.tight_layout()
class RainPredictorGUI:
def __init__(self, root):
self.root = root
self.root.title("降雨预测系统")
# 设置窗口大小和位置
window_width = 1000
window_height = 800
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()
center_x = int(screen_width/2 - window_width/2)
center_y = int(screen_height/2 - window_height/2)
self.root.geometry(f'{window_width}x{window_height}+{center_x}+{center_y}')
# 创建预测器实例
self.predictor = None
# 创建主框架
self.main_frame = ttk.Frame(root, padding="10")
self.main_frame.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
# 创建控制面板
self.create_control_panel()
# 创建输出区域
self.create_output_area()
# 创建图表区域
self.create_plot_area()
# 创建预测结果表格
self.create_result_table()
# 设置列和行的权重
self.root.columnconfigure(0, weight=1)
self.root.rowconfigure(0, weight=1)
self.main_frame.columnconfigure(1, weight=1)
self.main_frame.rowconfigure(3, weight=1)
def create_control_panel(self):
"""创建控制面板"""
control_frame = ttk.LabelFrame(self.main_frame, text="控制面板", padding="5")
control_frame.grid(row=0, column=0, columnspan=2, sticky=(tk.W, tk.E, tk.N, tk.S), pady=5)
# 年份选择
ttk.Label(control_frame, text="起始年份:").grid(row=0, column=0, padx=5, pady=5)
self.start_year = tk.StringVar(value="2015")
ttk.Entry(control_frame, textvariable=self.start_year, width=10).grid(row=0, column=1, padx=5, pady=5)
ttk.Label(control_frame, text="当前年份:").grid(row=0, column=2, padx=5, pady=5)
self.current_year = tk.StringVar(value="2024")
ttk.Entry(control_frame, textvariable=self.current_year, width=10).grid(row=0, column=3, padx=5, pady=5)
# 月份选择
ttk.Label(control_frame, text="预测月份:").grid(row=0, column=4, padx=5, pady=5)
self.month = tk.StringVar(value="3")
month_combo = ttk.Combobox(control_frame, textvariable=self.month, width=5)
month_combo['values'] = tuple(range(1, 13))
month_combo.grid(row=0, column=5, padx=5, pady=5)
# 按钮
ttk.Button(control_frame, text="生成数据", command=self.generate_data).grid(row=0, column=6, padx=5, pady=5)
ttk.Button(control_frame, text="训练模型", command=self.train_model).grid(row=0, column=7, padx=5, pady=5)
ttk.Button(control_frame, text="预测结果", command=self.predict).grid(row=0, column=8, padx=5, pady=5)
def create_output_area(self):
"""创建输出区域"""
output_frame = ttk.LabelFrame(self.main_frame, text="输出信息", padding="5")
output_frame.grid(row=1, column=0, columnspan=2, sticky=(tk.W, tk.E, tk.N, tk.S), pady=5)
self.output_text = tk.Text(output_frame, height=5, width=80)
self.output_text.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))
scrollbar = ttk.Scrollbar(output_frame, orient=tk.VERTICAL, command=self.output_text.yview)
scrollbar.grid(row=0, column=1, sticky=(tk.N, tk.S))
self.output_text['yscrollcommand'] = scrollbar.set
def create_plot_area(self):
"""创建图表区域"""
plot_frame = ttk.LabelFrame(self.main_frame, text="预测结果图表", padding="5")
plot_frame.grid(row=2, column=0, columnspan=2, sticky=(tk.W, tk.E, tk.N, tk.S), pady=5)
self.figure = plt.Figure(figsize=(10, 4), dpi=100)
self.canvas = FigureCanvasTkAgg(self.figure, master=plot_frame)
self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
def create_result_table(self):
"""创建预测结果表格"""
table_frame = ttk.LabelFrame(self.main_frame, text="详细预测结果", padding="5")
table_frame.grid(row=3, column=0, columnspan=2, sticky=(tk.W, tk.E, tk.N, tk.S), pady=5)
# 创建表格
columns = ('日期', '降雨概率')
self.tree = ttk.Treeview(table_frame, columns=columns, show='headings')
# 设置列标题
for col in columns:
self.tree.heading(col, text=col)
self.tree.column(col, width=100)
# 添加滚动条
scrollbar = ttk.Scrollbar(table_frame, orient=tk.VERTICAL, command=self.tree.yview)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
self.tree.configure(yscrollcommand=scrollbar.set)
self.tree.pack(fill=tk.BOTH, expand=1)
def generate_data(self):
"""生成数据按钮回调"""
try:
start_year = int(self.start_year.get())
current_year = int(self.current_year.get())
month = int(self.month.get())
self.predictor = RainPredictor(start_year, current_year, month)
result = self.predictor.generate_historical_data()
self.output_text.delete(1.0, tk.END)
self.output_text.insert(tk.END, result + "
")
except Exception as e:
messagebox.showerror("错误", str(e))
def train_model(self):
"""训练模型按钮回调"""
try:
if self.predictor is None:
raise ValueError("请先生成数据")
result = self.predictor.train_model()
self.output_text.insert(tk.END, result + "
")
except Exception as e:
messagebox.showerror("错误", str(e))
def predict(self):
"""预测按钮回调"""
try:
if self.predictor is None or self.predictor.model is None:
raise ValueError("请先生成数据并训练模型")
# 获取预测结果
dates, probabilities = self.predictor.predict_current_month()
# 更新图表
self.predictor.plot_predictions(dates, probabilities, self.figure)
self.canvas.draw()
# 更新表格
self.tree.delete(*self.tree.get_children())
for date, prob in zip(dates, probabilities):
self.tree.insert('', tk.END, values=(date.strftime('%Y-%m-%d'), f'{prob:.2%}'))
self.output_text.insert(tk.END, "预测完成
")
except Exception as e:
messagebox.showerror("错误", str(e))
def main():
root = tk.Tk()
app = RainPredictorGUI(root)
root.mainloop()
if __name__ == "__main__":
main()
暂无评论内容