ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

数据竞赛思路分享:机场客流量的时空分布预测

2020-02-01 16:55:27  阅读:233  来源: 互联网

标签:10 竞赛 09 ts pd 客流量 时空 2016 E1


历时两个月的比赛终于结束了,最终以第32名的成绩告终,在此和大家分享下解决问题的思路。

从初赛到复赛,有走过弯路,也有突然灵光一现的时刻。一路走来,对数据各种把玩,分析了各种可能的情况,尽可能得挖掘数据中潜在的信息来构建更为准确的模型。

本文无法涵盖所有的分析历程,但是会涉及解决问题的主要思路以及部分代码,详细的代码见Github页面

竞赛详细信息参见比赛官方网站


1. 问题描述

机场拥有巨大的旅客吞吐量,与巨大的人员流动相对应的则是巨大的服务压力。安防、安检、突发事件应急、值机、行李追踪等机场服务都希望能够预测未来的旅客吞吐量,并据此提前调配人力物力,更好的为旅客服务。本次大赛以广州白云机场真实的客流数据为基础,每天数万离港旅客在机场留下百万级的数据记录。希望参赛队伍通过数据算法来构建客流量预测模型。


2. 数据概览

提供的数据:

Table
连接WIFI AP (Access Point)的人数表 airport_gz_wifi_ap
安检旅客过关人数表 airport_gz_security_check
旅客进入-离开机场的行程表 airport_gz_departure
航班排班表airport_gz_flights [比赛一段时间后才提供]
机场登机口区域表 airport_gz_gates [比赛一段时间后才提供]
机场WIFI接入点坐标表 airport_gz_wifi_coor [复赛提供的]

例如airport_gz_wifi_ap 表数据概览:

wifi_ap_tag passenger_count time_stamp
E1-1A-1 15 2016-09-10-18-55-04
E1-1A-2 15 2016-09-10-18-55-04
E1-1A-3 38 2016-09-10-18-55-04
E1-1A-4 19 2016-09-10-18-55-04

提交表格案例:

passenger_count wifi_ap_tag slice10min
1.1 E1-1A-1 2016-09-14-15-0
2.2 E1-1A-1 2016-09-14-15-1
3.3 E1-1A-1 2016-09-14-15-2
4.4 E1-1A-1 2016-09-14-15-3
5.5 E1-1A-1 2016-09-14-15-4

3. 初赛

3.1初赛数据描述

初赛提供了2016-10-09至2016-09-25的数据

3.2初赛问题描述

选手需要预测未来三小时(9月25日15:00:00到18:00)的时间窗口里,机场内每个WIFI AP点每10分钟内的平均设备连接数量

3.3初赛解决方案

简要概括:均值加趋势

数据预处理:

提供的表格中时间数据都是精确到秒,而所提交的结果要求是每10分钟的平均情况,所以我们首先需要将数据按照每十分钟的间隔汇总起来(详细代码见Github)

此处提供两种方案:

  1. 以airport_gz_wifi_ap表为例截取time_stamp的部分字符串,然后按照截取的time_stamp和wifi_ap_tag进行aggregate

    t = t0[:15] # 例如将t0 = 2016-09-10-18-55-04截取为t = 2016-09-10-18-5 
    
  2. 将数据按照时间排序,然后抽出每十分钟的数据进行处理后整合,这个方式可能会比较麻烦,但是这个方式有他的优势,我们只需调整一个参数,便能让数据按照任意的时间间隔进行统计,便于以后复用函数

此处附加Python处理时间格式的一些函数

我们可以直接使用pandas中的参数解析时间数据

# Normal
df =pd.read_csv(path, parse_dates=['column name'])
# Special
dateparse = lambdax: pd.datetime.strptime(x, '%Y-%m-%d %H:%M:%S') 
df =pd.read_csv(path, parse_dates=['column name'], date_parser=dateparse)

当然也可以自己写函数处理

import pandas as pd
def ReturnTimeElement(Date):
    return [int(t) for t in Date.split('-')]
def TransToTime(TimeElement):
    return pd.datetime(*(TimeElement))
def GetTime(Date):
    TimeElement = ReturnTimeElement(Date)
    Time = TransToTime(TimeElement)
    return Time
T = '2016-10-19-9-47-00'
>>> GetTime(T)
datetime.datetime(2016, 10, 19, 9, 47)

处理后可以得到如下数据,命名为WIFITAPTag_Mean_All

PassengerCountMean Time WIFIAPTag
16.2 2016/9/10 19:00 E1-1A-1
19.7 2016/9/10 19:10 E1-1A-1
19.7 2016/9/10 19:20 E1-1A-1
20.5 2016/9/10 19:30 E1-1A-1
20.5 2016/9/10 19:40 E1-1A-1
24.8 2016/9/10 19:50 E1-1A-1

问题分析:

对于这个预测问题有以下关键两点:

  1. 机场每天的排班表基本稳定,用户在机场内的行走模式也基本稳定
  2. 时间序列具有一定程度的连续性,下午三点至六点的情况会一定程度延续此前几小时的情况

