记一次课程作业,利用所给数据,以AQI指数(空气质量状况指数)为因变量,其他指标为解释变量,建立线性回归分析模型,并形成分析报告。
Python&SPSS完成空气质量状况的指数(AQI)分析
题目要求
空气质量指数(air quality index,AQI是定量描述空气质量状况的指 数。为了更好地了解和人们身体健康息息相关的天气状况,本书模拟生成500条某地区天气数据,一行样本对应一天的天气。因变量为空气质量指 数:数值型变量。解释变量包括:星期:分类变量,包括星期一、星期二 等;最低气温:文本型变量,比如—5℃;最高气温:文本型变量,比如5℃;天气:分类变量,包括晴天、多云~睛、阴~多云、阴~小雨等,其 中“~”表示转,比如“晴~多云”表示晴转多云;风向:分类变量,包括 东风、西风、南风、北风、东南风、东北风、西南风、西北风等;风力:数 值型变量,1~5级。以AQI为因变量,其他指标为解释变量,建立线性回归分析模型,并形成分析报告。
原始数据
第1章习题1
https://pan.baidu.com/s/1EKJyXBFGrNlsdqSjUE0LCA
提取码:yno6
注:SPSS以及python打开如果中文显示有问题,可以改一下后缀啥的txt,csv互换试试。
星期,最高,最低,天气,风向,风力,AQI
星期六,29℃,20℃,霾~雷阵雨,无持续,1,145
星期二,11℃,6℃,小雨~阴,北,2,15
星期一,12℃,-3℃,晴,北,3,177
星期五,28℃,18℃,阴,南,2,90
星期日,-2℃,-10℃,晴,北,3,36
················
················
················
星期六,16℃,3℃,多云,无持续,1,91
星期一,15℃,4℃,多云,南,2,202
星期一,21℃,8℃,晴~阴,北,3,39
(部分展示)
数据处理
数据总览
直观上来看,除了风力和AQI,其他数据都需要进行处理,用Pandas处理比较方便,首先用python对数据的总体情况进行了解。
import pandas as pd
data = pd.read_csv("第1章习题1.txt", sep=',', encoding='GBK')
print(data.head(10))
print('数据总览\n')
print(data.info())
print('数据类型预览\n')
print(data.describe())
print('数据情况\n')
print(data[data.isnull() == True].count())
print('数据缺失值检验\n')
# 统计总量
print('星期', len(data['星期'].value_counts()))
print('天气', len(data['天气'].value_counts()))
print('风向', len(data['风向'].value_counts()))
输出:
星期 最高 最低 天气 风向 风力 AQI
0 星期六 29℃ 20℃ 霾~雷阵雨 无持续 1 145
1 星期二 11℃ 6℃ 小雨~阴 北 2 15
2 星期一 12℃ -3℃ 晴 北 3 177
3 星期五 28℃ 18℃ 阴 南 2 90
4 星期日 -2℃ -10℃ 晴 北 3 36
5 星期四 30℃ 17℃ 晴~阴 南 2 63
6 星期六 7℃ -2℃ 多云~阴 无持续 1 39
7 星期五 25℃ 13℃ 阵雨~多云 东 2 59
8 星期六 8℃ -4℃ 霾~晴 无持续 1 315
9 星期四 5℃ -5℃ 多云~晴 西北 3 28
数据总览
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 500 entries, 0 to 499
Data columns (total 7 columns):
# Column Non-Null Count Dtype
--- ------ -------------- -----
0 星期 500 non-null object
1 最高 500 non-null object
2 最低 500 non-null object
3 天气 500 non-null object
4 风向 500 non-null object
5 风力 500 non-null int64
6 AQI 500 non-null int64
dtypes: int64(2), object(5)
memory usage: 27.5+ KB
None
数据类型预览
风力 AQI
count 500.000000 500.000000
mean 1.946000 96.690000
std 0.944918 63.743112
min 1.000000 15.000000
25% 1.000000 52.000000
50% 2.000000 82.000000
75% 3.000000 120.000000
max 5.000000 429.000000
数据情况
星期 0
最高 0
最低 0
天气 0
风向 0
风力 0
AQI 0
dtype: int64
数据缺失值检验
星期 7
天气 57
风向 9
结果分析:
缺失值:所有数据完整,数据量500
数据类型:只有“风力”、“AQI”为“int64”,其他均为“object”需要进行转换
类型数:天气类型数为57,而数据总量只有500,需要重点进行处理
“最高”&“最低”变量处理
星期 最高 最低 天气 风向 风力 AQI
0 星期六 29℃ 20℃ 霾~雷阵雨 无持续 1 145
原始数据中,变量后带有摄氏度标识,利用代码将他删掉并加入到新的数据集data2
data2 = pd.DataFrame()
data2['max_t'] = data['最高'].replace('℃','',regex=True)
data2['min_t'] = data['最低'].replace('℃','',regex=True)
print(data2)
输出:
max_t min_t
0 29 20
1 11 6
2 12 -3
3 28 18
4 -2 -10
.. ... ...
495 21 12
496 29 18
497 16 3
498 15 4
499 21 8
[500 rows x 2 columns]
“风向”变量处理
print(data['风向'].value_counts())
无持续 194
南 124
北 104
西南 35
东 13
西北 11
东北 11
东南 5
西 3
Name: 风向, dtype: int64
Tableau展示的数据更直观,数据基本由‘北风’,‘南风’,‘无持续’组成,由于数据量不大,数据量第四多的“西南风”也只有35条,而“东南”,“西”只有个位数得数据量,进行回归意义不大,所以对于“风向“的处理方式为将数据分为四类:‘北风’,‘南风’,‘无持续’,‘风向_其他’。
由于需要进行线性回归分析,利用“get_dummies”方法将“风向”的四种变量转换为对应的矩阵,并添加到data2中。
代码实现:
data['风向'].replace(['西南','东','西北','东北','东南','西'],'风向_其他',inplace=True)
df_fx = pd.get_dummies(data['风向'])
print(df_fx.head(10))
data2 = data2.join(df_fx)
print(data2)
输出
北 南 无持续 风向_其他
0 0 0 1 0
1 1 0 0 0
2 1 0 0 0
3 0 1 0 0
4 1 0 0 0
5 0 1 0 0
6 0 0 1 0
7 0 0 0 1
8 0 0 1 0
9 0 0 0 1
max_t min_t 北 南 无持续 风向_其他
0 29 20 0 0 1 0
1 11 6 1 0 0 0
2 12 -3 1 0 0 0
3 28 18 0 1 0 0
4 -2 -10 1 0 0 0
.. ... ... .. .. ... ...
495 21 12 0 0 1 0
496 29 18 0 0 1 0
497 16 3 0 0 1 0
498 15 4 0 1 0 0
499 21 8 1 0 0 0
[500 rows x 6 columns]
“星期”变量处理
“星期”变量分布均匀,不需要多作处理,直接和“风向”的处理方式一致,转换成矩阵。
df_xq = pd.get_dummies(data['星期'])
print(df_xq)
data2 = data2.join(df_xq)
print(data2)
输出
星期一 星期三 星期二 星期五 星期六 星期四 星期日
0 0 0 0 0 1 0 0
1 0 0 1 0 0 0 0
2 1 0 0 0 0 0 0
3 0 0 0 1 0 0 0
4 0 0 0 0 0 0 1
.. ... ... ... ... ... ... ...
495 0 0 0 0 0 0 1
496 0 0 0 0 0 0 1
497 0 0 0 0 1 0 0
498 1 0 0 0 0 0 0
499 1 0 0 0 0 0 0
[500 rows x 7 columns]
max_t min_t 北 南 无持续 风向_其他 星期一 星期三 星期二 星期五 星期六 星期四 星期日
0 29 20 0 0 1 0 0 0 0 0 1 0 0
1 11 6 1 0 0 0 0 0 1 0 0 0 0
2 12 -3 1 0 0 0 1 0 0 0 0 0 0
3 28 18 0 1 0 0 0 0 0 1 0 0 0
4 -2 -10 1 0 0 0 0 0 0 0 0 0 1
.. ... ... .. .. ... ... ... ... ... ... ... ... ...
495 21 12 0 0 1 0 0 0 0 0 0 0 1
496 29 18 0 0 1 0 0 0 0 0 0 0 1
497 16 3 0 0 1 0 0 0 0 0 1 0 0
498 15 4 0 1 0 0 1 0 0 0 0 0 0
499 21 8 1 0 0 0 1 0 0 0 0 0 0
[500 rows x 13 columns]
“天气”变量处理
通过数据总览我们知道天气变量一共有57种之多,Tableau也显示“天气”并不是集中为某几种,各种天气种类都占有一席之地。
星期 最高 最低 天气 风向 风力 AQI
0 星期六 29℃ 20℃ 霾~雷阵雨 无持续 1 145
1 星期二 11℃ 6℃ 小雨~阴 北 2 15
我们同时也观察到有天气几乎都是复合型天气,由两种天气组成,我们尝试着把他拆开,继续观察。
from collections import Counter
c = data['天气'].apply(lambda x: x.split('~'))
lists = []
for i in c:
for ii in i:
lists.append(ii)
counts = Counter(lists)
for k,v in counts.items():
print(k,v)
print("总数:", len(counts))
输出
霾 45
雷阵雨 47
小雨 28
阴 96
晴 264
多云 225
阵雨 30
雾 4
雨夹雪 5
小到中雨 3
中到大雨 2
中雨 4
小雪 4
大到暴雨 1
大雨 2
大雪 1
暴雨 1
总数: 17
我们可以看到“天气”种类由原来的57种变成了现在的17种,同时也可以观察到有多种同类的变量:比如“大雨”、“小雨”、“中雨”。我们可以将他们按照一定规则量化,来看成一种天气,如:
雨 | 值 | 雪 | 值 |
---|---|---|---|
小雨 | 1 | 小雪 | 1 |
小到中雨 | 2 | ||
中雨 | 3 | 雨夹雪 | 2 |
中到大雨 | 4 | ||
大雨 | 5 | 大雪 | 3 |
大到暴雨 | 6 | ||
暴雨 | 7 |
data3 = pd.DataFrame()
data3['晴'] = c.apply(lambda x: 1 if '晴' in x else 0)
data3['多云'] = c.apply(lambda x: 1 if '多云' in x else 0)
data3['阴'] = c.apply(lambda x: 1 if '阴' in x else 0)
data3['雷阵雨'] = c.apply(lambda x: 1 if '雷阵雨' in x else 0)
data3['霾'] = c.apply(lambda x: 1 if '霾' in x else 0)
data3['雾'] = c.apply(lambda x: 1 if '雾' in x else 0)
data3['阵雨'] = c.apply(lambda x: 1 if '阵雨' in x else 0)
dict1={
'小雨':1,'小到中雨':2,'中雨':3,'中到大雨':4,'大雨':5,'大到暴雨':6,'暴雨':7}
dict2={
'小雪':1,'雨夹雪':2,'大雪':3}
def d_dict1(x):
m = 0
for i in x:
m1 = dict1.get(i)
if type(m1) == int:
if m1 > m:
m = m1
return m
def d_dict2(x):
m = 0
for i in x:
m1 = dict2.get(i)
if type(m1) == int:
if m1 > m:
m = m1
return m
data3['雨'] = c.apply(d_dict1)
data3['雪'] = c.apply(d_dict2)
print(data3)
data2 = data2.join(data3)
print(data2)
输出
晴 多云 阴 雷阵雨 霾 雾 阵雨 雨 雪
0 0 0 0 1 1 0 0 0 0
1 0 0 1 0 0 0 0 1 0
2 1 0 0 0 0 0 0 0 0
3 0 0 1 0 0 0 0 0 0
4 1 0 0 0 0 0 0 0 0
.. .. .. .. ... .. .. .. .. ..
495 1 0 0 0 1 0 0 0 0
496 1 0 0 0 0 0 0 0 0
497 0 1 0 0 0 0 0 0 0
498 0 1 0 0 0 0 0 0 0
499 1 0 1 0 0 0 0 0 0
[500 rows x 9 columns]
max_t min_t 北 南 无持续 风向_其他 星期一 星期三 星期二 ... 晴 多云 阴 雷阵雨 霾 雾 阵雨 雨 雪
0 29 20 0 0 1 0 0 0 0 ... 0 0 0 1 1 0 0 0 0
1 11 6 1 0 0 0 0 0 1 ... 0 0 1 0 0 0 0 1 0
2 12 -3 1 0 0 0 1 0 0 ... 1 0 0 0 0 0 0 0 0
3 28 18 0 1 0 0 0 0 0 ... 0 0 1 0 0 0 0 0 0
4 -2 -10 1 0 0 0 0 0 0 ... 1 0 0 0 0 0 0 0 0
.. ... ... .. .. ... ... ... ... ... ... .. .. .. ... .. .. .. .. ..
495 21 12 0 0 1 0 0 0 0 ... 1 0 0 0 1 0 0 0 0
496 29 18 0 0 1 0 0 0 0 ... 1 0 0 0 0 0 0 0 0
497 16 3 0 0 1 0 0 0 0 ... 0 1 0 0 0 0 0 0 0
498 15 4 0 1 0 0 1 0 0 ... 0 1 0 0 0 0 0 0 0
499 21 8 1 0 0 0 1 0 0 ... 1 0 1 0 0 0 0 0 0
[500 rows x 22 columns]
Process finished with exit code 0
数据分析
多重共线性
在Tableau中将最高气温放在横轴,最低气温作为竖轴,从图片能直观得感受到散点图呈现线性分布,可能会对之后得回归产生影响,可以假设AQI可能主要和温差有关,而不是最高温度或者最低温度有关,将这个变量加入后,将处理后的数据保存,进行建模。
新建“温差”变量
本来想直接在data2中操作,直接将“max_t”与“min_t”相减,但是报错说数据结构不对,又想直接把这两列转换为"int"类型,但是又报错说有减号存在,无法转换。最后不知道怎么办,只好先把他保存一次,然后重新读取一次,自动识别到为"int"类型,不得已出此下策,如果这步有解决方案,欢迎大佬们评论指出。
data2.to_csv('清洗后.txt', encoding='utf-8')
new_data = pd.read_csv("清洗后.txt", sep=',', encoding='utf-8',index_col=0)
new_data['温差'] = new_data['max_t']-new_data['min_t']
new_data['AQI'] = data['AQI']
print(new_data)
new_data.to_csv('分析后.csv', encoding='UTF-8')
输出
max_t min_t 北 南 无持续 风向_其他 星期一 星期三 ... 雷阵雨 霾 雾 阵雨 雨 雪 温差 AQI
0 29 20 0 0 1 0 0 0 ... 1 1 0 0 0 0 9 145
1 11 6 1 0 0 0 0 0 ... 0 0 0 0 1 0 5 15
2 12 -3 1 0 0 0 1 0 ... 0 0 0 0 0 0 15 177
3 28 18 0 1 0 0 0 0 ... 0 0 0 0 0 0 10 90
4 -2 -10 1 0 0 0 0 0 ... 0 0 0 0 0 0 8 36
.. ... ... .. .. ... ... ... ... ... ... .. .. .. .. .. .. ...
495 21 12 0 0 1 0 0 0 ... 0 1 0 0 0 0 9 163
496 29 18 0 0 1 0 0 0 ... 0 0 0 0 0 0 11 85
497 16 3 0 0 1 0 0 0 ... 0 0 0 0 0 0 13 91
498 15 4 0 1 0 0 1 0 ... 0 0 0 0 0 0 11 202
499 21 8 1 0 0 0 1 0 ... 0 0 0 0 0 0 13 39
[500 rows x 24 columns]
回归建模
数据导入
经过之前的数据处理得到新的数据集“分析后.csv”
利用SPSS进行多元线性回归分析
如果导入数据乱码,可以尝试更改上述代码保存的最后形式,如
new_data.to_csv('分析后.csv', encoding='UTF-8')
new_data.to_csv('分析后.csv', encoding='GBK')
new_data.to_csv('分析后.txt', encoding='UTF-8')
new_data.to_csv('分析后.txt', encoding='GBK')
一路默认下一步,小数点句号,分隔符逗号,最后变量名“V1”选择不导入就行。数据导入后如下图:
SPSS线性回归操作
欢迎参考
https://blog.csdn.net/weixin_44255182/article/details/108932498
分析——回归——线性
除了因变量,其他全部放自变量中
SPSS结果分析
模型概要
用步进的方式将变量一个一个加入建立模型,并计算R²,从模型1到模型6,每个变量加入后都使得R²增加,说明后一个模型都优于前一个模型,最终我们选择第六个模型,也就是包括了:雾、霾、晴、最低温度,最高温度,以及无持续风。
模型比较
模型6回归平方和最大,解释自变量的部分最多,并且所有模型显著性均小于0.05,说明所有模型都能很好得解释因变量。
排除的变量
这些变量在加入后,由于显著性均大于0.05,说明接受原假设,认为该变量系数为0,即不影响因变量,将其排除。不过这温差啥意思,我也看不懂….
共线性诊断
有没有大佬看得懂这个分析结果的,麻烦也留言一下
异常个案诊断
显示了七个预测异常个案,个案号为92,124,150,251,338,465。去看看这几个个案。
print(data.iloc[[91,123,149,250,337,464]])
星期 最高 最低 天气 风向 风力 AQI
91 星期三 3℃ -3℃ 霾 无持续 1 429
123 星期六 9℃ -2℃ 晴 北 2 303
149 星期日 19℃ 7℃ 多云~阴 风向_其他 2 278
250 星期六 17℃ 12℃ 小雨 无持续 1 246
337 星期三 6℃ -5℃ 霾 北 2 365
464 星期二 8℃ 5℃ 霾 北 3 401
好像也没看出个啥特别的原因。
模型回归系数
系数图表中可以看到min_t和max_t的VIF值较大,分别为20.57,18.93(远大于5),说明最高温度和最低温度之间确实存在共线性。
所有系数显著性都小于0.05,说明拒绝系数为0的原假设。
最终模型方程为:
AQI=
+81.090(常数)
+101.777 * 霾
-31.384* 晴
+101.929 * 雾
-2.995 * 最小温度
+2.355 * 最大温度
+10.452 * 是否有持续风
残差检验
残差直方图比较正常,基本符合正态分布
残差图显示,大部分数据预测准确,残差集中分布在(0,0)点向周围扩散,大部分数据在【-2,2之间】,没有趋势性变化,不过仍有,部分数据不能很好得预测,残差分布在远离正常值的【2,6】之间。对这三团有其他理解的也欢迎指教。
模型的不足
- 最高温度和最低温度存在共线性,需要去去除其中一个变量,重新建模。
- 由于将天气一分为二,不同的天气之间可能有一定相关性,比如雾霾基本都是成对出现,模型结果是雾、霾分别影响了AQI,可能只有霾是影响AQI的因素,但是由于雾霾都是成对出现,将他们划分开后,导致这个结果,可以继续研究。
- 模型中并没有星期的结果,可以继续考虑将时间划分为工作日和非工作日,观察AQI是否受影响
第二次数据处理
“星期”变量处理
将之前的星期变量变为”休息日“和”工作日“,将所有的”休息日“标记为1,”工作日“标记为0。考虑到之前的处理方式,我们只需要把”星期六“和”星期天“的加起来就行了。
import pandas as pd
data = pd.read_csv("分析后.csv", sep=',', encoding='GBK', index_col=0)
data["休息日"] = data["星期六"]+data["星期日"]
print(data)
data.to_csv('第二次处理.csv', encoding='utf-8')
输出
max_t min_t 北 南 无持续 风向_其他 星期一 星期三 ... 霾 雾 阵雨 雨 雪 温差 AQI 休息日
0 29 20 0 0 1 0 0 0 ... 1 0 0 0 0 9 145 1
1 11 6 1 0 0 0 0 0 ... 0 0 0 1 0 5 15 0
2 12 -3 1 0 0 0 1 0 ... 0 0 0 0 0 15 177 0
3 28 18 0 1 0 0 0 0 ... 0 0 0 0 0 10 90 0
4 -2 -10 1 0 0 0 0 0 ... 0 0 0 0 0 8 36 1
.. ... ... .. .. ... ... ... ... ... .. .. .. .. .. .. ... ...
495 21 12 0 0 1 0 0 0 ... 1 0 0 0 0 9 163 1
496 29 18 0 0 1 0 0 0 ... 0 0 0 0 0 11 85 1
497 16 3 0 0 1 0 0 0 ... 0 0 0 0 0 13 91 1
498 15 4 0 1 0 0 1 0 ... 0 0 0 0 0 11 202 0
499 21 8 1 0 0 0 1 0 ... 0 0 0 0 0 13 39 0
[500 rows x 25 columns]
第二次建模
第二次建模我们不加入”星期“变量,加入新变量”休息日“进行分析
SPSS分析
一样的结果,”休息日“显著性为0.249,大于0.05,接受原假设,认为”休息日“系数为0。
模型总结
最终模型为AQI=81.090+101.777 * 是否有霾-31.384 * 是为晴+101.929 * 是否有雾-2.995 * 最小温度+2.355 * 最大温度+10.452 * 是否无持续风
其中我们模型中并没有星期的信息,说明星期几对AQI影响不大,也就是说不同的日期出门差别不大。
模型中并没有风力大小的因子,也没有北风,南风。说明什么方向的风对AQI影响不大,风力的大小也对AQI影响不大,但是当天如果无持续风,空气质量会略微变差,说明在有风的天气空气质量会好一些。
影响AQI最重要的就是天气,可以看到如果当天有雾、霾空气质量会显著降低,这和我们的经验相符。如果当天是晴天,则空气质量较好。
关于温度,由于最后检验结果,最高温度和最低温度存在线性关系,不作分析。