森林火灾是一种突发性强、破坏性大、处置救助较为困难的自然灾害。森林火灾不仅烧毁林木,直接减少森林面积,而且严重破坏森林结构和森林环境,导致森林生态系统失去平衡,森林生物量下降,生产力减弱,益兽益鸟减少,甚至造成人畜伤亡。本案例通过探索性分析(EDA)和机器学习构建线性回归和决策树模型,预测森林火灾的面积,并分析什么特征是发生森林火灾的重要因素。
我们采用2007年葡萄牙蒙特西尼奥公园森林火灾的相关数据。该数据集来自UCI机器学习库,数据中包含地理位置、天气等信息,具体的变量描述如下表所示:
变量名 | 变量解释 |
---|---|
X |
公园地图的X轴空间坐标 |
Y |
公园地图的Y轴空间坐标 |
month |
火灾发生的月份 |
day |
火灾发生的星期 |
FFMC |
FWI系统的FFMC指数 |
DMC |
FWI系统的DMC指数 |
DC |
FWI系统的DC指数 |
ISI |
FWI系统的ISI指数 |
temp |
摄氏温度 |
RH |
相对湿度 |
wind |
风速 |
rain |
降雨量 |
area |
火灾面积 |
其中FWI是指火险气候指数(Fire Weather Index)。FWI系统中,FFMC指数是细小可燃物湿度码,代表森林细小可燃物的含水率;DMC指数是粗腐殖质湿度码,代表森林腐殖质上层的地表可燃物的含水率;DC指数是干旱码,是长期干旱对森林可燃物的影响的指数;ISI指数是初始蔓延指数,代表了火势蔓延的等级。
进行数据分析之前,我们首先从文件中加载数据集:
# 忽略警告信息
import warnings
warnings.filterwarnings("ignore")
import pandas as pd
fire = pd.read_csv('./input/forestfires.csv')
使用info()
函数输出数据集的基本信息,查看特征的类型:
fire.info()
从输出结果可以看出,该数据集一共有517条数据,包含13个变量,不存在缺失值。注意到month
和day
这两个特征不是数值型的数据,X
、Y
和RH
这三个特征是整型数据,其余的特征是浮点型数据。
为了更具体地查看数据的形式,对数据集中的数据建立直观的感受,我们用head()
函数输出数据集中的前5行数据:
fire.head()
从输出结果可以看出,month
特征的数据是月份的小写英文缩写,day
特征的数据则是星期的小写英文缩写。
下一步,我们用describe()
函数查看数据集中火灾面积的基本统计信息,包括均值,标准差,最小值,最大值和四分位数,并存储在area_des
中:
area_des = fire['area'].describe()
area_des
注意到火灾面积的标准差为63.66,与均值12.85相比过大,这说明数据之间的离散程度较大。
利用are_des
中的数据,我们计算数据集中火灾面积数据的极差和四分位数间距:
area_range = area_des['max'] - area_des['min']
print("数据集中火灾面积数据的极差:",area_range)
quartile_deviation = area_des['75%'] - area_des['25%']
print("数据集中火灾面积数据的四分位数间距:", quartile_deviation)
结果表明,数据集中火灾面积数据的极差很大,但四分位数间距很小,说明该列数据中存在一些离群点。
我们采用更直观的方法,将数据集中火灾面积这一列的数据通过直方图和箱线图进行展示,来查看数据的分布情况:
import matplotlib.pyplot as plt
%matplotlib inline
plt.rcParams['axes.unicode_minus'] = False #用于在图中显示负号
# 通过分布直方图查看area列数据的分布情况
fire['area'].plot(kind='hist', title='histogram')
# 通过箱线图查看area列数据的分布情况
fire['area'].plot.box()
plt.grid(linestyle="--", alpha=0.5)
plt.show()
从上图中我们可以看出,数据主要分布在0~200之间,但也有少量数据大于600,area
数据中存在一些离群点。
下面,我们集中对刚刚发现的问题进行处理。首先,我们将数据集中火灾面积大于600的离群点去掉:
fire = fire[fire["area"]<600]
然后我们将month
和day
列的数据转换成数值型:
# 将月份转换成数值型
fire['month'] = fire['month'].map({'jan':1, 'feb':2, 'mar':3, 'apr':4, 'may':5, 'jun':6,
'jul':7, 'aug':8, 'sep':9, 'oct':10, 'nov':11, 'dec':12})
# 将星期转换成数值型
fire['day'] = fire['day'].map({'mon':1, 'tue':2, 'wed':3, 'thu':4, 'fri':5, 'sat':6, 'sun':7})
# 查看转换后的数据
fire.head()
处理完毕后,我们将火灾面积大于0的数据从数据集中筛选出来,按月份分组,通过条形图和核密度图进行展示,查看在每个月份发生火灾的次数分布情况。
# 查看火灾面积不为0的数据
area_not_0 = fire[fire['area']!= 0]
#火灾发生时间的月份分布
month_counts = area_not_0['month'].value_counts()
month_counts.sort_index(inplace=True)
print(month_counts)
month_counts.plot(kind='bar', color = 'b')
plt.xlabel("month")
plt.ylabel("counts")
plt.grid(linestyle = '--', alpha = 0.5)
plt.show()
# 绘制火灾发生时间的月份分布的核密度图
area_not_0['month'].sort_index().plot(kind = 'kde')
plt.grid(linestyle = '--', alpha = 0.5)
plt.xlim(1,12)
plt.xlabel("month")
从两张图中可以看出,在8月份和9月份火灾发生的次数最多。
接着我们按星期进行分组,然后通过折线图进行展示,查看发生火灾的次数情况:
# 火灾发生时间的星期分布
day_counts = area_not_0['day'].value_counts()
day_counts.sort_index(inplace=True)
print(day_counts)
day_counts.plot(kind='line', color = 'r')
plt.grid(linestyle = '--', alpha = 0.5)
plt.xlabel("day")
plt.ylabel("counts")
plt.show()
从上图可以看出,在周五周六周日火灾发生的次数最多。
最后,我们绘制火灾发生的面积与FWI系统的FFMC指数、DMC指数、DC指数、ISI指数之间的散点图,查看火灾发生的面积与这些指数之间的关系:
ffmc = fire["FFMC"]
dmc = fire["DMC"]
dc = fire["DC"]
isi = fire["ISI"]
area = fire['area']
fig = plt.figure(figsize = (10,10))
ax1 = fig.add_subplot(221)
ax1.scatter(ffmc, area)
ax1.set_xlabel("FFMC指数")
ax1.set_ylabel("area")
ax1.set_title("火灾面积与FFCM指数之间的散点图")
ax2 = fig.add_subplot(222)
ax2.scatter(dmc, area, color = 'r')
ax2.set_xlabel("DMC指数")
ax2.set_ylabel("area")
ax2.set_title("火灾面积与DMC指数之间的散点图")
ax3 = fig.add_subplot(223)
ax3.scatter(dc, area, color = 'g')
ax3.set_xlabel("DC指数")
ax3.set_ylabel("area")
ax3.set_title("火灾面积与DC指数之间的散点图")
ax4 = fig.add_subplot(224)
ax4.scatter(isi, area, color = 'y')
ax4.set_xlabel("ISI指数")
ax4.set_ylabel("area")
ax4.set_title("火灾面积与ISI指数之间的散点图")
plt.show()
从上面的散点图中,我们发现火灾发生的面积与FFMC指数、DMC指数、DC指数、ISI指数之间并没有明显的相关性,下面我们通过计算火灾发生的面积与这些指数之间的Perason相关系数,通过相关系数查看数据之间的相关程度:
# 计算Pearson相关系数
corr_data = fire[['FFMC', 'DMC', 'DC', 'ISI', 'area']]
corr_data.corr()
通过上面的相关系数矩阵,我们发现,火灾发生的面积与FFMC指数、DMC指数、DC指数、ISI指数之间并没有明显的相关性。但我们发现FFMC指数和ISI指数,DMC指数和DC指数之间的相关性较强。
我们使用sklearn
中的MinMaxScaler
方法,对数据集中除了X
、Y
、month
、day
之外的列做Min-Max标准化,使得处理后的数据取值分布在[0,1]区间上:
# 最小—最大标准化处理
from sklearn.preprocessing import MinMaxScaler
fire.ix[:, 4:] = MinMaxScaler().fit_transform(fire.ix[:, 4:])
# 再次查看前5行
fire.head(5)
在训练模型之前需要将数据集划分为训练集和测试集,用训练集训练模型,用测试集评估模型:
# 数据集划分
# 目标特征分离
X = fire.iloc[:, :12]
y = fire['area']
# 将数据集划分为训练集和测试集,其中训练集占80%,测试集占20%
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=10)
print("训练集大小:",X_train.shape, "测试集大小:", X_test.shape)
查看我们使用的训练集:
X_train.head()
我们使用线性回归算法训练模型,然后用训练的模型在测试集上进行火灾面积的预测,使用均方误差(MSE)评估模型的性能:
# 使用训练集训练模型并在测试集上测试模型的性能
from sklearn.linear_model import LinearRegression
# 使用线性回归训练模型
model_LR = LinearRegression()
model_LR.fit(X_train, y_train)
pre_LR = model_LR.predict(X_test)
# 在测试集上评估模型的性能
# 计算均方误差
from sklearn.metrics import mean_squared_error
mse_LR = mean_squared_error(pre_LR, y_test)
print(mse_LR)
from sklearn.metrics import r2_score
print(r2_score(y_test, pre_LR))
输出线性回归模型中的系数,并绘制模型中每个特征的系数图,查看在该模型中每个特征对火灾面积的影响程度:
# 输出模型系数
pd.Series(model_LR.coef_, index=X.columns)
# 绘制模型中每个特征的系数图
pd.Series(model_LR.coef_, index=X.columns).plot(kind='barh', color='r')
我们使用决策树算法训练模型,然后用训练的模型在测试集上进行火灾面积的预测,使用均方误差(MSE)评估模型的性能:
# 构建决策树模型
# 使用训练集训练模型并在测试集上测试模型的性能
from sklearn.tree import DecisionTreeRegressor # 回归
from sklearn.tree import DecisionTreeClassifier # 分类
# 使用决策树构建模型
model_DF = DecisionTreeRegressor(max_depth=5, random_state=10)
model_DF.fit(X_train, y_train) # 训练
pre_DF = model_DF.predict(X_test) # 测试集上预测
# 在测试集上评估模型的性能
# 计算均方误差
mse_DF = mean_squared_error(pre_DF, y_test)
print(mse_DF)
我们将生成的决策树模型可视化,输出决策树模型的树结构,查看决策树模型的构建过程。图中每个节点包含的信息有:
# 导入必要库
from sklearn.tree import export_graphviz
from sklearn.externals.six import StringIO
from IPython.display import Image
import pydotplus
# 输出图片到dot文件
export_graphviz(model_DF, out_file='tree.dot',
feature_names=X_train.columns,
rounded=True, precision = 1)
# 使用dot文件构造树形图
graph= pydotplus.graph_from_dot_file('tree.dot')
Image(graph.create_png())
接着,我们输出该决策树模型中的特征重要性,查看在该模型中每个特征对火灾面积的影响程度:
pd.Series(model_DF.feature_importances_, index=X.columns).plot(kind='barh', color='g')
通过上图,我们可以发现在构建的决策树模型中,影响火灾发生面积的最重要的特征是ISI
指数和temp
摄氏温度。
本案例使用2007年葡萄牙蒙特西尼奥公园的相关数据进行森林火灾面积的预测,首先使用探索性数据分析探究数据集的基本情况、数据的分布情况以及数据之间的相关性等,然后使用线性回归和决策树进行模型的构建和模型的评估。