基于以上两点想法,就得到了两个基本模型:均值模型时间序列模型

比赛初期只提供了前三个表格,所以开始就注重分析了这几个表格,例如从WIFIAPTag中可以提取出大概的位置信息和楼层信息,分组统计不同区域的WIFIAP是否有接近的模式,同时也可从安检和出发表格中寻找一定的关联等等。

但是经过分析发现,airport_gz_security_check及airport_gz_departure的数据虽然和airport_gz_wifi_ap的数据有一定的关联,但是其本身存在较大的随机因素,用随机预测随机存在太大的变数,不如只使用airport_gz_wifi_ap中的数据进行更稳定的预测(当然肯定也有队伍能很好得从airport_gz_security_check及airport_gz_departure中提出很很棒的特征)。后期提供的几个表格由于数据质量问题,经分析后发现贡献不是特别大,故也没有进一步利用。

因而之后要说的均值模型时间序列模型都基于WIFITAPTag_Mean_All表格的数据,并且是以WIFIAP为对象, 每一个分开预测。

数据探索:

接下来让我们对数据有一个大概的了解

def GetTimeSeries(WIFIAPTag):
    '''
    Get WIFIAPTag 's Time Series
    '''
    Tag_Data = WIFITAPTag_Mean_All[WIFITAPTag_Mean_All.WIFIAPTag == WIFIAPTag]
    MinTime = min(Tag_Data.Time)
    MaxTime = max(Tag_Data.Time)
    DataTimeRange = pd.date_range(start = MinTime , end = MaxTime , freq = '10Min')
    ts_0 = pd.Series([0]*len(DataTimeRange),index=DataTimeRange)
    ts =pd.Series(Tag_Data.PassengerCountMean.values , index = Tag_Data.Time)
    TS = ts_0+ts
    TS = TS.fillna(0)
    return TS

以上函数能提取出特定WIFIAP的时间序列数据,及每10分钟的平均连接数

ts = GetTimeSeries('E1-1A-1<E1-1-01>')
ts
Out[7]: 
2016-09-10 19:00:00    16.2
2016-09-10 19:10:00    19.7
2016-09-10 19:20:00    19.7
...
2016-09-25 14:30:00    11.1
2016-09-25 14:40:00     8.0
2016-09-25 14:50:00    10.9
dtype: float64

绘图结果如下,可以看出每天还是有一定的规律,但是有异常的日子

ts.plot()

由于我们需要预测的是特定某几个小时的数据,所以需要如下函数提取部分的时间序列

def Get_Part_of_TimeSeries(TS,TimeRange):
    '''
    Input [start_time,end_time]
    '''
    return TS[TimeRange[0]:TimeRange[1]]

均值模型需要考虑之前每一天同一时间段的情况,所以有如下函数

def GenerateTs_0(Time):
    timerange = pd.date_range(start = Time[0],end = Time[1] ,freq = '10Min')
    ts = pd.Series(np.zeros(len(timerange)),index = timerange)
    return ts
  
def TsList(WIFIAPTag,Time):
    ts_list=[]
    ts = GetTimeSeries(WIFIAPTag)
    for i in range(1,15):
        TimeRange = Time - timedelta(i)
        ts_part = Get_Part_of_TimeSeries(ts,TimeRange)
        if len(ts_part) == 0 or ts_part.isnull().any():
            ts_list.append(GenerateTs_0(TimeRange))
        else:
            ts_list.append(ts_part)
    return np.array(ts_list)

使用以上函数便可以得到如下结果,其中array的每一行是之前每天下午三点到下午6点的数据

Time = np.array([pd.datetime(2016,9,25,15,0,0),pd.datetime(2016,9,25,17,50,0)])

Time
Out[11]: 
array([datetime.datetime(2016, 9, 25, 15, 0),
       datetime.datetime(2016, 9, 25, 17, 50)], dtype=object)

ts_list = TsList('E1-1A-1<E1-1-01>',Time)
ts_list
Out[25]: 
array([[  1.5,   0.4,   1.8, ...,  15. ,  15.9,  18.4],
       [ 11. ,  11.3,  13.4, ...,   6.3,   7.2,   9.4],
       [  7. ,   5.5,   4.9, ...,   4.9,   4. ,   6.4],
       ..., 
       [ 13.6,  16.4,  16.7, ...,   4.7,   3.9,   4.1],
       [  3.8,   4.2,   6. , ...,  10.2,   9.6,  19.4],
       [  5.2,   3.2,   4.2, ...,   5. ,   4.5,   4.3]])

但是之前这么多天必然有比较异常的日子,所以需要写如下函数将异常的日子过滤掉,此处的过滤策略是:

对每天特定时间段的数据求均值与标准差,然后将均值与标准差落在10%分位数以下和90%分位数以上的日子去除

def TrueFalseListCombine(TFlist1,TFlist2):
    return [l1 and l2 for l1,l2 in zip(TFlist1,TFlist2)]
 
