赞
踩
本项目选择使用Visual Studio Code(以下简称VS Code)作为主要的开发工具。
安装其他相关的工具和库,具体如下:
1.pandas(pd):提供了用于数据处理和分析的数据结构和工具,包括DataFrame和Series等,可以轻松地处理和操作结构化数据。
2. matplotlib.pyplot(plt):用于数据可视化,提供了绘制图表的函数,包括折线图、散点图、柱状图等,使数据可视化变得简单易用。
3. sklearn:用于机器学习。
4. requests、BeautifulSoup:用于爬虫程序获取数据集。
主程序:
1.数据加载:
检查训练数据和预测数据文件路径是否存在。
读取 CSV 文件中的数据。
2.数据预处理:
解析日期,并提取月份和日。
提取和解析最高气温和最低气温。
移除包含无效温度数据的行。
合并训练集和预测集,并对天气状况进行编码。
3.特征和标签选择:
选择特征列和标签列。
4.拆分数据集:
将数据集拆分为训练集和验证集。
5.训练回归模型:
初始化并训练多个回归模型。
6.创建和训练投票回归器:
创建一个投票回归器,并使用先前训练的模型进行训练。
7.预测:
对预测数据集进行预测。
8.绘制预测结果图表:
绘制真实温度和预测温度的折线图。
9.评估模型性能:
计算并显示各个模型的评估指标(MSE、MAE 和 R-squared)。
10.绘制评估指标图表:
绘制不同模型的 MSE、MAE 和 R-squared 的柱状图。
# 解析日期,并提取月份和日
train_data['日期'] = pd.to_datetime(train_data['日期'].str.replace('年', '-').str.replace('月', '-').str.replace('日', ''), format='%Y-%m-%d')
predict_data['日期'] = pd.to_datetime(predict_data['日期'].str.replace('年', '-').str.replace('月', '-').str.replace('日', ''), format='%Y-%m-%d')
train_data['月份'] = train_data['日期'].dt.month
train_data['日'] = train_data['日期'].dt.day
predict_data['月份'] = predict_data['日期'].dt.month
predict_data['日'] = predict_data['日期'].dt.day
通过这些步骤,原始的日期字符串被转换为 datetime 对象,并提取出月份和日期信息,生成新的列。这些新的列可以在后续的模型训练和预测过程中作为特征使用。
例:假设原始的 train_data 数据如下:
日期 | 天气状况 | 气温 |
---|---|---|
2011年01月01日 | 晴,/晴 | -9℃,/,0℃ |
2011年01月02日 | 多云,/阴 | -7℃,/,-2℃ |
调整后:
日期 | 天气状况 | 气温 | 月份 | 日 |
---|---|---|---|---|
2011年01月01日 | 晴,/晴 | -9℃,/,0℃ | 1 | 1 |
2011年01月02日 | 多云,/阴 | -7℃,/,-2℃ | 1 | 2 |
# 提取最高温度和最低温度
def parse_temperature(temp_str):
temps = temp_str.split(',/,')
if len(temps) == 2:
try:
return int(temps[0].replace('℃', '').strip()), int(temps[1].replace('℃', '').strip())
except ValueError:
return None, None
return None, None
通过这段代码,可以从原始的气温字符串中提取出具体的最低和最高气温,并将它们作为独立的列存储在数据框中。这些新列在后续的分析和建模过程中可以作为特征使用。
对‘天气状况’列进行编码,将其转换为数值类型,以便在后续的模型训练和预测过程中使用。具体步骤如下:
合并训练集和预测集:
combined_data = pd.concat([train_data, predict_data])
使用 pd.concat 将 train_data 和 predict_data 两个数据框按行进行合并,形成一个新的数据框 combined_data。
这样做的目的是为了确保 LabelEncoder 能够处理所有可能的‘天气状况’,无论它们出现在训练数据中还是预测数据中。
编码天气状况:
# 编码天气状况
le = LabelEncoder()
combined_data['天气状况'] = le.fit_transform(combined_data['天气状况'])
创建一个 LabelEncoder 实例 le。LabelEncoder 是sklearn.preprocessing 中的一个类,用于将分类数据转换为整数编码。
对 combined_data 数据框中的‘天气状况’列应用fit_transform方法。
fit_transform方法首先对数据进行拟合(即找到每个类别对应的整数编码),然后将这些类别转换为相应的整数编码。例如,如果‘天气状况’列包含类别 [“晴”, “多云”, “阴”],它们可能会被转换为 [0, 1, 2]。
拆分回原来的训练集和预测集:
# 拆分回原来的训练集和预测集
train_data = combined_data.iloc[:len(train_data)]
predict_data = combined_data.iloc[len(train_data):]
根据原始 train_data的长度,从 combined_data中提取出对应的行,重新形成 train_data。例如,如果原始 train_data有 100 行,则 combined_data.iloc[:100]将提取出前 100 行作为新的 train_data。
同样地,从 combined_data中提取出剩余的行,重新形成 predict_data。
到此为止,我们的数据已经全部处理完成,效果如下:
train_data:
日期 | 天气状况 | 气温 | 月份 | 日 | 最低气温 | 最高气温 |
---|---|---|---|---|---|---|
2011年01月01日 | 0 | -9℃,/,0℃ | 1 | 1 | -9 | 0 |
2011年01月02日 | 1 | -7℃,/,-2℃ | 1 | 2 | -7 | -2 |
predict_data:
日期 | 天气状况 | 气温 | 月份 | 日 | 最低气温 | 最高气温 |
---|---|---|---|---|---|---|
2021年01月01日 | 0 | -11℃,/,0℃ | 1 | 1 | -11 | 0 |
2021年01月02日 | 2 | -9℃,/,1℃ | 1 | 2 | -9 | 1 |
选择特征和标签:
# 选择特征和标签
X = train_data[['月份', '日', '天气状况', '最低气温']]
y = train_data['最高气温']
其中X为特征集,包含用于训练模型的所有特征。在这里,我们选择了 train_data中的’月份’、‘日’、‘天气状况’ 和’最低气温’列作为特征。
y为目标集,包含我们要预测的目标变量。在这里,我们选择了 train_data中的’最高气温’列作为目标变量。
拆分训练集和验证集:
# 拆分训练集和验证集
X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, random_state=42)
这是一个用于拆分数据集的函数,可以将数据集拆分为训练集和测试集(或验证集)。
test_size=0.2:表示将数据集中的 20% 分配给验证集,剩余 80% 作为训练集。
random_state=42:是一个随机种子,用于确保每次运行时都得到相同的随机分割结果。设置随机种子是为了使结果可重现,方便调试和比较不同模型的表现。
拆分后的数据如下:
X_train和y_train:训练集的特征和目标变量。
X_val和y_val:验证集的特征和目标变量。
这样做的目的是为了在训练模型时使用训练集,然后在验证集上评估模型的性能,以便调整模型的超参数或进行其他优化。
训练模型:
reg1 = GradientBoostingRegressor(random_state=1)
reg1.fit(X_train, y_train)
以梯度提升回归为例:
GradientBoostingRegressor是一个梯度提升回归器,属于集成学习算法的一种。random_state=1是设置随机种子的参数,用于确保每次运行时都得到相同的随机结果。设置随机种子可以使结果可重现,方便调试和比较不同模型的表现。
fit(X_train, y_train)是 GradientBoostingRegressor`类中的方法,用于训练模型。
X_train是训练集的特征数据,包含了模型用来学习的特征信息。通常是一个二维数组或数据框。
y_train是训练集的目标数据,包含了模型要预测的目标变量。通常是一个一维数组或序列。
训练模型的过程就是将特征数据和目标数据输入模型中,然后模型根据特征数据学习,调整自身的参数,使得预测结果尽可能地接近目标数据。在这个过程中,模型会根据损失函数的反馈逐步优化,直到达到预定义的停止条件。
一旦模型训练完成,它就可以用来对新的数据进行预测。在这里,reg1 就是一个已经训练好的梯度提升回归模型,可以用来对特征数据进行预测,并生成相应的目标变量预测值。
同上,我们一共训练了9个模型,为后续的比较作参考。
X_predict = predict_data[['月份', '日', '天气状况', '最低气温']]
pred1 = reg1.predict(X_predict)
dates = predict_data['日期']
plt.plot(dates, pred1, 'c-', label='SVR')
以SVR模型为例,在测试数据中,以’月份’, ‘日’, ‘天气状况’, '最低气温’为特征集,预测‘最高气温’,pred1为预测成功的样例。
plt.plot()是matplotlib.pyplot库中用于绘制折线图的函数,其中
dates是折线图的 x 轴数据,即日期信息。通常是一个时间序列或日期数组,用于表示横坐标。
pred4:这是折线图的 y 轴数据,即预测结果。通常是一个数组,用于表示纵坐标。
'c-'是用于定义折线的样式的字符串参数。其中 ‘c’表示折线的颜色,这里是青色;’-'表示折线的线型,这里是实线。
label='SVR’是用于图例的标签,用于标识折线的含义。在这里,SVR 表示支持向量回归器(SVR)模型的预测结果。
上述代码的作用是在图表中绘制一条折线,折线的 x 轴是日期信息,y 轴是使用支持向量回归器模型(SVR)预测的结果。折线的颜色是青色,线型是实线,并且在图例中添加了标签 ‘SVR’,以便在图表中说明这条折线代表的含义。
代码实例:
mse_scores = []
mae_scores = []
r2_scores = []
for pred in [pred1, pred2, pred3, pred4, pred5, pred6, pred7, pred8, pred9]:
mse_scores.append(mean_squared_error(true_temps, pred))
mae_scores.append(mean_absolute_error(true_temps, pred))
r2_scores.append(r2_score(true_temps, pred))
mse_scores = [mse_scores[i] for i in range(len(models))]
plt.subplot(1, 3, 1)
plt.barh(models, mse_scores, color='b')
plt.xlabel('MSE')
plt.title('Mean Squared Error')
本实验使用了9种不同的回归模型和集成学习算法来进行天气预测,上图为这9种模型的误差分析对比图
其中MSE、MAE 和 R-squared分别是均方误差,绝对平均误差和R平方值。均方误差和绝对平均误差越小、R平方值越接近1,代表预测误差越小。
基本弱学习器预测分析
在机器学习中,基本弱学习器(weak learner)是指那些相对简单、预测能力较弱的模型。尽管单个弱学习器的性能可能不佳,但通过集成方法将多个弱学习器组合起来,可以构建出强大的预测模型。
支持向量回归
预测效果:
可以看出,预测曲线的幅度相比于原始曲线幅度偏小,即预测曲线在取值上比较平均,这可能是因为构建超平面时对一些极值数据进行平均处理,导致预测曲线在极值附近效果不佳。
决策树回归
预测效果:
可以看出,预测曲线的幅度相比于原始曲线幅度偏大,预测噪声大,在极值附近尤为明显,这可能是因为数据集中的特征之间的交互影响较强,彼此之间有较高的关联性,因此预测误差较大。
线性回归
预测效果:
可以看出,对于天气预测这种周期性、非线性的关系的预测,线性回归预测误差大,是本实验9种模型中效果最差的一个。
K近邻回归
预测效果:
预测效果:
上图的n_neighbors(邻居数量)默认为5,可以看出,该模型对噪声的处理较差,如红色圆圈处所示。我们改变邻居数量再进行测试,并进行误差分析,运行结果如下:K值越大,模型越平滑,但可能欠拟合;K值越小,模型越灵活,但可能过拟合。
如上图所示,n_neighbors分别为5、8、3。由此看出,n_neighbors值越大,模型越平滑,但可能欠拟合;K值越小,模型越灵活,但可能过拟合。
小结:通过上述三种模型的预测效果和误差图来看,对于天气(最高气温)预测,单个弱学习器的性能不佳,误差较大。
集成算法预测分析
小结:综合以上全部模型来看,在天气预测实验当中,支持非线性的基本弱学习器效果好于支持线性的弱学习器,集成学习方法的效果普遍明显好于基本弱学习器,选择平均多种模型的集成学习方法效果好于集成单一弱学习器的集成学习方法,而串行迭代某一基本弱学习器的效果达到最优。
爬虫代码:
import requests from bs4 import BeautifulSoup import pandas as pd import os def get_data(url): try: resp = requests.get(url) if resp.status_code != 200: # 如果HTTP状态码不是200,意味着请求未成功,返回None return None html = resp.content.decode('gbk') soup = BeautifulSoup(html, 'html.parser') tr_list = soup.find_all('tr') dates, conditions, temps = [], [], [] for data in tr_list[1:]: # 跳过表头 sub_data = data.text.split() dates.append(sub_data[0]) conditions.append(','.join(sub_data[1:3])) temps.append(','.join(sub_data[3:6])) return pd.DataFrame({ '日期': dates, '天气状况': conditions, '气温': temps }) except Exception as e: print(f"Error fetching or parsing the data: {e}") return None def fetch_data(year, month): url = f'http://www.tianqihoubao.com/lishi/beijing/month/{year}{month:02d}.html' print(f'Fetching data for {year}-{month:02d}') return get_data(url) def append_to_csv(data, filename): # 将数据追加到CSV文件 try: data.to_csv(filename, mode='a', encoding='utf-8', index=False, header=not os.path.exists(filename)) print(f"Data for {data['日期'].iloc[0][:7]} appended successfully.") except Exception as e: print(f"Error appending data to CSV: {e}") def main(year, month): filename = 'E:\code\weather2\source.csv' data = fetch_data(year, month) if data is not None: append_to_csv(data, filename) else: print(f"No data available for {year}-{month:02d}") if __name__ == "__main__": year = 2013 # 指定的年份 month = 1 # 指定的月份 main(year, month)
主程序代码:
import pandas as pd import matplotlib.pyplot as plt from sklearn.model_selection import train_test_split from sklearn.ensemble import GradientBoostingRegressor, RandomForestRegressor, VotingRegressor from sklearn.linear_model import LinearRegression from sklearn.preprocessing import LabelEncoder from sklearn.svm import SVR from sklearn.neighbors import KNeighborsRegressor from sklearn.tree import DecisionTreeRegressor from sklearn.ensemble import ExtraTreesRegressor from sklearn.neural_network import MLPRegressor from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score import os # 读取数据 train_data_path = r'E:\code\weather2\source.csv' predict_data_path = r'E:\code\weather2\test.csv' # 确认路径是否存在 print("Train data exists:", os.path.exists(train_data_path)) print("Forecast data exists:", os.path.exists(predict_data_path)) train_data = pd.read_csv(train_data_path) predict_data = pd.read_csv(predict_data_path) # 解析日期,并提取月份和日 train_data['日期'] = pd.to_datetime(train_data['日期'].str.replace('年', '-').str.replace('月', '-').str.replace('日', ''), format='%Y-%m-%d') predict_data['日期'] = pd.to_datetime(predict_data['日期'].str.replace('年', '-').str.replace('月', '-').str.replace('日', ''), format='%Y-%m-%d') train_data['月份'] = train_data['日期'].dt.month train_data['日'] = train_data['日期'].dt.day predict_data['月份'] = predict_data['日期'].dt.month predict_data['日'] = predict_data['日期'].dt.day # 提取最高温度和最低温度 def parse_temperature(temp_str): temps = temp_str.split(',/,') if len(temps) == 2: try: return int(temps[0].replace('℃', '').strip()), int(temps[1].replace('℃', '').strip()) except ValueError: return None, None return None, None train_data[['最低气温', '最高气温']] = train_data['气温'].apply(lambda x: pd.Series(parse_temperature(x))) predict_data[['最低气温', '最高气温']] = predict_data['气温'].apply(lambda x: pd.Series(parse_temperature(x))) # 删除包含无效温度数据的行 train_data.dropna(subset=['最低气温', '最高气温'], inplace=True) predict_data.dropna(subset=['最低气温', '最高气温'], inplace=True) # 合并训练集和预测集以确保LabelEncoder能够处理所有可能的天气状况 combined_data = pd.concat([train_data, predict_data]) # 编码天气状况 le = LabelEncoder() combined_data['天气状况'] = le.fit_transform(combined_data['天气状况']) # 拆分回原来的训练集和预测集 train_data = combined_data.iloc[:len(train_data)] predict_data = combined_data.iloc[len(train_data):] # 选择特征和标签 X = train_data[['月份', '日', '天气状况', '最低气温']] y = train_data['最高气温'] # 拆分训练集和验证集 X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.2, random_state=42) # 训练回归模型 reg1 = GradientBoostingRegressor(random_state=1,n_estimators=200) reg2 = RandomForestRegressor(random_state=1) reg3 = LinearRegression() reg4 = SVR() reg5 = KNeighborsRegressor() reg6 = DecisionTreeRegressor(random_state=1) reg7 = ExtraTreesRegressor(random_state=1) reg8 = MLPRegressor(random_state=1,hidden_layer_sizes=(100, 100, 50)) reg1.fit(X_train, y_train) reg2.fit(X_train, y_train) reg3.fit(X_train, y_train) reg4.fit(X_train, y_train) reg5.fit(X_train, y_train) reg6.fit(X_train, y_train) reg7.fit(X_train, y_train) reg8.fit(X_train, y_train) # 创建和训练投票回归器 ereg = VotingRegressor([('gb', reg1), ('rf', reg2), ('lr', reg3), ('svr', reg4), ('knn', reg5), ('dt', reg6), ('et', reg7), ('mlp', reg8)]) ereg.fit(X_train, y_train) # 对预测数据进行预测 X_predict = predict_data[['月份', '日', '天气状况', '最低气温']] pred1 = reg1.predict(X_predict) pred2 = reg2.predict(X_predict) pred3 = reg3.predict(X_predict) pred4 = reg4.predict(X_predict) pred5 = reg5.predict(X_predict) pred6 = reg6.predict(X_predict) pred7 = reg7.predict(X_predict) pred8 = reg8.predict(X_predict) pred9 = ereg.predict(X_predict) # 获取日期信息 dates = predict_data['日期'] # 原始预测数据中的最高气温(真实值) true_temps = predict_data['最高气温'] # 绘制预测结果图表,使用折线图 plt.figure(figsize=(14, 7)) plt.plot(dates, true_temps, 'k-', label='True Temperature') # 原始数据 plt.plot(dates, pred1, 'g-', label='GradientBoostingRegressor') #梯度提升 plt.plot(dates, pred2, 'b-', label='RandomForestRegressor') #随机森林 plt.plot(dates, pred3, 'y-', label='LinearRegression') #线性回归 plt.plot(dates, pred4, 'c-', label='SVR') #支持向量回归 plt.plot(dates, pred5, 'm-', label='KNeighborsRegressor') # K 近邻回归 plt.plot(dates, pred6, 'orange', label='DecisionTreeRegressor') #决策树 plt.plot(dates, pred7, 'purple', label='ExtraTreesRegressor') plt.plot(dates, pred8, 'brown', label='MLPRegressor') #多层感知器回归 plt.plot(dates, pred9, 'r-', label='VotingRegressor') #投票 plt.xticks(rotation=45) plt.ylabel('Predicted Temperature') plt.xlabel('Date') plt.legend(loc="best") plt.title('Weather Prediction Comparison') mse_scores = [] mae_scores = [] r2_scores = [] for pred in [pred1, pred2, pred3, pred4, pred5, pred6, pred7, pred8, pred9]: mse_scores.append(mean_squared_error(true_temps, pred)) mae_scores.append(mean_absolute_error(true_temps, pred)) r2_scores.append(r2_score(true_temps, pred)) # 显示评估结果 models = ['GradientBoosting', 'RandomForest', 'LinearRegression', 'SVR', 'KNeighbors', 'DecisionTree', 'ExtraTrees', 'MLP', 'Voting'] for i in range(len(models)): print(f"Model: {models[i]}") print(f"MSE: {mse_scores[i]}") #均方误差 print(f"MAE: {mae_scores[i]}") #平均绝对误差 print(f"R-squared: {r2_scores[i]}") #R平方值 print() plt.show() models = ['GradientBoosting', 'RandomForest', 'LinearRegression', 'SVR', 'KNeighbors', 'DecisionTree', 'ExtraTrees', 'MLP', 'Voting'] # 定义评估指标 mse_scores = [mse_scores[i] for i in range(len(models))] mae_scores = [mae_scores[i] for i in range(len(models))] r2_scores = [r2_scores[i] for i in range(len(models))] # 绘制图表 plt.figure(figsize=(12, 6)) plt.subplot(1, 3, 1) plt.barh(models, mse_scores, color='b') plt.xlabel('MSE') plt.title('Mean Squared Error') plt.subplot(1, 3, 2) plt.barh(models, mae_scores, color='r') plt.xlabel('MAE') plt.title('Mean Absolute Error') plt.subplot(1, 3, 3) plt.barh(models, r2_scores, color='g') plt.xlabel('R-squared') plt.title('R-squared') plt.tight_layout() plt.show()
使用方法:先运行爬虫程序,通过修改指定年份和月份获得数据。
if __name__ == "__main__":
year = 2013 # 指定的年份
month = 1 # 指定的月份
main(year, month)
之后将会生成.csv文件。接下来拆分.CSV文件,这里我将2011年1月1日-2020年12月31日作为source.csv文件,2021年1月1日-2024年4月30日作为test.csv文件。
运行主程序前,先修改source.csv和test.csv文件的路径为正确地址,
train_data_path = r'E:\code\weather2\sourse.csv'
predict_data_path = r'E:\code\weather2\test.csv'
例如我的文件存在E:\code\weather2目录下。
最后运行主程序,显示折线图和柱形图。
感谢您的阅读,如有问题欢迎与作者联系。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。