我们给出了 2015 年至 2020 年印度 AQI 数据集。任务是建立一个模型,可以根据数据集中可用的独立特征准确预测 AQI。
颗粒物
颗粒物是固体和液体的混合物,包括碳、复杂有机化学品、硫酸盐、硝酸盐、矿物粉尘和悬浮在空气中的水。根据其尺寸(以微米为单位),它们分为 PM 2.5 和 PM 10。
氮氧化物
氮氧化物由氮和氧组成,归类为NOx气体,最常见的是一氧化氮(NO)和二氧化氮(NO2)。它们主要是化石燃料燃烧的结果。
氨
氨是大气中最丰富的碱性气体。 NH3 在大气颗粒物的形成中起着重要作用。氨被认为是农业和工业的副产品
二氧化硫
二氧化硫是一种无色、有强烈气味的气体,当煤炭和石油等含硫燃料燃烧时会形成二氧化硫,造成空气污染。当它们与大气中的物质发生反应形成酸雨时,会影响环境。
臭氧
地面臭氧是一种无色且高度刺激性的气体,形成于地球表面上方。
一氧化碳
含碳燃料的不完全燃烧会产生一氧化碳。一氧化碳是一种无色、剧毒的气体。
苯
室内的苯来自胶水、油漆、家具蜡、清洁剂等含苯产品。
甲苯
机动车和工业排放是污染物的主要来源。
二甲苯
机动车排放是城市空气环境中二甲苯的主要来源。
# 导入重要的库
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
df_city_day = pd.read_csv('city_day.csv')
df_city_day.head()
City | Date | PM2.5 | PM10 | NO | NO2 | NOx | NH3 | CO | SO2 | O3 | Benzene | Toluene | Xylene | AQI | AQI_Bucket | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | Ahmedabad | 2015-01-01 | NaN | NaN | 0.92 | 18.22 | 17.15 | NaN | 0.92 | 27.64 | 133.36 | 0.00 | 0.02 | 0.00 | NaN | NaN |
1 | Ahmedabad | 2015-01-02 | NaN | NaN | 0.97 | 15.69 | 16.46 | NaN | 0.97 | 24.55 | 34.06 | 3.68 | 5.50 | 3.77 | NaN | NaN |
2 | Ahmedabad | 2015-01-03 | NaN | NaN | 17.40 | 19.30 | 29.70 | NaN | 17.40 | 29.07 | 30.70 | 6.80 | 16.40 | 2.25 | NaN | NaN |
3 | Ahmedabad | 2015-01-04 | NaN | NaN | 1.70 | 18.48 | 17.97 | NaN | 1.70 | 18.59 | 36.08 | 4.43 | 10.14 | 1.00 | NaN | NaN |
4 | Ahmedabad | 2015-01-05 | NaN | NaN | 22.10 | 21.42 | 37.76 | NaN | 22.10 | 39.33 | 39.31 | 7.01 | 18.89 | 2.78 | NaN | NaN |
df_city_day.shape
(29531, 16)
df_city_day.describe()
PM2.5 | PM10 | NO | NO2 | NOx | NH3 | CO | SO2 | O3 | Benzene | Toluene | Xylene | AQI | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
count | 24933.000000 | 18391.000000 | 25949.000000 | 25946.000000 | 25346.000000 | 19203.000000 | 27472.000000 | 25677.000000 | 25509.000000 | 23908.000000 | 21490.000000 | 11422.000000 | 24850.000000 |
mean | 67.450578 | 118.127103 | 17.574730 | 28.560659 | 32.309123 | 23.483476 | 2.248598 | 14.531977 | 34.491430 | 3.280840 | 8.700972 | 3.070128 | 166.463581 |
std | 64.661449 | 90.605110 | 22.785846 | 24.474746 | 31.646011 | 25.684275 | 6.962884 | 18.133775 | 21.694928 | 15.811136 | 19.969164 | 6.323247 | 140.696585 |
min | 0.040000 | 0.010000 | 0.020000 | 0.010000 | 0.000000 | 0.010000 | 0.000000 | 0.010000 | 0.010000 | 0.000000 | 0.000000 | 0.000000 | 13.000000 |
25% | 28.820000 | 56.255000 | 5.630000 | 11.750000 | 12.820000 | 8.580000 | 0.510000 | 5.670000 | 18.860000 | 0.120000 | 0.600000 | 0.140000 | 81.000000 |
50% | 48.570000 | 95.680000 | 9.890000 | 21.690000 | 23.520000 | 15.850000 | 0.890000 | 9.160000 | 30.840000 | 1.070000 | 2.970000 | 0.980000 | 118.000000 |
75% | 80.590000 | 149.745000 | 19.950000 | 37.620000 | 40.127500 | 30.020000 | 1.450000 | 15.220000 | 45.570000 | 3.080000 | 9.150000 | 3.350000 | 208.000000 |
max | 949.990000 | 1000.000000 | 390.680000 | 362.210000 | 467.630000 | 352.890000 | 175.810000 | 193.860000 | 257.730000 | 455.030000 | 454.850000 | 170.370000 | 2049.000000 |
数据似乎没有任何异常值
#数据摘要
df_city_day.info()
<class 'pandas.core.frame.DataFrame'> RangeIndex: 29531 entries, 0 to 29530 Data columns (total 16 columns): # Column Non-Null Count Dtype --- ------ -------------- ----- 0 City 29531 non-null object 1 Date 29531 non-null object 2 PM2.5 24933 non-null float64 3 PM10 18391 non-null float64 4 NO 25949 non-null float64 5 NO2 25946 non-null float64 6 NOx 25346 non-null float64 7 NH3 19203 non-null float64 8 CO 27472 non-null float64 9 SO2 25677 non-null float64 10 O3 25509 non-null float64 11 Benzene 23908 non-null float64 12 Toluene 21490 non-null float64 13 Xylene 11422 non-null float64 14 AQI 24850 non-null float64 15 AQI_Bucket 24850 non-null object dtypes: float64(13), object(3) memory usage: 3.6+ MB
由于我们的日期列是对象类型,因此我们需要将其转换为日期时间格式。
#将日期转换为日期时间格式
df_city_day['Date']=pd.to_datetime(df_city_day['Date'])
检查空值
#检查空值
df_city_day.isna().sum()
City 0 Date 0 PM2.5 4598 PM10 11140 NO 3582 NO2 3585 NOx 4185 NH3 10328 CO 2059 SO2 3854 O3 4022 Benzene 5623 Toluene 8041 Xylene 18109 AQI 4681 AQI_Bucket 4681 dtype: int64
因为,我们有很多空值。放弃它们并不是一个好的选择,而是我们可以用中值代替它,因为对于特定城市,特征值将遵循类似的趋势。
# 替换空值
null_col = ['PM2.5', 'PM10', 'NO', 'NO2', 'NOx', 'NH3', 'CO', 'SO2', 'O3', 'Benzene', 'Toluene', 'Xylene', 'AQI']
for col in null_col:
df_city_day[col] = df_city_day[col].fillna(df_city_day[col].median())
df_city_day.isna().sum()
City 0 Date 0 PM2.5 0 PM10 0 NO 0 NO2 0 NOx 0 NH3 0 CO 0 SO2 0 O3 0 Benzene 0 Toluene 0 Xylene 0 AQI 0 AQI_Bucket 4681 dtype: int64
df_city_day.head()
City | Date | PM2.5 | PM10 | NO | NO2 | NOx | NH3 | CO | SO2 | O3 | Benzene | Toluene | Xylene | AQI | AQI_Bucket | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | Ahmedabad | 2015-01-01 | 48.57 | 95.68 | 0.92 | 18.22 | 17.15 | 15.85 | 0.92 | 27.64 | 133.36 | 0.00 | 0.02 | 0.00 | 118.0 | NaN |
1 | Ahmedabad | 2015-01-02 | 48.57 | 95.68 | 0.97 | 15.69 | 16.46 | 15.85 | 0.97 | 24.55 | 34.06 | 3.68 | 5.50 | 3.77 | 118.0 | NaN |
2 | Ahmedabad | 2015-01-03 | 48.57 | 95.68 | 17.40 | 19.30 | 29.70 | 15.85 | 17.40 | 29.07 | 30.70 | 6.80 | 16.40 | 2.25 | 118.0 | NaN |
3 | Ahmedabad | 2015-01-04 | 48.57 | 95.68 | 1.70 | 18.48 | 17.97 | 15.85 | 1.70 | 18.59 | 36.08 | 4.43 | 10.14 | 1.00 | 118.0 | NaN |
4 | Ahmedabad | 2015-01-05 | 48.57 | 95.68 | 22.10 | 21.42 | 37.76 | 15.85 | 22.10 | 39.33 | 39.31 | 7.01 | 18.89 | 2.78 | 118.0 | NaN |
我们仍然有 AQI_Bucket 的 NaN 值
# AQI_Bucket 的唯一计数
df_city_day['AQI_Bucket'].value_counts()
Moderate 8829 Satisfactory 8224 Poor 2781 Very Poor 2337 Good 1341 Severe 1338 Name: AQI_Bucket, dtype: int64
df_city_day.groupby('AQI_Bucket')['AQI'].mean()
AQI_Bucket Good 40.268456 Moderate 136.671877 Poor 245.663430 Satisfactory 76.765686 Severe 567.886398 Very Poor 343.004279 Name: AQI, dtype: float64
以上表中的平均 AQI 为参考,我们将替换 AQI_Bucket 的 NaN 值。
#使用条件替换AQI_Bucket的Null值
d1 = df_city_day[df_city_day.AQI<=50 ].AQI_Bucket.fillna('Good')
d2 = df_city_day[(df_city_day.AQI>50) & (df_city_day.AQI<=100)].AQI_Bucket.fillna('Satisfactory')
d3 = df_city_day[(df_city_day.AQI>100) & (df_city_day.AQI<=180)].AQI_Bucket.fillna('Moderate')
d4 = df_city_day[(df_city_day.AQI>180) & (df_city_day.AQI<=300)].AQI_Bucket.fillna('Poor')
d5 = df_city_day[(df_city_day.AQI>300) & (df_city_day.AQI<=450)].AQI_Bucket.fillna('Very Poor')
d6 = df_city_day[df_city_day.AQI>450].AQI_Bucket.fillna('Severe')
df_city_day.AQI_Bucket = pd.concat([d1,d2,d3,d4,d5,d6]).sort_index()
df_city_day.isna().sum()
City 0 Date 0 PM2.5 0 PM10 0 NO 0 NO2 0 NOx 0 NH3 0 CO 0 SO2 0 O3 0 Benzene 0 Toluene 0 Xylene 0 AQI 0 AQI_Bucket 0 dtype: int64
现在我们的数据没有任何空值。
df_city_day.head()
City | Date | PM2.5 | PM10 | NO | NO2 | NOx | NH3 | CO | SO2 | O3 | Benzene | Toluene | Xylene | AQI | AQI_Bucket | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | Ahmedabad | 2015-01-01 | 48.57 | 95.68 | 0.92 | 18.22 | 17.15 | 15.85 | 0.92 | 27.64 | 133.36 | 0.00 | 0.02 | 0.00 | 118.0 | Moderate |
1 | Ahmedabad | 2015-01-02 | 48.57 | 95.68 | 0.97 | 15.69 | 16.46 | 15.85 | 0.97 | 24.55 | 34.06 | 3.68 | 5.50 | 3.77 | 118.0 | Moderate |
2 | Ahmedabad | 2015-01-03 | 48.57 | 95.68 | 17.40 | 19.30 | 29.70 | 15.85 | 17.40 | 29.07 | 30.70 | 6.80 | 16.40 | 2.25 | 118.0 | Moderate |
3 | Ahmedabad | 2015-01-04 | 48.57 | 95.68 | 1.70 | 18.48 | 17.97 | 15.85 | 1.70 | 18.59 | 36.08 | 4.43 | 10.14 | 1.00 | 118.0 | Moderate |
4 | Ahmedabad | 2015-01-05 | 48.57 | 95.68 | 22.10 | 21.42 | 37.76 | 15.85 | 22.10 | 39.33 | 39.31 | 7.01 | 18.89 | 2.78 | 118.0 | Moderate |
让我们做一些 EDA
#AQI_Bucket的分布
import seaborn as sns
sns.displot(data=df_city_day,x='AQI_Bucket')
<seaborn.axisgrid.FacetGrid at 0x14a03f9e910>
让我们看看AQI_Bucket与城市的分布。
# AQI_Bucket 相对于城市的分布
df_city_day.groupby(['City', 'AQI_Bucket']).size().unstack().plot(kind='bar', stacked=True,figsize=(12,7))
plt.title(f"Total count of AQI_Bucket grouped by {'City'} ")
plt.ylabel('Count')
plt.show()
注意:- 此数据仅代表数据集中每个城市的数据计数。
对于大多数城市来说,与 AQI_Bucket 中的其他 AQI 标签相比,中等 AQI 的出现频率最高。
比较每个城市的平均 AQI。
# 比较每个城市的平均 AQI
df_city_day.groupby('City')['AQI'].mean().sort_values(ascending=False).plot(kind='bar',figsize=(12,7))
plt.ylabel('Average AQI')
plt.show()
现在让我们看看影响每个城市 AQI 的污染物浓度。
# 各城市污染物浓度
pollutant_list = ['PM2.5', 'PM10', 'NO', 'NO2', 'NOx', 'NH3', 'CO', 'SO2', 'O3', 'Benzene', 'Toluene', 'Xylene']
# 计算需要的行列数
n = int(len(pollutant_list)**0.5)
if n * n < len(pollutant_list):
n += 1
# 创建一个 n x n 的画布
fig, axes = plt.subplots(n, n, figsize=(12, 12))
fig.tight_layout(pad=3.0)
# 遍历污染物列表并创建条形图
for i, col in enumerate(pollutant_list):
ax = axes[i//n, i%n]
df_city_day.groupby('City')[col].mean().sort_values(ascending=False).plot(kind='bar', ax=ax, fontsize=6)
ax.set_ylabel(f'Average {col}', fontsize=12)
ax.set_xlabel('City', fontsize=12)
ax.set_title(f"Concentration of {col}", fontsize=12)
# 隐藏多余的图表
for j in range(i+1, n*n):
axes[j//n, j%n].axis('off')
plt.subplots_adjust(wspace=0.5, hspace=0.8) # 调整子图间距
plt.show()
探索历年来所有城市AQI的趋势。
df_city_day['Date'].min()
Timestamp('2015-01-01 00:00:00')
df_city_day['Date'].max()
Timestamp('2020-07-01 00:00:00')
我们的数据是从2015年1月1日到2020年7月1日
pd.DatetimeIndex(df_city_day['Date']).year.value_counts()
2019 7446 2018 6471 2017 4689 2020 4646 2016 3478 2015 2801 Name: Date, dtype: int64
# 创建“年份”列
df_city_day['Year'] = pd.DatetimeIndex(df_city_day['Date']).year
df_city_day.head()
City | Date | PM2.5 | PM10 | NO | NO2 | NOx | NH3 | CO | SO2 | O3 | Benzene | Toluene | Xylene | AQI | AQI_Bucket | Year | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | Ahmedabad | 2015-01-01 | 48.57 | 95.68 | 0.92 | 18.22 | 17.15 | 15.85 | 0.92 | 27.64 | 133.36 | 0.00 | 0.02 | 0.00 | 118.0 | Moderate | 2015 |
1 | Ahmedabad | 2015-01-02 | 48.57 | 95.68 | 0.97 | 15.69 | 16.46 | 15.85 | 0.97 | 24.55 | 34.06 | 3.68 | 5.50 | 3.77 | 118.0 | Moderate | 2015 |
2 | Ahmedabad | 2015-01-03 | 48.57 | 95.68 | 17.40 | 19.30 | 29.70 | 15.85 | 17.40 | 29.07 | 30.70 | 6.80 | 16.40 | 2.25 | 118.0 | Moderate | 2015 |
3 | Ahmedabad | 2015-01-04 | 48.57 | 95.68 | 1.70 | 18.48 | 17.97 | 15.85 | 1.70 | 18.59 | 36.08 | 4.43 | 10.14 | 1.00 | 118.0 | Moderate | 2015 |
4 | Ahmedabad | 2015-01-05 | 48.57 | 95.68 | 22.10 | 21.42 | 37.76 | 15.85 | 22.10 | 39.33 | 39.31 | 7.01 | 18.89 | 2.78 | 118.0 | Moderate | 2015 |
import seaborn as sns
import matplotlib.pyplot as plt
# 假设df_city_day是您的数据集
# df_city_day = ...
# 设置绘图尺寸
sns.set(rc={'figure.figsize':(25, 15)})
# 绘制折线图
sns.lineplot(data=df_city_day, x='Year', y='AQI', hue='City', err_style=None, linewidth=3)
# 设置标题和坐标轴标签
plt.title('Trend in Average AQI on year basis', fontsize=30)
plt.xlabel('Year', fontsize=20)
plt.ylabel('Avg. AQI', fontsize=20)
# 将图例放置在图表外部的右侧
plt.legend(bbox_to_anchor=(1.05, 1), loc='upper left', borderaxespad=0,prop={'size': 20})
# 显示图表
plt.show()
在新年或排灯节等特定日子查看 AQI 水平将会很有趣,这些日子污染增加的可能性最高。
现在为了控制这些污染物,我们需要了解这些污染物的来源。
为了简单起见,我们将考虑两类污染物 -
#创建新的派生特征
Pollutants_df = df_city_day.copy()
Pollutants_df['Industrial_pollutants'] = Pollutants_df['SO2']+Pollutants_df['O3']+Pollutants_df['Benzene']+Pollutants_df['Toluene']+Pollutants_df['Xylene']
Pollutants_df['Vehical_pollutants'] = Pollutants_df['PM2.5']+Pollutants_df['PM10']+Pollutants_df['NO']+Pollutants_df['NO2']+Pollutants_df['NOx']+Pollutants_df['NH3']+Pollutants_df['CO']
Pollutants_df.drop(columns=['PM2.5','PM10','NO','NO2','NOx','NH3','CO','SO2','O3','Benzene','Toluene','Xylene'],axis=1,inplace=True)
Pollutants_df.head()
City | Date | AQI | AQI_Bucket | Year | Industrial_pollutants | Vehical_pollutants | |
---|---|---|---|---|---|---|---|
0 | Ahmedabad | 2015-01-01 | 118.0 | Moderate | 2015 | 161.02 | 197.31 |
1 | Ahmedabad | 2015-01-02 | 118.0 | Moderate | 2015 | 71.56 | 194.19 |
2 | Ahmedabad | 2015-01-03 | 118.0 | Moderate | 2015 | 85.22 | 243.90 |
3 | Ahmedabad | 2015-01-04 | 118.0 | Moderate | 2015 | 70.24 | 199.95 |
4 | Ahmedabad | 2015-01-05 | 118.0 | Moderate | 2015 | 107.32 | 263.48 |
#创建新的数据框
Pollutants_df_new = Pollutants_df.groupby('City')[['Industrial_pollutants','Vehical_pollutants']].mean()
Pollutants_df_new.head()
Industrial_pollutants | Vehical_pollutants | |
---|---|---|
City | ||
Ahmedabad | 106.505620 | 299.591354 |
Aizawl | 32.157257 | 86.678850 |
Amaravati | 55.136877 | 171.539064 |
Amritsar | 44.451360 | 253.407789 |
Bengaluru | 46.840806 | 202.521229 |
#创建情节
Pollutants_df_new.plot.bar()
plt.title('Comparison for Industrial and Vehicle pollutants based on City',fontsize=30)
plt.xlabel('City',fontsize=20)
plt.ylabel('Pollutant Level',fontsize=20)
plt.show()
可以看出,机动车污染物是所有城市污染的主要贡献者。
德里在车辆污染方面名列前茅,而艾哈迈达巴德在工业污染方面名列前茅。
由于我们的数据集包含锁定期间的数据,因此可视化每个城市锁定之前和之后的污染图将非常有趣。
Pollutants_df.head()
City | Date | AQI | AQI_Bucket | Year | Industrial_pollutants | Vehical_pollutants | |
---|---|---|---|---|---|---|---|
0 | Ahmedabad | 2015-01-01 | 118.0 | Moderate | 2015 | 161.02 | 197.31 |
1 | Ahmedabad | 2015-01-02 | 118.0 | Moderate | 2015 | 71.56 | 194.19 |
2 | Ahmedabad | 2015-01-03 | 118.0 | Moderate | 2015 | 85.22 | 243.90 |
3 | Ahmedabad | 2015-01-04 | 118.0 | Moderate | 2015 | 70.24 | 199.95 |
4 | Ahmedabad | 2015-01-05 | 118.0 | Moderate | 2015 | 107.32 | 263.48 |
#数据范围
print(Pollutants_df['Date'].min())
print(Pollutants_df['Date'].max())
2015-01-01 00:00:00 2020-07-01 00:00:00
#创建 precovid 数据集
precovid_df = Pollutants_df[Pollutants_df['Date']<='2020-03-24']
precovid_df.shape
(26957, 7)
#创建 postcovid 数据集
postcovid_df = Pollutants_df[Pollutants_df['Date']>'2020-03-24']
postcovid_df.shape
(2574, 7)
# 过滤数据以创建新数据
precovid_df_new = precovid_df.groupby('City')[['Industrial_pollutants','Vehical_pollutants']].mean()
#创建情节
precovid_df_new.plot.bar()
plt.title('Pre-Covid Comparison for Industrial and Vehicle pollutants based on City',fontsize=30)
plt.xlabel('City',fontsize=20)
plt.ylabel('Pollutant Level',fontsize=20)
plt.show()
# 过滤数据以创建新数据
postcovid_df_new = postcovid_df.groupby('City')[['Industrial_pollutants','Vehical_pollutants']].mean()
#创建情节
postcovid_df_new.plot.bar()
plt.title('Post-Covid Comparison for Industrial and Vehicle pollutants based on City',fontsize=30)
plt.xlabel('City',fontsize=20)
plt.ylabel('Pollutant Level',fontsize=20)
plt.show()
有趣的是,看到后疫情场景如何因封锁而将车辆污染物降低到显着水平。
# 变量之间的相关性
plt.figure(figsize=(15, 8))
# 显式设置 numeric_only 参数为 True
correlation = df_city_day.corr(numeric_only=True)
sns.heatmap(correlation, annot=True, cmap='coolwarm')
<Axes: >
没有观察到显着相关性
#创建数据副本
model_data = df_city_day.copy()
model_data.head()
City | Date | PM2.5 | PM10 | NO | NO2 | NOx | NH3 | CO | SO2 | O3 | Benzene | Toluene | Xylene | AQI | AQI_Bucket | Year | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | Ahmedabad | 2015-01-01 | 48.57 | 95.68 | 0.92 | 18.22 | 17.15 | 15.85 | 0.92 | 27.64 | 133.36 | 0.00 | 0.02 | 0.00 | 118.0 | Moderate | 2015 |
1 | Ahmedabad | 2015-01-02 | 48.57 | 95.68 | 0.97 | 15.69 | 16.46 | 15.85 | 0.97 | 24.55 | 34.06 | 3.68 | 5.50 | 3.77 | 118.0 | Moderate | 2015 |
2 | Ahmedabad | 2015-01-03 | 48.57 | 95.68 | 17.40 | 19.30 | 29.70 | 15.85 | 17.40 | 29.07 | 30.70 | 6.80 | 16.40 | 2.25 | 118.0 | Moderate | 2015 |
3 | Ahmedabad | 2015-01-04 | 48.57 | 95.68 | 1.70 | 18.48 | 17.97 | 15.85 | 1.70 | 18.59 | 36.08 | 4.43 | 10.14 | 1.00 | 118.0 | Moderate | 2015 |
4 | Ahmedabad | 2015-01-05 | 48.57 | 95.68 | 22.10 | 21.42 | 37.76 | 15.85 | 22.10 | 39.33 | 39.31 | 7.01 | 18.89 | 2.78 | 118.0 | Moderate | 2015 |
由于 AQI_Bucket 是 AQI 的派生特征,并且 AQI 是我们的预测目标变量,因此我们将删除 AQI_Bucket 列。
Year 是我们从 Date 派生的一个功能,因此我们还将删除 Date 列。
# 删除日期列
model_data.drop(columns=['Date','AQI_Bucket'],axis=1,inplace=True)
#名义变量的一种热编码
model_data = pd.get_dummies(model_data, columns=["City","Year"])
#标准化连续特征
from scipy.stats import zscore
model_data[['PM2.5','PM10','NO','NO2','NOx','NH3','CO','SO2','O3','Benzene','Toluene','Xylene']]=model_data[['PM2.5','PM10','NO','NO2','NOx','NH3','CO','SO2','O3','Benzene','Toluene','Xylene']].apply(zscore)
model_data.head()
PM2.5 | PM10 | NO | NO2 | NOx | NH3 | CO | SO2 | O3 | Benzene | ... | City_Shillong | City_Talcher | City_Thiruvananthapuram | City_Visakhapatnam | Year_2015 | Year_2016 | Year_2017 | Year_2018 | Year_2019 | Year_2020 | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | -0.26654 | -0.193291 | -0.731090 | -0.412430 | -0.472010 | -0.23605 | -0.183488 | 0.812044 | 4.918625 | -0.200657 | ... | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
1 | -0.26654 | -0.193291 | -0.728765 | -0.522191 | -0.495418 | -0.23605 | -0.176052 | 0.630337 | 0.003261 | 0.057542 | ... | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
2 | -0.26654 | -0.193291 | 0.035219 | -0.365576 | -0.046258 | -0.23605 | 2.267235 | 0.896135 | -0.163059 | 0.276450 | ... | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
3 | -0.26654 | -0.193291 | -0.694820 | -0.401151 | -0.444192 | -0.23605 | -0.067495 | 0.279859 | 0.103251 | 0.110164 | ... | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
4 | -0.26654 | -0.193291 | 0.253765 | -0.273602 | 0.227173 | -0.23605 | 2.966167 | 1.499475 | 0.263137 | 0.291184 | ... | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 |
5 rows × 45 columns
#检查列
model_data.columns
Index(['PM2.5', 'PM10', 'NO', 'NO2', 'NOx', 'NH3', 'CO', 'SO2', 'O3', 'Benzene', 'Toluene', 'Xylene', 'AQI', 'City_Ahmedabad', 'City_Aizawl', 'City_Amaravati', 'City_Amritsar', 'City_Bengaluru', 'City_Bhopal', 'City_Brajrajnagar', 'City_Chandigarh', 'City_Chennai', 'City_Coimbatore', 'City_Delhi', 'City_Ernakulam', 'City_Gurugram', 'City_Guwahati', 'City_Hyderabad', 'City_Jaipur', 'City_Jorapokhar', 'City_Kochi', 'City_Kolkata', 'City_Lucknow', 'City_Mumbai', 'City_Patna', 'City_Shillong', 'City_Talcher', 'City_Thiruvananthapuram', 'City_Visakhapatnam', 'Year_2015', 'Year_2016', 'Year_2017', 'Year_2018', 'Year_2019', 'Year_2020'], dtype='object')
#目标变量“AQI”的分布
import seaborn as sns
plt.figure(figsize=(7,7))
sns.histplot(model_data['AQI'],color="grey")
<Axes: xlabel='AQI', ylabel='Count'>
# 因变量的偏度
model_data['AQI'].skew()
3.76975148929373
我们的目标变量是正偏态的,我们将尝试使用变换对其进行标准化。
# 使用不同的变换检查偏度
print(np.log10(model_data['AQI']).skew())
print(np.sqrt(model_data['AQI']).skew())
print(1/(model_data['AQI']).skew())
0.4742432997234769 1.6788266180736655 0.2652694754123837
由于倒数变换的偏度值在三种变换中最低。我们认为这种转变是最好的。
# 将因变量重新定位到最后一个索引
last_column = model_data.pop('AQI')
model_data.insert(44, 'AQI', last_column)
model_data.head()
PM2.5 | PM10 | NO | NO2 | NOx | NH3 | CO | SO2 | O3 | Benzene | ... | City_Talcher | City_Thiruvananthapuram | City_Visakhapatnam | Year_2015 | Year_2016 | Year_2017 | Year_2018 | Year_2019 | Year_2020 | AQI | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | -0.26654 | -0.193291 | -0.731090 | -0.412430 | -0.472010 | -0.23605 | -0.183488 | 0.812044 | 4.918625 | -0.200657 | ... | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 118.0 |
1 | -0.26654 | -0.193291 | -0.728765 | -0.522191 | -0.495418 | -0.23605 | -0.176052 | 0.630337 | 0.003261 | 0.057542 | ... | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 118.0 |
2 | -0.26654 | -0.193291 | 0.035219 | -0.365576 | -0.046258 | -0.23605 | 2.267235 | 0.896135 | -0.163059 | 0.276450 | ... | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 118.0 |
3 | -0.26654 | -0.193291 | -0.694820 | -0.401151 | -0.444192 | -0.23605 | -0.067495 | 0.279859 | 0.103251 | 0.110164 | ... | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 118.0 |
4 | -0.26654 | -0.193291 | 0.253765 | -0.273602 | 0.227173 | -0.23605 | 2.966167 | 1.499475 | 0.263137 | 0.291184 | ... | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 118.0 |
5 rows × 45 columns
因变量和自变量分割
#分割数据
X= model_data.iloc[:,:-1]
y= 1/model_data['AQI']
我们的数据已准备好用于模型
这里我们的任务是根据可用的污染物数据来预测 AQI。该任务可以使用回归模型来完成。 现在的问题是我们应该使用哪种回归模型?
这个问题的答案取决于业务需求。因为我们知道,当我们使用复杂模型提高预测的准确性时,模型的可解释性就会降低,这可能会给利益相关者带来问题。
考虑到上述情况,我们可以有三种选择——
上图最能说明模型的准确性和可解释性之间的关系
现在让我们尝试实现回归模型并观察结果。
# 预测函数
#创建预测函数来测试不同模型
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import r2_score
from sklearn.metrics import mean_squared_error
def predict(ml_model,X,y):
X_train,X_test,y_train,y_test=train_test_split(X,y,test_size=0.25,random_state=12)
sc=StandardScaler()
X_train=sc.fit_transform(X_train)
X_test=sc.transform(X_test)
model=ml_model.fit(X_train,y_train)
y_pred=model.predict(X_test)
plt.scatter(y_pred,y_test,color='b')
plt.xlabel('Predicted')
plt.ylabel('Actual')
# 评估指标
evaluation_metric=(f'R^2 is {r2_score(10**(y_test), 10**(y_pred))}\n Adj R^2 is {1-(1-r2_score(10**(y_test), 10**(y_pred)))*((X_test.shape[0]-1)/(X_test.shape[0]-X_test.shape[1]-1))}\n RMSE is: {np.sqrt(mean_squared_error(10**(y_test), 10**(y_pred)))}')
print(evaluation_metric)
sns.set(rc={'figure.figsize':(10,7)})
from sklearn.linear_model import LinearRegression
predict(LinearRegression(),X,y)
R^2 is 0.5727955326783882 Adj R^2 is 0.5702339359814474 RMSE is: 0.00890246727125143
from sklearn.preprocessing import PolynomialFeatures
poly_regs= PolynomialFeatures(degree= 2)
x_poly= poly_regs.fit_transform(X)
predict(LinearRegression(),x_poly,y)
R^2 is 0.743583398924464 Adj R^2 is 0.7017697575012436 RMSE is: 0.0068970847496440295
from sklearn.tree import DecisionTreeRegressor
predict(DecisionTreeRegressor(),X,y)
R^2 is 0.6937112748902108 Adj R^2 is 0.6918747112618611 RMSE is: 0.007538032369465511
from sklearn.ensemble import RandomForestRegressor
predict(RandomForestRegressor(),X,y)
R^2 is 0.8385357477899551 Adj R^2 is 0.8375675783844982 RMSE is: 0.005473066005065331
import xgboost as xgb
predict(xgb.XGBRegressor(),X,y)
R^2 is 0.8300554466563952 Adj R^2 is 0.8290364278028767 RMSE is: 0.005614952905727235
我们可以清楚地观察到,随机森林回归器最适合我们的数据,具有最佳 R2 分数和最小均方根误差 (RMSE)