feat: 完成共享单车课程设计

This commit is contained in:
luochen570
2026-06-08 23:15:05 +08:00
commit 0026c4bf84
24 changed files with 912 additions and 0 deletions

7
.gitignore vendored Normal file
View File

@@ -0,0 +1,7 @@
.venv/
__pycache__/
*.py[cod]
*.zip
*.tar.gz
outputs/rerun.log
.DS_Store

55
README.md Normal file
View File

@@ -0,0 +1,55 @@
# 共享单车日骑行量预测与可视化系统
## 项目内容
本项目完成《大数据分析技术》课程设计中“基于机器学习的共享单车日骑行量预测与可视化系统”的全部任务:
1. 多模型构建与初步筛选
2. 特征重要性解析
3. 模型评估与 2013 年前 100 天预测输出
4. 轻量级 GUI 预测可视化系统
## 目录说明
- `analyze_bike_sharing.py`:完整数据分析、建模、评估、预测、图表生成脚本
- `bike_demand_gui.py`tkinter 轻量级预测系统
- `generate_report.py`:课程设计报告生成脚本
- `outputs/model_comparison.csv`:模型对比结果
- `outputs/cross_validation_summary.csv`:交叉验证结果
- `outputs/feature_importance.csv`:特征重要性
- `outputs/2013前100天骑行量预测结果.xlsx`:最终预测结果
- `outputs/models/best_bike_demand_model.pkl`:最佳模型文件
- `outputs/figures/`:报告图表
- `outputs/共享单车日骑行量预测课程设计报告.docx`:课程设计报告
## 运行环境
```bash
cd /root/bike_course_design
python3 -m venv .venv
.venv/bin/pip install pymupdf openpyxl pandas matplotlib seaborn scikit-learn python-docx joblib
```
## 重新生成分析结果
```bash
cd /root/bike_course_design
.venv/bin/python analyze_bike_sharing.py
```
## 启动 GUI
```bash
cd /root/bike_course_design
.venv/bin/python bike_demand_gui.py
```
GUI 支持输入季节、年月、节假日、星期、工作日、天气、气温、体感温度、湿度、风速等特征,输出单场景预测值,并可加入多场景对比表。
## 本次运行核心结论
- 数据量731 条历史日度记录,预测集 100 条记录。
- 最佳模型:梯度提升树。
- 测试集指标RMSE 约 651.14MAE 约 449.00R² 约 0.8943。
- 五折交叉验证RMSE 均值约 655.12R² 均值约 0.8819。
- 关键特征:气温、年份、季节、体感温度、湿度。

265
analyze_bike_sharing.py Normal file
View File

