2021华为杯数学建模B题“空⽓质量预报⼆次建模”预处理思路
+Python代码
简介
前阵⼦和⼩伙伴做了2021年华为杯研赛的B题“空⽓质量预报⼆次建模”,发现数据预处理⼀块挺有意思的,涵盖了常规的缺失值(随机缺失、指标缺失/列缺失、条⽬缺失/⾏缺失)、异常值(偏离正态分布、⾮负数据为负),以及不常规的协同处理等,⼀直想着有机会整理⼀下。
【注】只想看协同处理部分,即“4. 监测点A、A1、A2、A3数据预处理”的可直接转到:
⽬录
1. 赛题及数据
赛题及数据⼤家可⾃⾏前往“”下载,或者直接在⽹上搜索。
1.1 竞赛试题
2021年B题题⽬为“空⽓质量预报⼆次建模”,简⽽⾔之,核⼼任务是让你⽤已有的基于WRF-CMAQ模型
得到的⼀次预报数据(包含6个污染物浓度指标和15个⽓象指标),加上提供的实测数据(6个污染物浓度指标和5个⽓象指标),在⼀次预报模型之上,建⽴⼀套⽐它更优的模型,因此叫⼆次建模。
更优的标准是,使⽤⼆次建模预测结果中“空⽓质量指数AQI”预报值的最⼤相对误差应尽量⼩,且“⾸要污染物”他预测准确度尽量⾼。
1.2 数据集预览
官⽅提供数据集如下。
附件1 监测点A空⽓质量预报基础数据
附件2 监测点B、C空⽓质量预报基础数据
附件3 监测点A1、A2、A3空⽓质量预报基础数据
从附件名字可以推测数据集的内部结构应该基本⼀致,只是监测点不同⽽已。事实也是如此,因此下
⾯主要概述监测点A的数据,其他同理。
时间那⾥有点乱,其实不难理解,重点关注“结束时间”就好,⼤家的结束⽇期都是2021年7⽉13⽇。
怎么理解呢?
⾸先,题⽬对时间表达有⼀个设定,即7:00代表的时点段为7:00-8:00,23:00代表的时间段是23:00-24:00。在此基础上:
(1)sheet1是预报的数据,题⽬设定是可以往后预报三天(当天+后两天),因此它站在7⽉13号预测,可以预测到7⽉15号。
(2)sheet2是实测时数据,题⽬设定每天预报的时间是早上七点,也就是说在7⽉13号做预报时,七
点及之前所有真实的数据是可以获取的,因此“实测时数据”截⽌到7⽉13号7:00。
◆按道理,“7:00”代表7-8点,那么早上七点预测,“7:00”的数据应该是不可以⽤的,只能⽤到6:00(代表6-7点),但是题⽬⾃⼰设定可⽤,就不给⾃⼰增加难度啦~
(3)sheet3是实测⽇数据,经初步的验证,除O3外(后⾯会讲),发现“实测⽇数据≈实测时数据的24h平均”,7⽉13⽇时间数据只到早上七点,那它当然没有当天的⽇数据啦,所以只到7⽉12号。
2. 监测点A数据预处理
数据预处理⼀般包括缺失值处理和异常值处理。
缺失值处理⼗分灵活,通常还要考虑数据实际含义;⽽且缺失值填补结果的优劣对后续的分析也有较⼤影响,本⽂⼤篇幅也是在进⾏缺失值的处理。
异常值处理相对⽽⾔较为简单,⼀般分两类异常。⼀是数据形态的异常,可⽤3西格玛原则处理;⼆是逻辑异常,⽐如数据集出现“⾮负数值为负”的情况;其他情况需根据具体案例判断是否存在异常。
以下数据处理均在Spyder3.7中进⾏。
import os
import pandas as pd
import numpy as np
#设置⼯作⽬录
os.chdir('C:\\Users\...)
#导⼊数据:监测点A的三个数据集
ad_excel("附件1 监测点A空⽓质量预报基础数据.xlsx","监测点A逐⼩时污染物浓度与⽓象⼀次预报数据")
ad_excel("附件1 监测点A空⽓质量预报基础数据.xlsx","监测点A逐⼩时污染物浓度与⽓象实测数据")
ad_excel("附件1 监测点A空⽓质量预报基础数据.xlsx","监测点A逐⽇污染物浓度实测数据")
2.1 监测点A⼀次预报数据处理
2.1.1 缺失值处理
如果不做实际案例,⽹上的数据预处理教程往往是通过某命令到缺失的位置,然后通过⼀些⽅法(如均值、众数、随机森林等)填充缺失值。但在这⾥你会发现,如果直接缺失值,结果会显⽰不存在缺失。
由于isnull,any()只识别nan类型的缺失,为防⽌缺失的类型是null,我们将null转化为nan再查⼀次,结果还是显⽰不存在缺失。
##df1A数据预处理
#查变量是否存在缺失值
df1A.isnull().any()
#避免缺失的类型是null,将null转化为nan
df1A = place('null',np.NaN)
df1A.isnull().any()
Out[1]:
模型运⾏⽇期                False
预测时间                  False
地点                    False
近地2⽶温度(℃)            False
地表温度(K)              False
⽐湿(kg/kg)            False
湿度(%)                False
近地10⽶风速(m/s)          False
近地10⽶风向(°)            False
⾬量(mm)                Falsemultibank
云量                    False
边界层⾼度(m)              False
⼤⽓压(Kpa)              False
感热通量(W/m²)            False
潜热通量(W/m²)            False
长波辐射(W/m²)            False
短波辐射(W/m²)            False
地⾯太阳能辐射(W/m²)        False
SO2⼩时平均浓度(µg/m³)      False
NO2⼩时平均浓度(µg/m³)      False
PM10⼩时平均浓度(µg/m³)    False
PM2.5⼩时平均浓度(µg/m³)    False
O3⼩时平均浓度(µg/m³)      False
CO⼩时平均浓度(mg/m³)      False
dtype: bool
这就代表数据没有缺失值吗?
简述数组指针和指针数组的区别
答案显然是否定的。数据缺失,不⼀定是“挖空式”的缺失,⽐如这份数据,如果是“模拟运⾏⽇期”或者“预测时间”的整⼀条数据缺失,通过上述⽅法是查不到的。解决⽅式:
1、查看每⼀个“模型运⾏⽇期”下,是否有某个⼩时的缺失。
题⽬设定:每次模拟可以预测当天+未来两天共3天(72⼩时)的数据。
对每⼀个“模型运⾏⽇期”进⾏计数,发现每个“模型运⾏⽇期”下均有72条数据,即预测的三天。说明只要进⾏了预测,就⼀定会⼀次性预测三天的,不存在中间某个⼩时的预测缺失,代码见下⽂。
2、查看“模型运⾏时间”是否存在缺失。
⽅法1:上⼀步的输出结果的Index就是每⼀个模型运⾏⽇期,可以通过[F.index]逐⼀查看每个⽇期。但在这道题中,时间跨度将近⼀年,这样⼦看太⿇烦了。
⽅法2:统计每个⽉份出现的天数,少于正常天数的⽉份说明那个⽉有“模型运⾏⽇期”的缺失,直接去那个⽉份的数据。这⾥通过绘制“年”和“⽉”的交叉表,得到⽉份的情况(详见Out[2]),再根据⽉份情况查看是哪⼀天存在缺失(详见Out[3]-Out[5])。
分析
2020年7⽉份仅9天是因为从2020年7⽉23号开始的,到7⽉31⽇刚好9天,不存在缺失。
2020年11⽉份存在缺失,经查,为:2020-11-11
2021年1⽉份存在缺失,经查,为:2021-1-25
2021年5⽉份存在缺失,经查,为:2021-5-21
2021年7⽉份截⽌到7⽉13⽇,刚好13天,不存在缺失。
验证:不放⼼的话,可以验证⼀下是否仅有3天缺失。
2020年7⽉23⽇-2021年7⽉13⽇,共365-9=356天
F中共353条⽇期数据(或交叉表cross_ym2总和正好为353天[cross_ym2.sum().sum()]),正好缺失3天,分析结果应该⽆误。
88二进制转十进制公式缺失值处理:
⼀般情况下,都需要对缺失值进⾏填充,除实际意义外,若数据集中含有缺失值,后⾯很多命令是会报错的。但在这⾥我们并不补充缺失值,原因如下:
⼀是缺失的是整条数据,对后⾯数据处理不存在影响,只是后续需要合并不同监测点的数据时,不能简单的横向拼接;
⼆是考虑实际意义,⼀个“模型运⾏⽇期”的缺失代表预测的连续72条时数据的缺失,3个⽇期就代表3*72=216条数据缺失,不好补,可能也补不好,所以还是不补了。
既然不补,了解缺失值有什么作⽤呢?
个⼈看来,了解数据概况是预处理的重要⼀步,不管作不作处理,都要做到“⼼中有数”。
##df1A数据预处理
#查看每⼀个“模型运⾏⽇期”下,是否有某个⼩时的缺失
F=df1A['模型运⾏⽇期'].value_counts()
#输出结果:每个⽇期下均有72条数据(略)
#分析:72条时数据即预测的三天。说明只要进⾏了模拟,就会⼀次性模型三天的,不存在中间某个⼩时的预测缺失
#通过交叉表查看是否有“模型运⾏⽇期”的缺失
df1A['年份'] = df1A['模型运⾏⽇期'].dt.year  #提取“模型运⾏⽇期”的年份
df1A['⽉份'] = df1A['模型运⾏⽇期'].dt.month  #提取“模拟运⾏⽇期”的⽉份
cross_ym = pd.crosstab(index=df1A['年份'],columns=df1A['⽉份'],values=df1A['⽉份'],aggfunc='count')
#由于每个“模型运⾏⽇期”对应72条数据,所以还需要除以72
cross_ym2 = cross_ym/72
Out[2]:
⽉份      1    2    3    4    5    6    7    8    9    10    11    12
气象python零基础入门教程年份
2020  NaN  NaN  NaN  NaN  NaN  NaN  9.0  31.0  30.0  31.0  29.0  31.0
2021  30.0  28.0  31.0  30.0  30.0  30.0  13.0  NaN  NaN  NaN  NaN  NaN
#查看是2020-11、2021-1、2021-5中是哪⼀天缺失
lack1 = df1A[(df1A['年份']==2020) & (df1A['⽉份']==11)]  #提取2020年11⽉数据
lack1d = lack1['模型运⾏⽇期'].value_counts()            #统计“模拟运⾏⽇期”,主要是想要它的indexmagento空间
lack1d.index.sort_values()        #.sort_values() 主要为了为⽇期排个序,⽅便看
Out[3]:
DatetimeIndex(['2020-11-01', '2020-11-02', '2020-11-03', '2020-11-04',
'2020-11-05', '2020-11-06', '2020-11-07', '2020-11-08',
'2020-11-09', '2020-11-10', '2020-11-12', '2020-11-13',
'2020-11-14', '2020-11-15', '2020-11-16', '2020-11-17',
'2020-11-18', '2020-11-19', '2020-11-20', '2020-11-21',
'2020-11-22', '2020-11-23', '2020-11-24', '2020-11-25',
'2020-11-26', '2020-11-27', '2020-11-28', '2020-11-29',
'2020-11-30'],
dtype='datetime64[ns]', freq=None)
lack2 = df1A[(df1A['年份']==2021) & (df1A['⽉份']==1)]  #提取2021年1⽉数据
lack2d = lack2['模型运⾏⽇期'].value_counts()
lack2d.index.sort_values()
Out[4]:
DatetimeIndex(['2021-01-01', '2021-01-02', '2021-01-03', '2021-01-04',
'2021-01-05', '2021-01-06', '2021-01-07', '2021-01-08',
'2021-01-09', '2021-01-10', '2021-01-11', '2021-01-12',
'2021-01-13', '2021-01-14', '2021-01-15', '2021-01-16',
'2021-01-17', '2021-01-18', '2021-01-19', '2021-01-20',
'2021-01-21', '2021-01-22', '2021-01-23', '2021-01-24',
'2021-01-26', '2021-01-27', '2021-01-28', '2021-01-29',
'2021-01-30', '2021-01-31'],
dtype='datetime64[ns]', freq=None)
lack3 = df1A[(df1A['年份']==2021) & (df1A['⽉份']==5)]  #提取2021年5⽉数据
lack3d = lack3['模型运⾏⽇期'].value_counts()
lack3d.index.sort_values()
Out[5]:
DatetimeIndex(['2021-05-01', '2021-05-02', '2021-05-03', '2021-05-04',
'2021-05-05', '2021-05-06', '2021-05-07', '2021-05-08',
'2021-05-09', '2021-05-10', '2021-05-11', '2021-05-12',
'2021-05-13', '2021-05-14', '2021-05-15', '2021-05-16',
'2021-05-17', '2021-05-18', '2021-05-19', '2021-05-20',
'2021-05-22', '2021-05-23', '2021-05-24', '2021-05-25',
'2021-05-26', '2021-05-27', '2021-05-28', '2021-05-29',
'2021-05-30', '2021-05-31'],
dtype='datetime64[ns]', freq=None)
2.1.2 异常值处理
由于sheet1数据为基于WRF-CMAQ模型得到的⼀次预报数据,所谓异常值也可能就是这个模型本来的输出结果,⽽不是因为整理或记录错误产⽣的异常。
题⽬要求改进⼀次预报模型,若是模型本来输出的结果就是偏⼤或偏⼩的,没有道理将其作为异常值处理。因此对于附件1"监测点A逐⼩时污染物浓度与⽓象⼀次预报数据",不做异常值处理。
linux定时执行shell脚本到此为⽌,sheet1的预处理⼯作已经完毕。实际上仅是了解了sheet1的数据缺失情况,发现在2020-11-11、2021-01-25、2021-5-21三天没有做预测,并没有进⾏清洗数据的⼯作。
2.2 监测点A实测时数据处理
通过浏览附件1sheet2“监测点A逐⼩时污染物浓度与⽓象实测数据”,⼤致可将数据缺失类型整理如下。