import tkinter as tk from tkinter import ttk, messagebox from pathlib import Path import joblib import pandas as pd BASE_DIR = Path(__file__).resolve().parent MODEL_PATH = BASE_DIR / 'outputs' / 'models' / 'best_bike_demand_model.pkl' FEATURES = ['季节', '年', '月', '节假日', '星期', '工作日', '天气', '气温', '体感温度', '湿度', '风速'] class BikeDemandApp: def __init__(self, root): self.root = root self.root.title('共享单车日骑行量预测系统') self.root.geometry('820x560') self.model = joblib.load(MODEL_PATH) self.entries = {} self.history = [] self.build_ui() def build_ui(self): container = ttk.Frame(self.root, padding=16) container.pack(fill=tk.BOTH, expand=True) left = ttk.LabelFrame(container, text='输入特征', padding=12) left.pack(side=tk.LEFT, fill=tk.Y, padx=(0, 12)) fields = [ ('季节', '1=春 2=夏 3=秋 4=冬', '1'), ('年', '0=2011 1=2012 2=2013', '2'), ('月', '1~12', '1'), ('节假日', '0=否 1=是', '0'), ('星期', '0=周日 ... 6=周六', '1'), ('工作日', '0=否 1=是', '1'), ('天气', '1=晴好 2=多云 3=小雨雪 4=恶劣', '1'), ('气温', '归一化 0~1', '0.32'), ('体感温度', '归一化 0~1', '0.34'), ('湿度', '归一化 0~1', '0.70'), ('风速', '归一化 0~1', '0.15'), ] for row, (name, hint, default) in enumerate(fields): ttk.Label(left, text=name).grid(row=row, column=0, sticky='w', pady=4) entry = ttk.Entry(left, width=14) entry.insert(0, default) entry.grid(row=row, column=1, sticky='ew', pady=4) ttk.Label(left, text=hint, foreground='#666').grid(row=row, column=2, sticky='w', padx=6) self.entries[name] = entry ttk.Button(left, text='预测单场景', command=self.predict_one).grid(row=len(fields), column=0, columnspan=3, sticky='ew', pady=(12, 4)) ttk.Button(left, text='加入对比列表', command=self.add_to_history).grid(row=len(fields) + 1, column=0, columnspan=3, sticky='ew', pady=4) ttk.Button(left, text='清空对比列表', command=self.clear_history).grid(row=len(fields) + 2, column=0, columnspan=3, sticky='ew', pady=4) right = ttk.Frame(container) right.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True) result_box = ttk.LabelFrame(right, text='预测结果', padding=16) result_box.pack(fill=tk.X) self.result_var = tk.StringVar(value='请输入特征后点击预测') ttk.Label(result_box, textvariable=self.result_var, font=('Arial', 22, 'bold'), foreground='#0f766e').pack(anchor='w') self.level_var = tk.StringVar(value='') ttk.Label(result_box, textvariable=self.level_var, font=('Arial', 12)).pack(anchor='w', pady=(8, 0)) table_box = ttk.LabelFrame(right, text='多场景对比', padding=8) table_box.pack(fill=tk.BOTH, expand=True, pady=(12, 0)) columns = ('序号', '月', '天气', '气温', '工作日', '预测总骑行量') self.tree = ttk.Treeview(table_box, columns=columns, show='headings', height=12) for col in columns: self.tree.heading(col, text=col) self.tree.column(col, width=90, anchor='center') self.tree.pack(fill=tk.BOTH, expand=True) def collect_features(self): values = {} for feature, entry in self.entries.items(): try: values[feature] = float(entry.get()) except ValueError as exc: raise ValueError(f'{feature} 必须是数字') from exc for feature in ['季节', '年', '月', '节假日', '星期', '工作日', '天气']: values[feature] = int(values[feature]) for feature in ['气温', '体感温度', '湿度', '风速']: if not 0 <= values[feature] <= 1: raise ValueError(f'{feature} 应在 0~1 之间') return values def predict_value(self): values = self.collect_features() frame = pd.DataFrame([values], columns=FEATURES) prediction = int(max(round(self.model.predict(frame)[0]), 0)) return values, prediction def predict_one(self): try: _, prediction = self.predict_value() except Exception as exc: messagebox.showerror('输入错误', str(exc)) return self.result_var.set(f'预计日骑行量:{prediction:,} 次') if prediction >= 5000: level = '需求等级:高,需要提前调度车辆。' elif prediction >= 3000: level = '需求等级:中,维持常规运维。' else: level = '需求等级:低,可减少热点补车频率。' self.level_var.set(level) def add_to_history(self): try: values, prediction = self.predict_value() except Exception as exc: messagebox.showerror('输入错误', str(exc)) return self.history.append((values, prediction)) idx = len(self.history) self.tree.insert('', 'end', values=(idx, values['月'], values['天气'], values['气温'], values['工作日'], prediction)) self.predict_one() def clear_history(self): self.history.clear() for item in self.tree.get_children(): self.tree.delete(item) if __name__ == '__main__': root = tk.Tk() app = BikeDemandApp(root) root.mainloop()