@@ -0,0 +1,265 @@
import json
from pathlib import Path
import joblib
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import seaborn as sns
from sklearn.compose import ColumnTransformer
from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor
from sklearn.inspection import permutation_importance
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
from sklearn.model_selection import KFold, cross_validate, train_test_split
from sklearn.neural_network import MLPRegressor
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import OneHotEncoder, StandardScaler
from sklearn.svm import SVR
BASE_DIR = Path(__file__).resolve().parent
DATA_PATH = Path('/root/.hermes-web-ui/upload/default/28752a235eb6ef9d.xlsx')
OUT_DIR = BASE_DIR / 'outputs'
FIG_DIR = OUT_DIR / 'figures'
MODEL_DIR = OUT_DIR / 'models'
for directory in [OUT_DIR, FIG_DIR, MODEL_DIR]:
directory.mkdir(parents=True, exist_ok=True)
plt.rcParams['font.sans-serif'] = ['Noto Sans CJK SC', 'WenQuanYi Zen Hei', 'SimHei', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False
sns.set_theme(style='whitegrid')
FEATURES = ['季节', '', '', '节假日', '星期', '工作日', '天气', '气温', '体感温度', '湿度', '风速']
CATEGORICAL_FEATURES = ['季节', '', '', '节假日', '星期', '工作日', '天气']
NUMERIC_FEATURES = ['气温', '体感温度', '湿度', '风速']
TARGET = '总骑行量'
def rmse(y_true, y_pred):
return float(np.sqrt(mean_squared_error(y_true, y_pred)))
def evaluate_model(model, x_train, x_test, y_train, y_test):
model.fit(x_train, y_train)
pred = model.predict(x_test)
return {
'RMSE': rmse(y_test, pred),
'MAE': float(mean_absolute_error(y_test, pred)),
'R2': float(r2_score(y_test, pred)),
}, pred
def make_preprocessor(scale_numeric=False):
numeric_transformer = StandardScaler() if scale_numeric else 'passthrough'
return ColumnTransformer(
transformers=[
('cat', OneHotEncoder(handle_unknown='ignore'), CATEGORICAL_FEATURES),
('num', numeric_transformer, NUMERIC_FEATURES),
],
remainder='drop',
verbose_feature_names_out=False,
)
def make_models():
return {
'线性回归': Pipeline([
('preprocess', make_preprocessor(scale_numeric=True)),
('model', LinearRegression()),
]),
'随机森林': Pipeline([
('preprocess', make_preprocessor()),
('model', RandomForestRegressor(n_estimators=500, random_state=42, min_samples_leaf=2, n_jobs=-1)),
]),
'梯度提升树': Pipeline([
('preprocess', make_preprocessor()),
('model', GradientBoostingRegressor(random_state=42, n_estimators=300, learning_rate=0.05, max_depth=3)),
]),
'支持向量回归': Pipeline([
('preprocess', make_preprocessor(scale_numeric=True)),
('model', SVR(C=200, gamma='scale', epsilon=50)),
]),
'神经网络': Pipeline([
('preprocess', make_preprocessor(scale_numeric=True)),
('model', MLPRegressor(hidden_layer_sizes=(80, 40), activation='relu', alpha=0.001, max_iter=3000, random_state=42, early_stopping=True)),
]),
}
def load_data():
daily = pd.read_excel(DATA_PATH, sheet_name='每天数据')
future = pd.read_excel(DATA_PATH, sheet_name='预测集')
daily['日期'] = pd.to_datetime(daily['日期'])
future['日期'] = pd.to_datetime(future['日期'])
return daily, future
def feature_importance(best_model, x_test, y_test):
model_name = type(best_model.named_steps['model']).__name__
if hasattr(best_model.named_steps['model'], 'feature_importances_'):
transformed_names = best_model.named_steps['preprocess'].get_feature_names_out()
raw_importances = pd.Series(best_model.named_steps['model'].feature_importances_, index=transformed_names)
collapsed = {}
for feature in FEATURES:
collapsed[feature] = raw_importances[[idx == feature or idx.startswith(feature + '_') for idx in raw_importances.index]].sum()
importance = pd.Series(collapsed).sort_values(ascending=False)
method = f'{model_name} 内置重要性'
else:
result = permutation_importance(best_model, x_test, y_test, n_repeats=30, random_state=42, scoring='neg_root_mean_squared_error', n_jobs=-1)
importance = pd.Series(result.importances_mean, index=FEATURES).sort_values(ascending=False)
method = '排列重要性'
return importance, method
def save_figures(daily, y_test, y_pred, residuals, importance, future_pred_df):
plt.figure(figsize=(12, 6))
sns.lineplot(data=daily, x='日期', y=TARGET, color='#2563eb')
plt.title('2011-2012 年共享单车每日总骑行量趋势')
plt.xlabel('日期')
plt.ylabel('总骑行量')
plt.tight_layout()
plt.savefig(FIG_DIR / 'historical_trend.png', dpi=180)
plt.close()
plt.figure(figsize=(8, 6))
sns.scatterplot(x=y_test, y=y_pred, color='#16a34a', edgecolor='white')
low, high = min(y_test.min(), y_pred.min()), max(y_test.max(), y_pred.max())
plt.plot([low, high], [low, high], '--', color='#ef4444', linewidth=1.5)
plt.title('测试集真实值-预测值散点图')
plt.xlabel('真实总骑行量')
plt.ylabel('预测总骑行量')
plt.tight_layout()
plt.savefig(FIG_DIR / 'actual_vs_predicted.png', dpi=180)
plt.close()
plt.figure(figsize=(8, 5))
sns.histplot(residuals, kde=True, color='#f97316')
plt.axvline(0, color='#111827', linestyle='--', linewidth=1)
plt.title('测试集残差分布')
plt.xlabel('残差(真实值 - 预测值)')
plt.ylabel('频数')
plt.tight_layout()
plt.savefig(FIG_DIR / 'residual_distribution.png', dpi=180)
plt.close()
plt.figure(figsize=(9, 6))
plot_imp = importance.sort_values(ascending=True)
plt.barh(plot_imp.index, plot_imp.values, color='#7c3aed')
plt.title('特征重要性排序')
plt.xlabel('重要性')
plt.tight_layout()
plt.savefig(FIG_DIR / 'feature_importance.png', dpi=180)
plt.close()
plt.figure(figsize=(12, 6))
sns.lineplot(data=future_pred_df, x='日期', y='预测总骑行量', marker='o', color='#0891b2')
plt.title('2013 年前 100 天共享单车骑行量预测趋势')
plt.xlabel('日期')
plt.ylabel('预测总骑行量')
plt.tight_layout()
plt.savefig(FIG_DIR / 'future_prediction_trend.png', dpi=180)
plt.close()
def main():
daily, future = load_data()
missing = daily[FEATURES + [TARGET]].isna().sum().to_dict()
duplicates = int(daily.duplicated(subset=['日期']).sum())
x = daily[FEATURES]
y = daily[TARGET]
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=42)
models = make_models()
initial_rows = []
predictions = {}
for name, model in models.items():
metrics, pred = evaluate_model(model, x_train, x_test, y_train, y_test)
initial_rows.append({'模型': name, **metrics})
predictions[name] = pred
joblib.dump(model, MODEL_DIR / f'{name}.pkl')
initial_df = pd.DataFrame(initial_rows).sort_values('RMSE')
best_name = initial_df.iloc[0]['模型']
best_model = models[best_name]
best_pred = predictions[best_name]
residuals = y_test.to_numpy() - best_pred
cv = KFold(n_splits=5, shuffle=True, random_state=42)
cv_result = cross_validate(
best_model,
x,
y,
cv=cv,
scoring={'rmse': 'neg_root_mean_squared_error', 'mae': 'neg_mean_absolute_error', 'r2': 'r2'},
n_jobs=-1,
)
cv_summary = {
'模型': best_name,
'RMSE均值': float(-cv_result['test_rmse'].mean()),
'RMSE标准差': float(cv_result['test_rmse'].std()),
'MAE均值': float(-cv_result['test_mae'].mean()),
'MAE标准差': float(cv_result['test_mae'].std()),
'R2均值': float(cv_result['test_r2'].mean()),
'R2标准差': float(cv_result['test_r2'].std()),
}
importance, importance_method = feature_importance(best_model, x_test, y_test)
future_pred = best_model.predict(future[FEATURES])
future_pred_df = future.copy()
future_pred_df['预测总骑行量'] = np.maximum(np.round(future_pred), 0).astype(int)
save_figures(daily, y_test, best_pred, residuals, importance, future_pred_df)
initial_df.to_csv(OUT_DIR / 'model_comparison.csv', index=False, encoding='utf-8-sig')
pd.DataFrame([cv_summary]).to_csv(OUT_DIR / 'cross_validation_summary.csv', index=False, encoding='utf-8-sig')
importance.rename('重要性').to_csv(OUT_DIR / 'feature_importance.csv', encoding='utf-8-sig')
future_pred_df.to_excel(OUT_DIR / '2013前100天骑行量预测结果.xlsx', index=False)
test_pred_df = pd.DataFrame({
'日期': daily.loc[x_test.index, '日期'].values,
'真实总骑行量': y_test.values,
'预测总骑行量': np.round(best_pred).astype(int),
'残差': np.round(residuals, 2),
}).sort_values('日期')
test_pred_df.to_csv(OUT_DIR / 'test_set_predictions.csv', index=False, encoding='utf-8-sig')
joblib.dump(best_model, MODEL_DIR / 'best_bike_demand_model.pkl')
summary = {
'data': {
'rows': int(len(daily)),
'columns': int(daily.shape[1]),
'future_rows': int(len(future)),
'missing': missing,
'duplicate_dates': duplicates,
'target_min': int(y.min()),
'target_max': int(y.max()),
'target_mean': float(y.mean()),
},
'initial_comparison': initial_df.to_dict(orient='records'),
'best_model': best_name,
'best_test_metrics': initial_df.iloc[0].to_dict(),
'cross_validation': cv_summary,
'importance_method': importance_method,
'top_features': importance.head(8).to_dict(),
'future_prediction': {
'min': int(future_pred_df['预测总骑行量'].min()),
'max': int(future_pred_df['预测总骑行量'].max()),
'mean': float(future_pred_df['预测总骑行量'].mean()),
},
'outputs': {
'figures': [str(p) for p in sorted(FIG_DIR.glob('*.png'))],
'best_model': str(MODEL_DIR / 'best_bike_demand_model.pkl'),
'prediction_excel': str(OUT_DIR / '2013前100天骑行量预测结果.xlsx'),
}
}
(OUT_DIR / 'analysis_summary.json').write_text(json.dumps(summary, ensure_ascii=False, indent=2), encoding='utf-8')
print(json.dumps(summary, ensure_ascii=False, indent=2))
if __name__ == '__main__':
main()

