目录
前言
- 🍨 本文为🔗365天深度学习训练营 中的学习记录博客
- 🍖 原作者:K同学啊
一、我的环境
- 电脑系统:Windows 11
- 语言环境:Python 3.9.7
- 编辑器:Jupyter Lab
二、随机森林代码实现
1. 导入库和数据
import pandas as pd
import numpy as np
#Seaborn库是基于Matplotlib的Python数据可视化库,提供了更高级的统计图表绘制功能
import seaborn as sns
#导入Matplotlib的pyplot模块,用于创建静态、交互式和动画的2D图表
import matplotlib.pyplot as plt
#LabelEncoder类用于将分类标签转换为数字编码
from sklearn.preprocessing import LabelEncoder
#train_test_split函数用于将数据集分割为训练集和测试集
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier
#classification_report函数用于显示主要的分类指标,如精确度、召回率、F1分数和支持度
from sklearn.metrics import classification_report
data = pd.read_csv('F:\\博士期间\\课程学习\\机器学习\\365训练营\\data\\weather_classification_data.csv')
data
2. 数据检查与预处理
#数据检查与预处理
# 查看数据信息
data.info()
#RangeIndex: 显示数据的索引范围,通常是从0开始的整数序列。
#Data columns: 列出所有列的名字。
#Non-null values: 显示每列非空值的数量。如果数据中存在缺失值,这里会显示一个小于数据总量的数字。
#Dtypes: 显示每列的数据类型,例如int64表示整数,float64表示浮点数,object通常表示字符串类型。
#如果你需要对数据进行预处理,根据data.info()提供的信息,你可能会执行以下操作:
#处理缺失值:使用如data.dropna()来删除含有缺失值的行,或者使用data.fillna()来填充缺失值。
#转换数据类型:如果发现数据类型的不匹配,可以使用data.astype()方法转换数据类型。
#筛选特定列:如果某些列不是分析所需的,可以使用data.drop()方法删除这些列。
#data.info()不会显示数据的具体内容,如果你需要查看数据的具体内容,可以使用data.head()来查看前五行,或者使用data.tail()来查看后五行。
# 查看分类特征的唯一值
#定义一个名为characteristic的列表,其中包含了要检查的分类特征的列名
characteristic = ['Cloud Cover','Season','Location','Weather Type']
#for循环的开始,它将遍历characteristic列表中的每个元素(即列名)
for i in characteristic:
#使用格式化字符串打印出当前遍历到的列名
print(f'{i}:')
#使用Pandas的unique()函数来找出DataFrame中第i列的所有唯一值,并将它们打印出来
print(data[i].unique())
#打印出50个连字符-,作为分隔符,以便在输出中清晰地区分不同特征的输出结果
print('-'*50)
import matplotlib.pyplot as plt
#FontProperties用于设置字体
from matplotlib.font_manager import FontProperties
# 使用中文支持的字体
#置图表中的字体为支持中文的字体,这里是SimHei(黑体)
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
# feature_map字典将数据集中的英文特征名称映射为中文描述
feature_map = {
'Temperature': '温度',
'Humidity': '湿度百分比',
'Wind Speed': '风速',
'Precipitation (%)': '降水量百分比',
'Atmospheric Pressure': '大气压力',
'UV Index': '紫外线指数',
'Visibility (km)': '能见度'
}
#设置绘图画布大小:创建一个大小为15x10英寸的画布
plt.figure(figsize=(15, 10))
#循环遍历每个特征并绘制箱线图
#使用enumerate(feature_map.items(), 1)遍历特征及其中文描述,并为每个子图分配一个序号i
for i, (col, col_name) in enumerate(feature_map.items(), 1):
#将画布分为2行4列的网格,并选择第i个子图的位置
plt.subplot(2, 4, i)
#绘制箱线图,其中data[col]是数据集中对应特征的列
sns.boxplot(y=data[col])
#设置图表的标题,显示特征的中文描述
plt.title(f'{col_name}的箱线图', fontsize=14)
#设置y轴标签为“数值”
plt.ylabel('数值', fontsize=12)
#为y轴添加虚线网格,透明度为0.7
plt.grid(axis='y', linestyle='--', alpha=0.7)
#自动调整子图参数,使之填充整个画布区域并且不互相重叠
plt.tight_layout()
plt.show()
1. 温度的异常值存在大量超出常识的温度,这里以超过60摄氏度认定为异常值,需要进行处理。
2. 湿度百分比和降水量百分比,由于数值存在超过100%的值,认为超过100%的值为异常值,需要进行处理。
3. 风速的高值可能是由于台风、龙卷风等极端天气事件,故不处理。
4. 大气压力的异常值可能由于高海拔地区或气象现象(如低气压系统)引起。
5. 能见度低可能是由于雾霾、雨雪等天气现象,这些异常值在特定条件下是正常的,故不处理。
#检查温度、湿度百分比和降雨量百分比超过特定阈值的数据点,并计算这些数据点的数量以及它们在总数据中所占的百分比
print(f"温度超过60°C的数据量:{data[data['Temperature'] > 60].shape[0]},占比{round(data[data['Temperature'] > 60].shape[0] / data.shape[0] * 100,2)}%。")
print(f"湿度百分比超过100%的数据量:{data[data['Humidity'] > 100].shape[0]},占比{round(data[data['Humidity'] > 100].shape[0] / data.shape[0] * 100,2)}%。")
print(f"降雨量百分比超过100%的数据量:{data[data['Precipitation (%)'] > 100].shape[0]},占比{round(data[data['Precipitation (%)'] > 100].shape[0] / data.shape[0] * 100,2)}%。")
异常值占比很小,这里可以直接删除,或者将其赋值为100%,为了保持数据集的一致性和准确性,这里选择直接删除,可以避免它们对分析结果或模型训练产生负面影响。
print("删前的数据shape:", data.shape)
data = data[(data['Temperature'] <= 60) & (data['Humidity'] <= 100) & (data['Precipitation (%)'] <= 100)]
print("删后的数据shape:", data.shape)
3. 数据分析
#数据分析
data.describe(include='all')
#count:每列的非空值数量。
#unique:每列中不同值的数量(对于非数值型列)。
#top:每列中出现次数最多的值(对于非数值型列)。
#freq:出现次数最多的值的频率(对于非数值型列)。
#创建一个宽20英寸、高15英寸的画布
plt.figure(figsize=(20, 15))
plt.subplot(3, 4, 1)
#histplot函数用于绘制数据的直方图
#kde=True:指定是否在直方图上绘制核密度估计线
#bins=20 表示将数据分成20个箱来绘制直方图
sns.histplot(data['Temperature'], kde=True,bins=20)
plt.title('温度分布')
plt.xlabel('温度')
plt.ylabel('频数')
plt.subplot(3, 4, 2)
sns.boxplot(y=data['Humidity'])
plt.title('湿度百分比箱线图')
plt.ylabel('湿度百分比')
plt.subplot(3, 4, 3)
sns.histplot(data['Wind Speed'], kde=True,bins=20)
plt.title('风速分布')
plt.xlabel('风速(km/h)')
plt.ylabel('频数')
plt.subplot(3, 4, 4)
sns.boxplot(y=data['Precipitation (%)'])
plt.title('降雨量百分比箱线图')
plt.ylabel('降雨量百分比')
plt.subplot(3, 4, 5)
#创建一个计数图(count plot)
sns.countplot(x='Cloud Cover', data=data)
plt.title('云量 (描述)分布')
plt.xlabel('云量 (描述)')
plt.ylabel('频数')
plt.subplot(3, 4, 6)
sns.histplot(data['Atmospheric Pressure'], kde=True,bins=10)
plt.title('大气压分布')
plt.xlabel('气压 (hPa)')
plt.ylabel('频数')
plt.subplot(3, 4, 7)
sns.histplot(data['UV Index'], kde=True,bins=14)
plt.title('紫外线等级分布')
plt.xlabel('紫外线指数')
plt.ylabel('频数')
plt.subplot(3, 4, 8)
#value_counts() 函数计算 'Season' 列中每个唯一值的出现次数
Season_counts = data['Season'].value_counts()
#plt.pie() 函数用于绘制饼图
#Season_counts 是要绘制的数值数据,即上面计算的每个季节的计数
#labels=Season_counts.index 指定饼图的每个扇区的标签,即季节的名称
#autopct='%1.1f%%' 指定扇区的文本格式,这里使用的格式会在每个扇区显示其占比,保留一位小数,并以百分比的形式表示
#设置饼图的起始角度为140度
plt.pie(Season_counts, labels=Season_counts.index, autopct='%1.1f%%', startangle=140)
plt.title('季节分布')
plt.subplot(3, 4, 9)
sns.histplot(data['Visibility (km)'], kde=True,bins=10)
plt.title('能见度分布')
plt.xlabel('能见度(Km)')
plt.ylabel('频数')
plt.subplot(3, 4, 10)
sns.countplot(x='Location', data=data)
plt.title('地点分布')
plt.xlabel('地点')
plt.ylabel('频数')
plt.subplot(3, 4, (11,12))
sns.countplot(x='Weather Type', data=data)
plt.title('天气类型分布')
plt.xlabel('天气类型')
plt.ylabel('频数')
plt.tight_layout()
plt.show()
● 温度:温度数据集中在较合理的范围内(主要在0°C到40°C),极端高温(>60°C)的数据已被清理。整体分布稍微左偏,说明较低温度的情况较多。
● 湿度:湿度分布在合理范围内(20%到100%),中位数和平均值接近,说明数据分布相对对称。
● 风速:数据集中在较低的风速范围内(0-20 km/h),极端高风速事件少见,数据左偏,低风速情况更为常见。
● 降水量:降水量分布较均匀,中位数为54%,反映了各种天气条件下的降水概率。
● 大气压力:大气压力主要集中在标准范围(990-1020 hPa),数据分布正常,没有明显的异常值。
● 紫外线指数:紫外线指数大多较低,极端高指数的情况罕见,表明大部分时间的紫外线风险较低。
● 能见度:能见度数据大多集中在5 km左右,反映了多数情况下的中等能见度条件。
● 云量:多云(overcast)在数据集中出现频率较高。
● 季节分布:冬季数据最多,可能是数据采集季节或地区气候特征的反映。
● 地点分布:主要来自山区和内陆地区,这可能影响天气类型和其他气象特征的分布。
● 天气类型:分布比较均匀,没有单一类别占据绝对优势。
4. 随机森林
#随机森林
#复制数据:复制原始数据集data,以避免在预处理时直接修改原始数据
new_data = data.copy()
#创建一个空字典来存储每个分类特征的LabelEncoder实例
label_encoders = {}
#分类特征列表:定义一个列表,包含所有需要进行标签编码的分类特征名称
categorical_features = ['Cloud Cover', 'Season', 'Location', 'Weather Type']
#循环进行标签编码:对于列表中的每个分类特征,创建一个LabelEncoder实例,
#用它来拟合并转换数据中的类别标签,然后将转换后的数据存储在new_data中,
#同时将LabelEncoder实例存储在label_encoders字典中。
for feature in categorical_features:
le = LabelEncoder()
new_data[feature] = le.fit_transform(data[feature])
label_encoders[feature] = le
for feature in categorical_features:
print(f"'{feature}'特征的对应关系:")
for index, class_ in enumerate(label_encoders[feature].classes_):
print(f" {index}: {class_}")
# 构建特征矩阵X和目标向量y
#从new_data中去除'Weather Type'列,将其作为目标向量y,剩余部分作为特征矩阵x
x = new_data.drop(['Weather Type'],axis=1)
y = new_data['Weather Type']
# 划分数据集
#使用train_test_split函数将数据集划分为训练集和测试集
x_train,x_test,y_train,y_test = train_test_split(x,y,
test_size=0.3,
random_state=15)
# 构建随机森林模型
rf_clf = RandomForestClassifier(random_state=15)
rf_clf.fit(x_train, y_train)
# 使用随机森林进行预测
y_pred_rf = rf_clf.predict(x_test)
#使用classification_report函数生成分类报告,其中包括精确度、召回率、F1分数和支持度等指标,并打印出来
class_report_rf = classification_report(y_test, y_pred_rf)
print(class_report_rf)
5. 结果分析
#结果分析
#提取特征重要度:
#从训练好的随机森林模型rf_clf中提取特征重要度
feature_importances = rf_clf.feature_importances_
#创建一个新的Pandas DataFrame features_rf,包含两列:'特征'和'重要度'。
#'特征'列包含特征矩阵x的列名,'重要度'列包含对应的特征重要度得分。
features_rf = pd.DataFrame({'特征': x.columns, '重要度': feature_importances})
#按重要度排序:
features_rf.sort_values(by='重要度', ascending=False, inplace=True)
plt.figure(figsize=(10, 8))
sns.barplot(x='重要度', y='特征', data=features_rf)
plt.xlabel('重要度')
plt.ylabel('特征')
plt.title('随机森林特征图')
plt.show()
随机森林模型的预测准确率很高,并且通过特征度分析,发现影响模型的主要因素有:温度、湿度、紫外线指数、能见度、大气压力。
三、心得体会
1. 分类特征的唯一值
1.1 定义
-
定义:分类特征的唯一值指的是在数据集中,某个分类特征(即标签或类别型的变量)所具有的不同值的集合。这些值是互不相同的,并且代表了该特征所有可能的类别或分组。
-
关于分类特征唯一值的要点:
互不相同:分类特征的唯一值集合中的每个值都是独一无二的,没有重复。
类别标签:在分类问题中,分类特征通常用作标签,用于区分不同的类别或组。
数据类型:分类特征通常存储为字符串、整数或特定的枚举类型,具体取决于数据的表示方式。
特征的重要性:分类特征的唯一值有助于确定特征在数据集中的重要性,以及它们对模型预测能力的贡献。
数据分布:分类特征的唯一值可以揭示数据在不同类别上的分布情况,有助于识别数据集中的不平衡问题。
特征工程:了解分类特征的唯一值有助于进行特征工程,比如特征编码、独热编码(One-Hot Encoding)等。
模型训练:在训练分类模型时,了解分类特征的唯一值有助于模型学习如何区分不同的类别。
数据预处理:在数据预处理阶段,查看分类特征的唯一值有助于识别和处理缺失值、错误数据或不一致的数据输入。
可视化分析:分类特征的唯一值可以用于数据可视化,比如制作饼图、柱状图等,以直观展示不同类别的分布。
算法需求:某些机器学习算法可能需要将分类特征转换为数值型,以便算法可以处理,如使用Label Encoding或One-Hot Encoding。
1.2 查看分类特征的唯一值
- 理解数据分布:通过查看分类特征的唯一值,可以对数据集中各类别的分布有一个直观的了解,这有助于识别数据中的模式或异常情况
- 特征工程:了解分类特征的唯一值有助于进行特征工程,比如确定是否需要对某些类别进行重新编码或合并,以提高模型的性能
- 处理不平衡数据:如果某些分类特征的值非常不均衡,可能需要采取措施如重采样来平衡数据,以避免模型偏向于多数类
- 特征重要性评估:随机森林算法能够评估特征的重要性,查看分类特征的唯一值有助于理解为什么某些特征可能比其他特征更重要
- 数据预处理:在将数据集用于随机森林模型之前,查看分类特征的唯一值可以帮助识别数据清洗和预处理的需求,例如处理缺失值或错误数据
- 提高模型泛化能力:随机森林通过集成多个决策树来降低过拟合的风险,查看分类特征的唯一值有助于选择那些对提高模型泛化能力有积极作用的特征
- 特征选择:随机森林模型在拟合数据后,可以提供一个变量重要性的度量,即feature_importances_参数。这个参数返回一个数组,表示随机森林模型认为训练特征的重要程度。查看分类特征的唯一值可以辅助这一过程,帮助识别对预测准确性影响较大的特征
- 模型解释性:随机森林提供了特征重要性的评估,这对于理解数据、进行特征选择和解释模型预测都非常有用
2. enumerate 函数
for i, (col, col_name) in enumerate(feature_map.items(), 1):
(col, col_name) 是通过遍历 feature_map 字典的项生成的元组。这个字典将数据集中的天气特征列名映射为更易于理解的中文名称。下面是对 (col, col_name) 的详细解释:
col:这是数据集中的特征列名(键),例如 'Temperature'、'Humidity' 等。
col_name:这是 col 对应的中文描述(值),例如 '温度'、'湿度百分比' 等。
在代码中,enumerate(feature_map.items(), 1) 函数用于遍历 feature_map 字典的每一项。enumerate 函数的一般形式是 enumerate(iterable, start),其中 iterable 是一个可迭代对象,start 是计数的起始值,默认为0。这里使用 feature_map.items() 获取字典的键值对,然后 enumerate 从1开始对每个项进行计数。
feature_map.items():返回一个包含字典中所有(键,值)对的迭代器。
enumerate(feature_map.items(), 1):返回一个迭代器,产生索引(从1开始)和 feature_map 中的项(键值对)。
在循环中,每次迭代都会从 enumerate 产生一个元组,其中包含当前项的索引(i)和实际的键值对(col, col_name)。这样,代码就可以使用 col 来访问数据 data 中相应的列,并使用 col_name 来设置箱线图的标题。
例如,如果 feature_map 如下所示:
feature_map = {
'Temperature': '温度',
'Humidity': '湿度百分比',
# ... 其他特征
}
那么在循环中第一次迭代时,(col, col_name) 将会是 (‘Temperature’, ‘温度’),第二次迭代时将会是 (‘Humidity’, ‘湿度百分比’),以此类推。这样,每个箱线图将会展示数据集 data 中相应特征的分布情况,并且图表的标题将会是该特征的中文描述。
3. 箱线图
箱线图(Boxplot),又称为盒须图或盒状图,是一种用于展示一组数据分布特征的统计图表。它能够提供数据的最小值、第一四分位数(Q1)、中位数(Q2)、第三四分位数(Q3)和最大值的摘要统计信息,并且能够直观地展示数据的异常值。
-
箱体(Box):
箱线图中的箱体从第一四分位数(Q1)延伸到第三四分位数(Q3)。
箱体的长度(IQR,Interquartile Range,四分位距)表示数据的中间50%分布在多大的数值范围内。 -
中位线(Median Line):
箱体内的水平线表示数据的中位数,即第二四分位数(Q2),将数据集分为上下两个部分,各占总数据量的50%。
-
须(Whiskers):
须是箱线图从箱体延伸出去的两条线,表示数据的分布范围。
通常,须的长度不会超过1.5倍的IQR(Q3 + 1.5 * IQR 和 Q1 - 1.5 * IQR)。
如果数据点超出这个范围,它们通常被认为是异常值。 -
异常值(Outliers):任何位于须之外的数据点都被认为是异常值,它们用单独的点表示,可以是圆点、星号或其他标记。
-
数据分布:箱线图可以展示数据的偏斜程度。如果箱体和中位线偏向一侧,这表明数据分布可能是偏斜的。
-
比较:当有多个箱线图并排放置时,可以比较不同组或类别的数据分布特征,如集中趋势和离散程度。
-
数据的偏斜性(Skewness):如果箱线图的箱体和中位线明显偏向一侧,这可能表明数据分布是偏斜的。
4. 其他
- 核密度估计线,提供了数据分布的平滑曲线,可以帮助观察数据的密度和形状。这种类型的图表对于理解数据的分布特征非常有用,比如识别数据的集中趋势、偏斜性或异常值。核密度估计部分提供了数据分布的平滑视图,而直方图则提供了具体的频率计数。