2021华为杯数学建模B题“空气质量预报二次建模” 预处理思路+Python代码

简介

前阵子和小伙伴做了2021年华为杯研赛的B题“空气质量预报二次建模”,发现数据预处理一块挺有意思的,涵盖了常规的缺失值(随机缺失、指标缺失/列缺失、条目缺失/行缺失)、异常值(偏离正态分布、非负数据为负),以及不常规的协同处理等,一直想着有机会整理一下。

【注】只想看协同处理部分,即“4. 监测点A、A1、A2、A3数据预处理”的可直接转到[Python] 反距离权重插值案例及代码_禾木页的博客-CSDN博客

目录

1. 赛题及数据

1.1 竞赛试题

1.2 数据集预览

2. 监测点A数据预处理

2.1 监测点A一次预报数据处理

2.1.1 缺失值处理

2.1.2 异常值处理

2.2 监测点A实测时数据处理

2.2.1 整行数据缺失:删失处理

2.2.2 缺失值填补法1:前后均值填充

2.2.3 缺失值填补法2:随机森林插值

2.2.4 异常值处理step1:3西格玛原则

2.2.5 异常值处理step2:非负数据变成0

2.3 监测点A实测日数据处理

2.3.1 缺失值填补:用时数据均值填补

2.3.2 异常值处理step1:用时数据均值替换

2.3.3 异常值处理step2:3西格玛原则

2.3.4 异常值处理step3:非负数据变成0

3. 监测点B、C数据预处理 

4. 监测点A1、A2、A3数据预处理 

4.1 监测点A1、A2、A3一次预报数据

 4.2 监测点A1、A2、A3实测时数据处理

4.2.1 缺失值处理step1:反距离权重插值(IDW)

4.2.2 缺失值处理step2:随机森林插补

 4.2.3 异常值处理:3西格玛+非负数据处理

4.3 监测点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\...)  
os.getcwd()
#导入数据:监测点A的三个数据集
df1A=pd.read_excel("附件1 监测点A空气质量预报基础数据.xlsx","监测点A逐小时污染物浓度与气象一次预报数据")
df2A=pd.read_excel("附件1 监测点A空气质量预报基础数据.xlsx","监测点A逐小时污染物浓度与气象实测数据")
df3A=pd.read_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 = df1A.replace('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)                False
云量                    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天,分析结果应该无误。

缺失值处理:
一般情况下,都需要对缺失值进行填充,除实际意义外,若数据集中含有缺失值,后面很多命令是会报错的。但在这里我们并不补充缺失值,原因如下:

一是缺失的是整条数据,对后面数据处理不存在影响,只是后续需要合并不同监测点的数据时,不能简单的横向拼接;

二是考虑实际意义,一个“模型运行日期”的缺失代表预测的连续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
年份                                                                          
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()             #统计“模拟运行日期”,主要是想要它的index
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',
       
  • 16
    点赞
  • 163
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

禾木页

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值