129
bike_demand_gui.py Normal file
View File

@@ -0,0 +1,129 @@
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()

View File

@@ -0,0 +1,64 @@
6
三、基于机器学习的共享单车日骑行量预测与可视化系统
1. 课题背景
在构建绿色、低碳城市的进程中,“最后一公里”出行难题一直是阻碍公共交通
高效利用的关键瓶颈。共享单车作为一种创新的分时租赁模式,有效填补了这一空
白,不仅提升了居民使用公共交通的整体意愿,还促进了绿色出行理念的普及。然
而,共享单车的运营效率高度依赖于对用户需求的精准预判。骑行需求受多种复杂
因素影响,如天气状况、季节变化、是否为工作日或节假日等。因此,利用历史运
营数据,通过数据挖掘与机器学习技术建立精准的需求预测模型,对于优化车辆调
度、提升用户体验及实现企业精细化运营具有重要的现实意义。
2. 数据描述
本课题采用的数据集源自美国华盛顿特区的Capital Bikeshare 系统是UCI 机器
学习库中的经典数据集Bike Sharing Dataset。我们选用其中的日度统计数据见“共
享单车骑行量数据.xlsx”的第一张工作表“每天数据”数据集共包含731 条记录,覆
盖了2011 年与2012 年两年完整的骑行数据共16 列,各列含义见第二张工作表“特
征说明”。
此外数据集还提供了待预测的2013 年前100 天的相关特征,见第三张工作表
“预测集”,用于最终模型的骑行量预测。
3. 设计任务
本课题围绕共享单车每日总骑行量预测展开,要求完成“模型构建与对比→特征
影响剖析→性能评估与预测→可视化系统开发”的完整技术研究流程,全面锻炼数据
建模、特征分析、模型优化及GUI 开发的综合能力。
任务一:多模型构建与初步筛选
目标:建立包含基线模型和候选模型的完整模型池,为后续分析提供严谨的性
能基准。
核心步骤:
1数据准备从数据集第一张工作表中提取特征12 维)与目标变量,并将
数据集划分为训练集和测试集;
2模型选型
基线模型:选择线性回归模型作为基准;
候选模型:至少选择两类非线性回归模型(如树模型+ 核方法/神经网络),体
现方法多样性;
3基础训练对每类模型使用默认或合理初始参数进行训练
4对比在验证集上计算各模型的RMSE/MAE/R²形成初步性能排序
5输出保存各模型对象及初步评估结果为任务三的深入评估和任务二的
特征分析提供候选模型池。
7
任务二:特征重要性解析
目标:挖掘影响骑行量的关键因子,解释模型决策逻辑。
核心步骤:
1选定最优模型基于任务一结果选取性能最佳模型
2量化与可视化通过内置属性树模型或排列重要性其他模型计算
特征贡献,绘制条形图;
3业务解读结合领域知识分析关键特征的影响机制
4输出特征重要性报告为模型精简提供依据。
任务三:模型评估与预测输出
目标:验证模型泛化能力,生成最终预测结果。
核心步骤:
1全面评估通过交叉验证计算RMSE/MAE/R²绘制真实值-预测值散点图
及残差分布;
2确定最优模型综合指标与稳定性选定部署模型
3预测与可视化基于2013 年前100 天特征数据生成预测值,输出表格及
趋势折线图;
4输出保存模型文件如.pkl和预测结果作为GUI 系统的数据源。
任务四:轻量级预测可视化系统开发
目标:实现分析成果的交互式应用。
核心步骤:
1框架选型采用轻量GUI 工具如Streamlit、tkinter、PyQT
2界面设计左侧输入区日期、天气、温度等+右侧结果展示区;
3模型集成加载最优模型编写特征处理与预测函数
4动态可视化让用户直观理解预测结果同时支持单场景查询数字+仪
表盘)和多场景对比(折线图)两种核心需求;
5优化与输出实现输入校验与实时响应交付可运行程序及使用说明。

