降雨预测系统(机器学习)

这是一个基于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()

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容