Python机器学习数据建模与分析——Numpy和Pandas综合应用案例:空气质量监测数据的预处理和基本分析
Posted 心无旁骛~
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python机器学习数据建模与分析——Numpy和Pandas综合应用案例:空气质量监测数据的预处理和基本分析相关的知识,希望对你有一定的参考价值。
本篇文章主要以北京市空气质量监测数据为例子,聚集数据建模中的数据预处理和基本分析环节,说明Numpy和Pandas的数据读取、数据分组、数据重编码、分类汇总等数据加工处理功能。同时在实现案例的过程中对用到的Numpy和Pandas相关函数进行讲解。
文章目录
数据
在进行案例之前,我首先将本案例即将用到的数据集链接分享:北京市空气质量数据
大家可以进入文档中,将数据复制到你自己创建的Excel文件中,更改文件名为北京市空气质量数据。
数据含义解释:
数据名称 | 含义 |
---|---|
日期 | 空气质量监测的日期 |
AQI | 空气质量指数 |
质量等级 | 空气质量等级,判段污染程度 |
PM2.5 | 空气中细颗粒物的含量 |
PM10 | 空气中人体可吸入颗粒物的含量 |
SO2 | 空气中二氧化硫的含量 |
CO | 空气中一氧化碳的含量 |
NO2 | 空气中二氧化氮的含量 |
03 | 空气中臭氧的含量 |
一、空气质量监测数据的预处理
数据预处理的目标如下:
- 根据空气质量监测的日期,生成对应的季度标志变量。
- 对空气质量指数AQI分组,获得对应的空气质量等级。
代码及运行结果如下所示:
import numpy as np
import pandas as pd
data=pd.read_excel('北京市空气质量数据.xlsx') # 数据文件地址
data=data.replace(0,np.NaN)
data['年']=data['日期'].apply(lambda x:x.year)
month=data['日期'].apply(lambda x:x.month)
quarter_month='1':'一季度','2':'一季度','3':'一季度',
'4':'二季度','5':'二季度','6':'二季度',
'7':'三季度','8':'三季度','9':'三季度',
'10':'四季度','11':'四季度','12':'四季度'
data['季度']=month.map(lambda x:quarter_month[str(x)])
bins=[0,50,100,150,200,300,1000]
data['等级']=pd.cut(data['AQI'],bins,labels=['一级优','二级良','三级轻度污染','四级中度污染','五级重度污染','六级严重污染'])
print('对AQI的分组结果:\\n0'.format(data[['日期','AQI','等级','季度']]))
# 运行结果如下:
对AQI的分组结果:
日期 AQI 等级 季度
0 2014-01-01 81.0 二级良 一季度
1 2014-01-02 145.0 三级轻度污染 一季度
2 2014-01-03 74.0 二级良 一季度
3 2014-01-04 149.0 三级轻度污染 一季度
4 2014-01-05 119.0 三级轻度污染 一季度
... ... ... ... ...
2150 2019-11-22 183.0 四级中度污染 四季度
2151 2019-11-23 175.0 四级中度污染 四季度
2152 2019-11-24 30.0 一级优 四季度
2153 2019-11-25 40.0 一级优 四季度
2154 2019-11-26 73.0 二级良 四季度
[2155 rows x 4 columns]
代码说明:
(1)第6行:利用数据框函数replace()将数据框中的0(表示无监测结果)替换为缺失值NaN。
(2)第7,8行:利用.apply()方法以及匿名函数,基于“日期”变量得到每个样本观测的年份和月份。
(3)第9-12行:建立一个关于月份和季度的字典quarter_month。
(4)第13行:利用Python函数map(),依据字典quarter_month,将序列month中的1,2,3等月份映射(对应)到相应的季度上。
(5)第14行:生成一个后续用于对AQI分组的列表bins。它描述了AQI和空气质量等级的数值对应关系。
(6)第15行:利用Pandas的cut()方法对AQI进行分组。
二、上例中所用到的函数讲解
2.1 lambda表达式
介绍:
Lambda 表达式是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象,是一个匿名函数,即没有函数名的函数。Lambda表达式可以表示闭包。
示例:
double1 = lambda x:2*x
print("lambda表达式的输出:",double1(2))
def double2(x):
return 2*x
print("double2函数的输出",double2(2))
# 输出结果如下:
lambda表达式的输出: 4
double2函数的输出 4
详细可参考博客:python的lambda表达式详细讲解
2.2 apply()函数
介绍:
apply函数是pandas里面所有函数中自由度最高的函数。该函数如下:
*DataFrame.apply(func, axis=0, broadcast=False, raw=False, reduce=None,args=(), *kwds)
该函数最有用的是第一个参数,这个参数是函数,相当于C/C++的函数指针。
这个函数需要自己实现,函数的传入参数根据axis来定,比如axis = 1,就会把一行数据作为Series的数据 结构传入给自己实现的函数中,我们在函数中实现对Series不同属性之间的计算,返回一个结果,则apply函数 会自动遍历每一行DataFrame的数据,最后将所有结果组合成一个Series数据结构并返回。
说太多概念性的东西可能不太理解,这里直接上样例:
import pandas as pd
data=pd.read_excel('E:\\python机器学习数据建模与分析\\数据\\北京市空气质量数据.xlsx')
print(data['日期'])
data['年']=data['日期'].apply(lambda x:x.year)
print(data['年'])
# 输出结果如下:
0 2014-01-01
1 2014-01-02
2 2014-01-03
3 2014-01-04
4 2014-01-05
...
2150 2019-11-22
2151 2019-11-23
2152 2019-11-24
2153 2019-11-25
2154 2019-11-26
Name: 日期, Length: 2155, dtype: datetime64[ns]
0 2014
1 2014
2 2014
3 2014
4 2014
...
2150 2019
2151 2019
2152 2019
2153 2019
2154 2019
Name: 年, Length: 2155, dtype: int64
通过输出结果我们其实可以看出,我们使用apply函数可以将日期中的年份提取出来。
想要更加详细了解可以看这篇博客:python中apply函数
2.3 map函数
介绍:
map函数是 Python 内置的高阶函数,在Python3.0版本中,它接收一个函数 f 和一个 list,并通过把函数 f 依次作用在 list 的每个元素上,返回一个list的可迭代对象。如果想得到一个list列表,则用list(map())进行强制转换。
map(function, iterable)
- function – 函数
- iterable – 序列
map函数的第一个参数是一个函数,第二个参数是一个序列,里面的每个元素作为函数的参数进行计算和判断。函数返回值则被作为新的元素存储起来。
示例:
def add(x):
return x**2 #计算x的平方
lists = range(11) #创建包含 0-10 的列表
a = map(add,lists) #计算 0-10 的平方,并映射
print(a) # 返回一个迭代器:<map object at 0x0000025574F68F70>
print(list(a)) # 使用 list() 转换为列表。
# 结果为:[0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
# 使用lambda匿名函数的形式复现上面的代码会更简洁一些
print(list(map(lambda x:x**2,range(11))))
# 结果为:[0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
2.4 cut函数
在对数据进行分段分组时,可采用cut方法,用bins的方式实现。这种情况一般使用于,对于年龄、分数等数据。
import random
import pandas as pd
import numpy as np
from pandas import Series,DataFrame
#用随机数产生一个二维数组。分别是年龄的性别。
df=pd.DataFrame('Age':np.random.randint(0,70,100),
'Sex':np.random.choice(['M','F'],100),
)
#用cut函数对于年龄进行分段分组,用bins来对年龄进行分段,左开右闭
age_groups=pd.cut(df['Age'],bins=[0,18,35,55,70,100])
# print(age_groups)
print(df.groupby(age_groups).count())
# 结果如下所示:
Age Sex
Age
(0, 18] 23 23
(18, 35] 23 23
(35, 55] 31 31
(55, 70] 20 20
(70, 100] 0 0
cut()方法主要用于对连续数据分组,也称对连续数据进行离散化处理。在上面的例子中,我们使用cut(),依照分组标准(即列表bins)对变量AQI进行分组并给出分组标签。即:AQI在区间 ( 0 , 50 ] (0, 50] (0,50]的为一组,组标签为“一级优”,在区间 ( 50 , 100 ] (50,100] (50,100]的为一组,组标签为“二级良”,等等以此类推。生成的“等级”与变量(与数据集中原有的“质量等级”一致)为分类型(有顺序的)变量。
补充解释DataFrame函数:
DataFrame是一个类似于二维数组或表格(如excel)的对象,它每列的数据都可以是不同的数据类型。
注意:
DataFrame的索引不仅有行索引,还有列索引,数据可以有多列
创建方式:
Pandas的DataFrame类对象的原型如下(仅作了解):
pandas.DataFrame(data = None,index = None,columns = None,dtype = None,copy = False )
index:表示行标签。若不设置该参数,则默认会自动创建一个从0~N的整数索引。
columns:列标签
举个例子:
通过传入数组来创建DataFrame类对象
import numpy as np
import pandas as pd
# 创建数组
demo_arr = np.array([['a', 'b', 'c'],
['d', 'e', 'f']])
# 基于数组创建DataFrame对象
df_obj = pd.DataFrame(demo_arr)
print(df_obj)
# 输出结果如下:
0 1 2
0 a b c
1 d e f
在创建DataFrame类对象时,如果为其指定了列索引,则DataFrame的列会按照指定索引的顺序进行排列,比如指定列索引No1,No2, No3的顺序:
import numpy as np
import pandas as pd
# 创建数组
demo_arr = np.array([['a', 'b', 'c'],
['d', 'e', 'f']])
# 基于数组创建DataFrame对象
df_obj = pd.DataFrame(demo_arr, columns=['No1', 'No2', 'No3'])
print(df_obj)
# 输出结果如下:
No1 No2 No3
0 a b c
1 d e f
详细了解请看博客:Pandas数据结构–Series、DataFrame详解
三、空气质量监测数据的基本分析
在上面的基础上,我们利用Pandas的数据分类汇总和列联表编制等功能,对空气监测数据进行基本分析。基本分析的目标如下:
- 计算各季度AQI和PM2.5的平均值等描述统计量。
- 找到空气质量较差的若干天的数据,以及各季度中空气质量较差的若干天的数据。
- 计算季度和空气质量等级的交叉列联表。
- 派生空气质量等级的虚拟变量。
- 数据集的抽样。
3.1 基本统计描述
以下代码利用Pandas实现以上前三个目标:
print('各季度AQI和PM2.5的均值:\\n0'.format(data.loc[:,['AQI','PM2.5']].groupby(data['季度']).mean()))
print('各季度AQI和PM2.5的描述统计量:\\n',data.groupby(data['季度'])['AQI','PM2.5'].apply(lambda x:x.describe()))
def top(df,n=10,column='AQI'):
return df.sort_values(by=column,ascending=False)[:n] # 对AQI列的数据进行降序排列,然后返回前n个(这里n=10)
print('空气质量最差的5天:\\n',top(data,n=5)[['日期','AQI','PM2.5','等级']])
print('各季度空气质量最差的3天:\\n',data.groupby(data['季度']).apply(lambda x:top(x,n=3)[['日期','AQI','PM2.5','等级']]))
print('各季度空气质量情况:\\n',pd.crosstab(data['等级'],data['季度'],margins=True,margins_name='总计',normalize=False))
输出结果如下图所示:
代码说明:
(1)第1行:利用数据框的groupby()方法,计算各季度AQI和PM2.5的平均值。groupby()方法是将数据按指定变量进行分组,可以对分组结果进一步计算均值等。
(2)第2行:计算几个季度AQI和PM2.5的基本描述统计量(均值,标准差,最小值,四分位数,最大值)。这里将groupby、apply以及lambda表达式集中在一起使用。首先,将数据按照季度分组;然后,对分组后的AQI和PM2.5,分别根据lambda表达式指定的处理步骤处理(计算基本描述统计量)。
(3)第4,5行:定义了一个名为top的用户自定义函数:对给定数据框,按指定列(默认AQI列)值的降序排序,返回排在前n(默认10)条数据。
(4)第6行:调用用户自定义函数top,对data数据框中,按AQI值的降序排序并返回前5条数据,即AQI最高的5天的数据。
(5)第7行:首先对数据按季度分组,依次对分组数据调用用户自定义函数top,得到各季度AQI最高的3天数据。
(6)第8行:利用Pandas函数crosstab()对数据按季度和空气质量等级交叉分组,并给出各个组的样本量。
例如,在2014年1月至2019年11月之间的2149天中,空气质量为严重污的天数为46天,集中分布在第一和第四季的冬天供暖季,分别是21天和23天。
crosstab()函数可以方便地编制两个分类变量的列联表。列联表单元格可以是频数,也可以是百分比,还可指定是否添加行列合计等。
3.2 groupby函数
pandas对象支持的groupby()方法语法格式如下:
groupby(by=None, axis=0, level=None, as_index=True, sort=True, group_keys=True, squeeze=False)
- 参数by用于指定分组依据,可以是函数、字典、Series对象、DataFrame对象的列名等;
- 参数axis表示分组轴的方向,可以是0或’index’,1或’columns’,默认值为0;
- 参数level表示如果某个轴是一个MultiIndex对象(层级索引),则按照特定级别或多个级别分组;
- 参数as_index=False表示用来分组的列中的数据不作为结果DataFrame对象的index;
- 参数sort指定是否对分组标签进行排序,默认值为True。
使用groupby()方法可以实现两种分组方式,返回的对象结果不同。如果仅对DataFrame对象中的数据进行分组,将返回一个DataFrameGroupBy对象;如果是对DataFrame对象中某一列数据进行分组,将返回一个SeriesGroupBy对象。
# 按列名对列分组
obj1 = data['Country'].groupby(data['Region'])
print(type(obj1))
# out
<class'pandas.core.groupby.generic.SeriesGroupBy’>
# 按列名对数据分组
obj2 = data.groupby(data['Region'])
print(type(obj2))
# out
<class'pandas.core.groupby.generic.DataFrameGroupBy'>
可以使用groupby(‘label’)方法按照单列分组,也可以使用groupby(‘label1’,‘label2’)方法按照多列分组,返回一个GroupBy对象。
data.groupby('Region')# 按单列分组
# out:<pandas.core.groupby.generic.DataFrameGroupByobject at 0x7f0aee73e850>
data.groupby(['Region', 'Country'])# 按多列分组
# out:<pandas.core.groupby.generic.DataFrameGroupByobject at 0x7f0aedeb99d0>
使用数据分组的groupby()方法返回一个GroupBy对象,此时并未真正进行计算,只是保存了数据分组的中间结果。
3.3 派生虚拟自变量
这里,利用Pandas派生空气质量等级的虚拟变量。
虚拟变量也称作哑变量,是统计学处理分类型数据的一种常用方式。对具有K个类别的分类型变量X,也可以生成K个变量如
X
1
,
X
2
,
.
.
.
,
X
K
X_1,X_2,...,X_K
X1,X2,...,XK,且每个变量仅有0和1两种取值。这些变量称为分类型变量的虚拟变量。其中,1表示属于某个类别,0表示不属于某个类别,和True和False含义差不多。
虚拟变量在数据预测建模中将起到非常重要的作用。Pandas生成虚拟变量的实现如下所示:
pd.get_dummies(data['等级'])
data.join(pd.get_dummies(data['等级']))
代码说明:
(1)第1行:利用Pandas的get_dummies得到分类型变量“等级”的哑变量。
例如:数据中的“等级”是包含6个类别的分类型变量。相应的6个虚拟变量依次表示:是否为一级优,是否为二级良等等。如2014年1月1日的等级为二级良,所以后面二级良的哑变量为1,其它的相应为0。
(2)第2行:利用数据框的join()方法,将原始数据和哑变量数据,按行索引进行横向合并。
使用join()方法进行数据的横向合并的时候,要确保两分数据的样本观测在行索引上是一一对应的,否则会出现“张冠李戴”,也就是哑变量的取值和实际不符。
3.4 数据集的抽样
数据集的抽样在数据建模中极其普遍,因此掌握Numpy的抽样实现方式是非常必要的。以下利用Numpy对空气质量监测数据进行了两种策略的抽样:一种是简单随机抽样;另一种是依条件抽样。
# 简单随机抽样
np.random.seed(123)
sampler=np.random.randint(0,len(data),10)
print(sampler)
sampler=np.random.permutation(len(data))[:10]
print(sampler)
# 条件抽样
data.take(sampler)
data.loc[data['质量等级']=='优',:]
简单随机抽样的结果如下:
[1346 1122 1766 2154 1147 1593 1761 96 47 73]
[1883 326 43 1627 1750 1440 993 1469 1892 865]
条件抽样的结果如下:
代码说明:
(1)第3行:利用Pandas函数random.randint()在指定范围内随机抽取指定个数(这里是10)的随机数。
(2)第 5行:利用Pandas函数random.permutation是对数据随机打乱重排。之后再抽取前10个样本观测。
(3)第8行:利用数据框的take()方法,基于指定随机数获得数据集的一个子集。
(4)第9行:利用数据框访问的方式,抽取满足指定条件(质量等级等于优)行的数据。
四、Matplotlib的综合应用:空气质量监测数据的图形化展示
Matplotlib是Python中最常用的绘图模块,其主要特点如下:
(1)Matplotlib的Pyplot子模块与MATLAB非常相似,可以方便地绘制各种常见的统计图形,是用户进行探索式数据分析的重要工具。
(2)可以通过各种函数设置图形的图标题、线条样式、字符形状、颜色、轴属性以及字体属性等等。
以下我们就用Matplotlib子模块Pyplot的强大功能基于空气质量监测数据进行画图。
4.1 AQI的时序变化特点
以下代码利用Matplotlib的线图展示2014年至2019年每日AQI的时序变化特点(运行环境选取jupyter notebook):
import numpy as np
import pandas as<以上是关于Python机器学习数据建模与分析——Numpy和Pandas综合应用案例:空气质量监测数据的预处理和基本分析的主要内容,如果未能解决你的问题,请参考以下文章