124
generate_report.py Normal file
View File

@@ -0,0 +1,124 @@
import json
from pathlib import Path
import pandas as pd
from docx import Document
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.shared import Inches, Pt
BASE_DIR = Path(__file__).resolve().parent
OUT_DIR = BASE_DIR / 'outputs'
FIG_DIR = OUT_DIR / 'figures'
REPORT_PATH = OUT_DIR / '共享单车日骑行量预测课程设计报告.docx'
def add_heading(document, text, level):
heading = document.add_heading(text, level=level)
for run in heading.runs:
run.font.name = '宋体'
return heading
def add_paragraph(document, text):
paragraph = document.add_paragraph(text)
paragraph.paragraph_format.first_line_indent = Pt(24)
paragraph.paragraph_format.line_spacing = 1.5
for run in paragraph.runs:
run.font.name = '宋体'
run.font.size = Pt(11)
return paragraph
def add_table_from_df(document, df, title=None):
if title:
add_paragraph(document, title)
table = document.add_table(rows=1, cols=len(df.columns))
table.style = 'Table Grid'
hdr = table.rows[0].cells
for i, col in enumerate(df.columns):
hdr[i].text = str(col)
for _, row in df.iterrows():
cells = table.add_row().cells
for i, value in enumerate(row):
if isinstance(value, float):
cells[i].text = f'{value:.4f}'
else:
cells[i].text = str(value)
document.add_paragraph('')
def main():
summary = json.loads((OUT_DIR / 'analysis_summary.json').read_text(encoding='utf-8'))
comparison = pd.read_csv(OUT_DIR / 'model_comparison.csv')
cv = pd.read_csv(OUT_DIR / 'cross_validation_summary.csv')
importance = pd.read_csv(OUT_DIR / 'feature_importance.csv')
future = pd.read_excel(OUT_DIR / '2013前100天骑行量预测结果.xlsx')
document = Document()
section = document.sections[0]
section.top_margin = Inches(0.8)
section.bottom_margin = Inches(0.8)
section.left_margin = Inches(0.9)
section.right_margin = Inches(0.9)
title = document.add_heading('基于机器学习的共享单车日骑行量预测与可视化系统', 0)
title.alignment = WD_ALIGN_PARAGRAPH.CENTER
subtitle = document.add_paragraph('《大数据分析技术》课程设计报告')
subtitle.alignment = WD_ALIGN_PARAGRAPH.CENTER
document.add_paragraph('')
add_heading(document, '一、课题背景与研究目标', 1)
add_paragraph(document, '共享单车是解决城市公共交通“最后一公里”问题的重要方式。其运营效率依赖于车辆供需匹配能力,而日骑行量会受到季节、天气、温度、湿度、风速、工作日等多因素共同影响。本课程设计以 Capital Bikeshare 日度数据为基础,构建机器学习模型预测共享单车每日总骑行量,并形成可视化分析结果与轻量级预测系统。')
add_paragraph(document, '本项目围绕“模型构建与对比、特征影响剖析、性能评估与预测、GUI 系统开发”四项任务展开,最终输出模型文件、评估结果、预测表格、图表和可运行程序。')
add_heading(document, '二、数据说明与预处理', 1)
data = summary['data']
add_paragraph(document, f'历史数据来自 Excel 第一张工作表“每天数据”,共 {data["rows"]} 条记录、{data["columns"]} 列,覆盖 2011—2012 年完整日度骑行数据;预测集来自第三张工作表“预测集”,包含 2013 年前 {data["future_rows"]} 天的待预测特征。')
add_paragraph(document, f'目标变量为“总骑行量”,历史样本最小值为 {data["target_min"]},最大值为 {data["target_max"]},均值为 {data["target_mean"]:.2f}。经检查,建模字段不存在缺失值,日期字段无重复记录。')
add_paragraph(document, '建模特征选取季节、年、月、节假日、星期、工作日、天气、气温、体感温度、湿度、风速等 11 个字段。类别型特征采用 One-Hot 编码数值型特征在对线性回归、SVR 和神经网络建模时进行标准化处理。')
document.add_picture(str(FIG_DIR / 'historical_trend.png'), width=Inches(6.2))
add_heading(document, '三、多模型构建与初步筛选', 1)
add_paragraph(document, '按照课程设计要求,模型池包含一个基线模型和多类非线性候选模型。基线模型采用线性回归;候选模型包括随机森林、梯度提升树、支持向量回归和神经网络,覆盖树模型、核方法和神经网络方法。数据按 8:2 划分训练集和测试集,使用 RMSE、MAE、R² 进行比较。')
add_table_from_df(document, comparison, '表 1 模型初步对比结果')
best = summary['best_test_metrics']
add_paragraph(document, f'从测试集结果看,{summary["best_model"]} 的综合表现最佳RMSE={best["RMSE"]:.2f}MAE={best["MAE"]:.2f}R²={best["R2"]:.4f}。因此后续特征解释、交叉验证与 2013 年预测均以该模型作为最终模型。')
add_heading(document, '四、特征重要性解析', 1)
add_paragraph(document, f'由于最优模型为梯度提升树,项目采用模型内置特征重要性计算特征贡献,并将 One-Hot 后的同源类别特征聚合回原始字段。特征重要性结果显示,气温、年、季节、体感温度、湿度对骑行量影响最大。')
document.add_picture(str(FIG_DIR / 'feature_importance.png'), width=Inches(6.2))
add_table_from_df(document, importance.head(8), '表 2 前 8 个重要特征')
add_paragraph(document, '业务上,气温和体感温度直接影响用户骑行意愿,舒适天气下出行需求更高;年份特征反映共享单车系统规模和用户习惯随时间增长;季节与月份体现周期性需求变化;湿度、风速和天气体现不良气象条件对骑行行为的抑制作用。')
add_heading(document, '五、模型全面评估', 1)
add_paragraph(document, '为了验证模型泛化能力,对最佳模型进行五折交叉验证,并绘制测试集真实值-预测值散点图和残差分布图。')
add_table_from_df(document, cv, '表 3 五折交叉验证结果')
cv_row = summary['cross_validation']
add_paragraph(document, f'交叉验证 RMSE 均值为 {cv_row["RMSE均值"]:.2f},标准差为 {cv_row["RMSE标准差"]:.2f}R² 均值为 {cv_row["R2均值"]:.4f},说明模型在不同数据划分下较稳定。')
document.add_picture(str(FIG_DIR / 'actual_vs_predicted.png'), width=Inches(5.8))
document.add_picture(str(FIG_DIR / 'residual_distribution.png'), width=Inches(5.8))
add_paragraph(document, '真实值-预测值散点大体分布在对角线附近,说明模型能较好拟合总体变化趋势。残差分布集中在 0 附近,表明模型不存在明显系统性偏差,但在极端低需求或高需求日期仍可能出现较大误差。')
add_heading(document, '六、2013 年前 100 天预测结果', 1)
fp = summary['future_prediction']
add_paragraph(document, f'使用最终梯度提升树模型对预测集进行推断2013 年前 100 天预测骑行量最小值为 {fp["min"]},最大值为 {fp["max"]},均值为 {fp["mean"]:.2f}。完整结果已保存为 Excel 文件“2013前100天骑行量预测结果.xlsx”。')
document.add_picture(str(FIG_DIR / 'future_prediction_trend.png'), width=Inches(6.2))
sample_future = future[['日期', '季节', '', '天气', '气温', '湿度', '预测总骑行量']].head(10).copy()
sample_future['日期'] = sample_future['日期'].dt.strftime('%Y-%m-%d')
add_table_from_df(document, sample_future, '表 4 预测结果样例(前 10 天)')
add_heading(document, '七、轻量级预测可视化系统', 1)
add_paragraph(document, '系统采用 Python tkinter 开发,程序文件为 bike_demand_gui.py。界面左侧提供季节、年份、月份、节假日、星期、工作日、天气、气温、体感温度、湿度、风速等输入项右侧展示单场景预测结果与多场景对比表。')
add_paragraph(document, '系统启动时加载 outputs/models/best_bike_demand_model.pkl并对用户输入进行数字类型和归一化范围校验。用户可点击“预测单场景”获得预计日骑行量也可将多组输入加入对比列表用于比较不同天气或温度条件下的需求变化。')
add_paragraph(document, '运行方式:在项目目录执行 `.venv/bin/python bike_demand_gui.py`。')
add_heading(document, '八、结论', 1)
add_paragraph(document, '本课程设计完成了从数据读取、预处理、模型训练、模型比较、特征解释、交叉验证、未来预测到 GUI 系统开发的完整流程。实验结果表明梯度提升树在该共享单车日骑行量预测任务中优于线性回归、随机森林、SVR 和神经网络,能够较好刻画天气、季节和时间因素与骑行需求之间的非线性关系。')
add_paragraph(document, '后续可进一步引入节假日类型、降水量、重大活动、站点级空间信息等外部变量,并采用时间序列交叉验证或集成学习优化模型,以提升极端天气和异常日期下的预测鲁棒性。')
document.save(REPORT_PATH)
print(REPORT_PATH)
if __name__ == '__main__':
main()