def ExceptOutlier(ts_list):
    Mean = pd.DataFrame([np.mean(i) for i in ts_list])
    mean_low = Mean > Mean.quantile(0.1)
    mean_up = Mean < Mean.quantile(0.9)
    TF = TrueFalseListCombine(mean_low.values,mean_up.values)
    mean_index = Mean[TF].index.values    
    Std = pd.DataFrame([np.std(i) for i in ts_list])
    std_low = Std > Std.quantile(0.1)
    std_up = Std < Std.quantile(0.9)
    TF = TrueFalseListCombine(std_low.values,std_up.values)
    std_index = Std[TF].index.values  
    valid_index = list(set(mean_index)&set(std_index))
    return valid_index 

例如对刚生成的ts_list处理得到

ExceptOutlier(ts_list)
Out[26]: [0, 1, 2, 3, 4, 6, 8, 9, 10, 12]

为了更直观我们使用如下函数绘图

def DrawTsList(ts_list):
    plt.plot(ts_list.T)
DrawTsList(ts_list) DrawTsList(ts_list[ExceptOutlier(ts_list)])

上图左侧为所有日子的时间序列,右图为去除异常日子之后的时间序列,可以看出已经将特别异常的几天去除了

均值模型:

每天的量值都存在一定的差异,直接将所有去除异常之后的日子取均值并不是特别好的策略,在此我们认为,机场下午3点至6点的人流总量应当与当天这个时刻之前的人流量存在一定的关系,所以我们取了上午6点到下午3点这一时间段的数据作为人流量值的参考。我们是有预测当天上午6点到下午3点的数据的,故可以依据此和之前去除异常后的多天该时间段的数据计算之前各天下午3点到下午6点数据的贡献度。

基于以上思想,并做了一点小修改写了如下两个模型,两个模型比较接近,但是在某些WIFIAP上其中一个会表现好很多,这使得我们之后利用误差分析挑选模型时多一个候选模型。

def Ratio(L):
    return np.array(L*1.0/sum(L))

def Imitate1(WIFIAPTag,TrainTime,PredictTime):

    TrainTimeTsList = TsList(WIFIAPTag,TrainTime)
    PredictTimeTsList = TsList(WIFIAPTag,PredictTime)
    IndexWithoutOutlier = ExceptOutlier(PredictTimeTsList)
    
    ValidTrainTimeTsList = TrainTimeTsList[IndexWithoutOutlier]
    ValidPredictTimeTsList = PredictTimeTsList[IndexWithoutOutlier]
    PredictDayTrainTs = Get_Part_of_TimeSeries(GetTimeSeries(WIFIAPTag),TrainTime)
    
    if len(PredictDayTrainTs) == 0:
        PredictTs = ValidPredictTimeTsList.mean(axis=0)
    else:
        MeanPredictDayTrainTs = PredictDayTrainTs.mean()
        MeanValidTrainTimeTsList = ValidTrainTimeTsList.mean(axis=1)
        
        RatioList = Ratio(MeanPredictDayTrainTs/MeanValidTrainTimeTsList)
        PredictTs = np.dot(ValidPredictTimeTsList.T,RatioList)
        
    PredictTimeRange = pd.date_range(start = PredictTime[0],end = PredictTime[1] ,freq = '10Min')
    TS_Predict = pd.Series(PredictTs,index = PredictTimeRange)
    
    return TS_Predict 

def Imitate2(WIFIAPTag,TrainTime,PredictTime):
    
    TrainTimeTsList = TsList(WIFIAPTag,TrainTime)
    PredictTimeTsList = TsList(WIFIAPTag,PredictTime)
    IndexWithoutOutlier = ExceptOutlier(PredictTimeTsList)
    
    ValidTrainTimeTsList = TrainTimeTsList[IndexWithoutOutlier]
    ValidPredictTimeTsList = PredictTimeTsList[IndexWithoutOutlier]
    PredictDayTrainTs = Get_Part_of_TimeSeries(GetTimeSeries(WIFIAPTag),TrainTime)
    
    if len(PredictDayTrainTs) == 0:
        PredictTs = ValidPredictTimeTsList.mean(axis=0)
    else:
        MeanPredictDayTrainTs = PredictDayTrainTs.mean()
        MeanValidTrainTimeTsList = ValidTrainTimeTsList.mean(axis=1)
        
        RatioList = MeanPredictDayTrainTs/MeanValidTrainTimeTsList
        PredictTs = np.dot(ValidPredictTimeTsList.T,RatioList) / len(RatioList)
        
    PredictTimeRange = pd.date_range(start = PredictTime[0],end = PredictTime[1] ,freq = '10Min')
    TS_Predict = pd.Series(PredictTs,index = PredictTimeRange)
    
    return TS_Predict
    

大专栏

标签:10,竞赛,09,ts,pd,客流量,时空,2016,E1
来源: https://www.cnblogs.com/liuzhongrong/p/12248987.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有