Binary file not shown.

View File

@@ -0,0 +1,100 @@
{
"data": {
"rows": 731,
"columns": 16,
"future_rows": 100,
"missing": {
"季节": 0,
"年": 0,
"月": 0,
"节假日": 0,
"星期": 0,
"工作日": 0,
"天气": 0,
"气温": 0,
"体感温度": 0,
"湿度": 0,
"风速": 0,
"总骑行量": 0
},
"duplicate_dates": 0,
"target_min": 22,
"target_max": 8714,
"target_mean": 4504.3488372093025
},
"initial_comparison": [
{
"模型": "梯度提升树",
"RMSE": 651.1440567037434,
"MAE": 448.99523765137667,
"R2": 0.8942640825672749
},
{
"模型": "随机森林",
"RMSE": 703.9966504917254,
"MAE": 450.0664823777129,
"R2": 0.8764025354772573
},
{
"模型": "支持向量回归",
"RMSE": 738.1033329574317,
"MAE": 529.5340809792003,
"R2": 0.8641365279788986
},
{
"模型": "线性回归",
"RMSE": 796.4617765623753,
"MAE": 583.0197588300691,
"R2": 0.8418029967286178
},
{
"模型": "神经网络",
"RMSE": 809.7923656712804,
"MAE": 609.9584660642089,
"R2": 0.836463110808386
}
],
"best_model": "梯度提升树",
"best_test_metrics": {
"模型": "梯度提升树",
"RMSE": 651.1440567037434,
"MAE": 448.99523765137667,
"R2": 0.8942640825672749
},
"cross_validation": {
"模型": "梯度提升树",
"RMSE均值": 655.1166606641779,
"RMSE标准差": 42.75460340121525,
"MAE均值": 466.577603572732,
"MAE标准差": 14.719481812429436,
"R2均值": 0.8818566774188483,
"R2标准差": 0.023087453303387278
},
"importance_method": "GradientBoostingRegressor 内置重要性",
"top_features": {
"气温": 0.38973625494728426,
"年": 0.2984069767787937,
"季节": 0.0932052430981216,
"体感温度": 0.07101028400532663,
"湿度": 0.05891447646010377,
"天气": 0.029590235728090393,
"风速": 0.026177907657402667,
"月": 0.01607120463407045
},
"future_prediction": {
"min": 1319,
"max": 6025,
"mean": 3775.15
},
"outputs": {
"figures": [
"/root/bike_course_design/outputs/figures/actual_vs_predicted.png",
"/root/bike_course_design/outputs/figures/feature_importance.png",
"/root/bike_course_design/outputs/figures/future_prediction_trend.png",
"/root/bike_course_design/outputs/figures/historical_trend.png",
"/root/bike_course_design/outputs/figures/residual_distribution.png"
],
"best_model": "/root/bike_course_design/outputs/models/best_bike_demand_model.pkl",
"prediction_excel": "/root/bike_course_design/outputs/2013前100天骑行量预测结果.xlsx"
}
}

View File

@@ -0,0 +1,2 @@
模型,RMSE均值,RMSE标准差,MAE均值,MAE标准差,R2均值,R2标准差
梯度提升树,655.1166606641779,42.75460340121525,466.577603572732,14.719481812429436,0.8818566774188483,0.023087453303387278
1 模型 RMSE均值 RMSE标准差 MAE均值 MAE标准差 R2均值 R2标准差
2 梯度提升树 655.1166606641779 42.75460340121525 466.577603572732 14.719481812429436 0.8818566774188483 0.023087453303387278

View File

@@ -0,0 +1,12 @@
,重要性
气温,0.38973625494728426
,0.2984069767787937
季节,0.0932052430981216
体感温度,0.07101028400532663
湿度,0.05891447646010377
天气,0.029590235728090393
风速,0.026177907657402667
,0.01607120463407045
星期,0.00832340663049113
工作日,0.004855667817542203
节假日,0.003708342242773187
1 重要性
2 气温 0.38973625494728426
3 0.2984069767787937
4 季节 0.0932052430981216
5 体感温度 0.07101028400532663
6 湿度 0.05891447646010377
7 天气 0.029590235728090393
8 风速 0.026177907657402667
9 0.01607120463407045
10 星期 0.00832340663049113
11 工作日 0.004855667817542203
12 节假日 0.003708342242773187

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 230 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

View File

@@ -0,0 +1,6 @@
模型,RMSE,MAE,R2
梯度提升树,651.1440567037434,448.99523765137667,0.8942640825672749
随机森林,703.9966504917254,450.0664823777129,0.8764025354772573
支持向量回归,738.1033329574317,529.5340809792003,0.8641365279788986
线性回归,796.4617765623753,583.0197588300691,0.8418029967286178
神经网络,809.7923656712804,609.9584660642089,0.836463110808386
1 模型 RMSE MAE R2
2 梯度提升树 651.1440567037434 448.99523765137667 0.8942640825672749
3 随机森林 703.9966504917254 450.0664823777129 0.8764025354772573
4 支持向量回归 738.1033329574317 529.5340809792003 0.8641365279788986
5 线性回归 796.4617765623753 583.0197588300691 0.8418029967286178
6 神经网络 809.7923656712804 609.9584660642089 0.836463110808386

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,148 @@
日期,真实总骑行量,预测总骑行量,残差
2011-01-03,1349,1577,-228.44
2011-01-11,1263,1199,64.1
2011-01-24,1416,1465,-49.06
2011-01-31,1501,1554,-52.9
2011-02-01,1360,950,410.36
2011-02-03,1550,1504,46.2
2011-02-09,1605,1455,150.06
2011-02-14,1913,2320,-406.86
2011-02-19,1635,1886,-251.38
2011-02-24,1807,1201,606.29
2011-02-25,1461,1134,327.36
2011-03-02,2134,2191,-56.9
2011-03-05,2077,2130,-53.29
2011-03-07,1872,1694,177.93
2011-03-08,2133,2248,-115.27
2011-03-11,1977,1676,300.91
2011-03-12,2132,2349,-217.47
2011-03-14,2046,2226,-179.8
2011-03-18,3239,4044,-804.76
2011-03-19,3117,3370,-253.27
2011-03-20,2471,2064,407.14
2011-03-21,2077,2692,-615.29
2011-03-23,2121,2103,18.4
2011-03-26,2496,2591,-94.52
2011-03-28,2028,2460,-431.86
2011-04-01,2227,1954,273.39
2011-04-08,1471,1773,-301.99
2011-04-12,2034,3717,-1682.75
2011-04-20,3944,4423,-478.72
2011-04-29,4595,4603,-8.29
2011-05-01,3351,3850,-499.24
2011-05-12,4864,4534,330.31
2011-05-14,3409,4328,-918.66
2011-05-16,3958,3809,148.76
2011-05-29,4788,4370,417.83
2011-06-05,4906,4317,588.98
2011-06-08,4401,4109,291.88
2011-06-14,4891,4949,-58.17
2011-06-15,5180,4954,225.68
2011-06-24,4991,5067,-75.7
2011-07-01,5362,5370,-7.84
2011-07-12,4258,4201,57.37
2011-07-16,5923,5163,760.0
2011-07-18,4458,4614,-156.27
2011-07-19,4541,4128,413.2
2011-07-30,4475,3982,493.17
2011-07-31,4302,3854,448.1
2011-08-01,4266,4499,-232.75
2011-08-07,3785,4459,-673.9
2011-08-09,4602,4177,424.81
2011-08-16,4725,4796,-70.79
2011-08-23,5895,4925,970.15
2011-08-24,5130,4796,334.21
2011-09-02,4727,4418,309.5
2011-09-05,3351,3770,-419.29
2011-09-12,4713,5118,-404.54
2011-09-17,4511,4179,331.63
2011-09-23,2395,3520,-1124.77
2011-09-24,5423,4669,754.39
2011-10-03,3570,3909,-338.53
2011-10-14,3644,4314,-669.66
2011-10-18,4748,4912,-164.41
2011-10-28,3747,3665,82.18
2011-10-30,3331,3567,-235.88
2011-11-03,3974,3630,344.25
2011-11-11,3368,2273,1094.97
2011-11-16,1817,2447,-630.11
2011-11-23,2566,3439,-873.31
2011-11-24,1495,2390,-895.02
2011-11-25,2792,3982,-1189.72
2011-11-28,3867,4444,-576.52
2011-12-02,3940,3851,88.98
2011-12-05,3811,3527,284.14
2011-12-07,705,1651,-946.28
2011-12-09,3620,3786,-165.78
2011-12-11,2743,2912,-169.37
2011-12-17,2739,2998,-259.32
2011-12-18,2431,2814,-383.19
2011-12-19,3403,3491,-88.36
2011-12-22,3068,2795,272.56
2011-12-23,2209,1710,498.59
2011-12-24,1011,2143,-1131.82
2011-12-27,1162,1523,-360.64
2012-01-04,2368,2742,-374.41
2012-01-05,3272,3353,-81.08
2012-01-11,2177,2724,-547.45
2012-01-16,2298,2465,-167.13
2012-02-04,2832,2833,-0.88
2012-02-09,3830,3233,597.12
2012-02-28,4363,4302,60.68
2012-03-01,4990,6011,-1020.55
2012-03-06,3956,3165,791.34
2012-03-10,4118,3709,408.86
2012-03-12,5298,5856,-558.23
2012-03-19,6153,6305,-151.59
2012-03-21,6230,6325,-95.16
2012-03-24,3372,5652,-2279.64
2012-03-25,4996,4696,300.25
2012-04-01,6041,4352,1688.79
2012-04-10,5918,6416,-497.99
2012-04-12,5409,4941,468.22
2012-04-13,6398,6410,-12.48
2012-04-14,7460,6934,526.08
2012-04-18,4367,5904,-1537.08
2012-04-25,6196,6642,-445.52
2012-05-28,6043,6531,-487.78
2012-05-29,5743,6794,-1050.6
2012-06-07,7494,7312,181.84
2012-06-11,6664,6500,164.39
2012-06-20,6211,6244,-32.85
2012-06-26,7442,6989,453.43
2012-06-29,5463,5797,-334.07
2012-07-04,7403,5172,2230.86
2012-07-11,7264,7181,82.86
2012-07-13,7499,7513,-13.89
2012-07-22,7410,6212,1198.03
2012-07-23,6966,6820,145.94
2012-07-26,6861,6310,551.18
2012-08-01,7580,6910,669.83
2012-08-03,7175,6090,1085.44
2012-08-18,7865,7339,526.24
2012-08-20,6530,6418,112.26
2012-08-24,7582,7232,350.11
2012-08-25,6053,6488,-435.36
2012-08-28,7040,7101,-60.93
2012-08-29,7697,7241,455.71
2012-08-30,7713,7287,425.85
2012-09-16,7333,7350,-16.69
2012-09-21,8167,7771,396.26
2012-09-24,7436,7609,-172.87
2012-09-25,7538,7702,-164.34
2012-10-05,8156,7832,323.83
2012-10-12,7282,7113,168.73
2012-10-29,22,3514,-3491.84
2012-11-06,5686,4806,879.7
2012-11-09,5992,5555,436.88
2012-11-14,5495,4837,657.66
2012-11-21,5146,5562,-416.47
2012-11-28,5260,4875,385.29
2012-11-30,5668,5264,404.17
2012-12-04,6606,6580,26.22
2012-12-12,5319,5046,272.6
2012-12-18,5557,5442,115.19
2012-12-19,5267,4985,281.86
2012-12-27,2114,1858,255.82
2012-12-28,3095,2515,579.82
2012-12-30,1796,1942,-145.6
1 日期 真实总骑行量 预测总骑行量 残差
2 2011-01-03 1349 1577 -228.44
3 2011-01-11 1263 1199 64.1
4 2011-01-24 1416 1465 -49.06
5 2011-01-31 1501 1554 -52.9
6 2011-02-01 1360 950 410.36
7 2011-02-03 1550 1504 46.2
8 2011-02-09 1605 1455 150.06
9 2011-02-14 1913 2320 -406.86
10 2011-02-19 1635 1886 -251.38
11 2011-02-24 1807 1201 606.29
12 2011-02-25 1461 1134 327.36
13 2011-03-02 2134 2191 -56.9
14 2011-03-05 2077 2130 -53.29
15 2011-03-07 1872 1694 177.93
16 2011-03-08 2133 2248 -115.27
17 2011-03-11 1977 1676 300.91
18 2011-03-12 2132 2349 -217.47
19 2011-03-14 2046 2226 -179.8
20 2011-03-18 3239 4044 -804.76
21 2011-03-19 3117 3370 -253.27
22 2011-03-20 2471 2064 407.14
23 2011-03-21 2077 2692 -615.29
24 2011-03-23 2121 2103 18.4
25 2011-03-26 2496 2591 -94.52
26 2011-03-28 2028 2460 -431.86
27 2011-04-01 2227 1954 273.39
28 2011-04-08 1471 1773 -301.99
29 2011-04-12 2034 3717 -1682.75
30 2011-04-20 3944 4423 -478.72
31 2011-04-29 4595 4603 -8.29
32 2011-05-01 3351 3850 -499.24
33 2011-05-12 4864 4534 330.31
34 2011-05-14 3409 4328 -918.66
35 2011-05-16 3958 3809 148.76
36 2011-05-29 4788 4370 417.83
37 2011-06-05 4906 4317 588.98
38 2011-06-08 4401 4109 291.88
39 2011-06-14 4891 4949 -58.17
40 2011-06-15 5180 4954 225.68
41 2011-06-24 4991 5067 -75.7
42 2011-07-01 5362 5370 -7.84
43 2011-07-12 4258 4201 57.37
44 2011-07-16 5923 5163 760.0
45 2011-07-18 4458 4614 -156.27
46 2011-07-19 4541 4128 413.2
47 2011-07-30 4475 3982 493.17
48 2011-07-31 4302 3854 448.1
49 2011-08-01 4266 4499 -232.75
50 2011-08-07 3785 4459 -673.9
51 2011-08-09 4602 4177 424.81
52 2011-08-16 4725 4796 -70.79
53 2011-08-23 5895 4925 970.15
54 2011-08-24 5130 4796 334.21
55 2011-09-02 4727 4418 309.5
56 2011-09-05 3351 3770 -419.29
57 2011-09-12 4713 5118 -404.54
58 2011-09-17 4511 4179 331.63
59 2011-09-23 2395 3520 -1124.77
60 2011-09-24 5423 4669 754.39
61 2011-10-03 3570 3909 -338.53
62 2011-10-14 3644 4314 -669.66
63 2011-10-18 4748 4912 -164.41
64 2011-10-28 3747 3665 82.18
65 2011-10-30 3331 3567 -235.88
66 2011-11-03 3974 3630 344.25
67 2011-11-11 3368 2273 1094.97
68 2011-11-16 1817 2447 -630.11
69 2011-11-23 2566 3439 -873.31
70 2011-11-24 1495 2390 -895.02
71 2011-11-25 2792 3982 -1189.72
72 2011-11-28 3867 4444 -576.52
73 2011-12-02 3940 3851 88.98
74 2011-12-05 3811 3527 284.14
75 2011-12-07 705 1651 -946.28
76 2011-12-09 3620 3786 -165.78
77 2011-12-11 2743 2912 -169.37
78 2011-12-17 2739 2998 -259.32
79 2011-12-18 2431 2814 -383.19
80 2011-12-19 3403 3491 -88.36
81 2011-12-22 3068 2795 272.56
82 2011-12-23 2209 1710 498.59
83 2011-12-24 1011 2143 -1131.82
84 2011-12-27 1162 1523 -360.64
85 2012-01-04 2368 2742 -374.41
86 2012-01-05 3272 3353 -81.08
87 2012-01-11 2177 2724 -547.45
88 2012-01-16 2298 2465 -167.13
89 2012-02-04 2832 2833 -0.88
90 2012-02-09 3830 3233 597.12
91 2012-02-28 4363 4302 60.68
92 2012-03-01 4990 6011 -1020.55
93 2012-03-06 3956 3165 791.34
94 2012-03-10 4118 3709 408.86
95 2012-03-12 5298 5856 -558.23
96 2012-03-19 6153 6305 -151.59
97 2012-03-21 6230 6325 -95.16
98 2012-03-24 3372 5652 -2279.64
99 2012-03-25 4996 4696 300.25
100 2012-04-01 6041 4352 1688.79
101 2012-04-10 5918 6416 -497.99
102 2012-04-12 5409 4941 468.22
103 2012-04-13 6398 6410 -12.48
104 2012-04-14 7460 6934 526.08
105 2012-04-18 4367 5904 -1537.08
106 2012-04-25 6196 6642 -445.52
107 2012-05-28 6043 6531 -487.78
108 2012-05-29 5743 6794 -1050.6
109 2012-06-07 7494 7312 181.84
110 2012-06-11 6664 6500 164.39
111 2012-06-20 6211 6244 -32.85
112 2012-06-26 7442 6989 453.43
113 2012-06-29 5463 5797 -334.07
114 2012-07-04 7403 5172 2230.86
115 2012-07-11 7264 7181 82.86
116 2012-07-13 7499 7513 -13.89
117 2012-07-22 7410 6212 1198.03
118 2012-07-23 6966 6820 145.94
119 2012-07-26 6861 6310 551.18
120 2012-08-01 7580 6910 669.83
121 2012-08-03 7175 6090 1085.44
122 2012-08-18 7865 7339 526.24
123 2012-08-20 6530 6418 112.26
124 2012-08-24 7582 7232 350.11
125 2012-08-25 6053 6488 -435.36
126 2012-08-28 7040 7101 -60.93
127 2012-08-29 7697 7241 455.71
128 2012-08-30 7713 7287 425.85
129 2012-09-16 7333 7350 -16.69
130 2012-09-21 8167 7771 396.26
131 2012-09-24 7436 7609 -172.87
132 2012-09-25 7538 7702 -164.34
133 2012-10-05 8156 7832 323.83
134 2012-10-12 7282 7113 168.73
135 2012-10-29 22 3514 -3491.84
136 2012-11-06 5686 4806 879.7
137 2012-11-09 5992 5555 436.88
138 2012-11-14 5495 4837 657.66
139 2012-11-21 5146 5562 -416.47
140 2012-11-28 5260 4875 385.29
141 2012-11-30 5668 5264 404.17
142 2012-12-04 6606 6580 26.22
143 2012-12-12 5319 5046 272.6
144 2012-12-18 5557 5442 115.19
145 2012-12-19 5267 4985 281.86
146 2012-12-27 2114 1858 255.82
147 2012-12-28 3095 2515 579.82
148 2012-12-30 1796 1942 -145.6