写在前面

emmm,这篇文章就整理一下我处理正式赛题阶段一(读取数据的方式是分块读取,保存为三个csv文件)的过程吧

赛题介绍:

正式赛题——船运到达时间预测
在企业全球化业务体系中,海运物流作为其最重要的一项支撑。其中,船运公司会和数据供应公司进行合作,对运输用的船通过GPS进行定位以监控船的位置;在运输管理的过程中,货物到达目的港的时间是非常重要的一项数据,那么需要通过船运的历史数据构建模型,对目的港到达时间进行预测,预测时间简称为ETA(estimated time of arrival),目的港到达时间预测为ARRIVAL_ETA。
本次大赛提供历史运单GPS数据、历史运单事件数据、港口坐标数据,预测货物运单的到达时间,对应“历史运单事件”数据中EVENT_CODE字段值为ARRIVAL AT PORT时EVENT_CONVOLUTION_DATE的时间值。

一、比赛数据
大赛提供脱敏后的训练数据及测试数据,训练数据集包括:历史运单GPS数据、历史运单事件数据、港口坐标数据,这些数据主要用于参赛队伍训练模型,制定预估策略;测试运单数据为不同运单、运输过程中的不同位置所构成,供选手测试对应的ETA时间。
货物运单在船运过程中,会产生大量的GPS运单数据,记录为“历史运单GPS数据”;货物运单在船运过程中离开起运港、到达中转港、到达目的港等关键事件,记录为“历史运单事件数据”;“港口的坐标数据“为与运单船运相关的港口坐标信息。
允许选手合理增加与题目相关的外部数据进行纠正,如大赛提供的港口坐标数据存在偏差时可自行补充数据纠正。

  1. 历史运单GPS数据
    历史运单GPS数据描述每个运单在船运的过程中,所在船产生的GPS位置的相关信息。

    数据说明:
    每个运单表示一次运输的运输单号,不会重复使用,一次运输过程中的多条GPS数据拥有相同的运输单号。船号为运单货物所在的船编号,会重复出现在不同次运输的GPS数据中。需要注意的是GPS数据中可能会有异常的GPS,可能且不限于如下问题:
    (1) GPS坐标在陆地,或者有些港口是内陆的港口。
    (2) GPS漂移:两点距离过大,超过船的行驶能力。
    (3) GPS在部分地区的比较稀疏(比如南半球、敏感海域)。
    (4) 最后的GPS点可能和港口的距离较远(比如塞港时,或者临近目的港时已无GPS数据)。
    (5) speed字段之后数据可能会有少量缺失(如GPS设备短暂异常)。

  2. 历史运单事件数据
    历史运单事件数据描述每个运单在船运的过程中,与港口相关的关键信息,如离开起运港、到达目的港等。

  3. 港口坐标数据
    港口坐标数据描述每个运单在船运的过程中涉及的港口位置信息。

    数据说明:
    部分地址信息可能已脱敏、缺失或有偏差,选手可自行补充或修正。

  4. 测试运单数据
    测试运单数据为运单运输过程中的不同位置点所构成,供选手测试对应的ETA时间。测试运单数据如下表描述。

    二、选手提交结果
    所有参与竞赛的选手登录到大赛平台,提交结果数据,具体提交格式要求:

    其中,ETA为选手评估的时间值;creatDate为该表或该CSV文件创建时间,用于区别多次提交数据。对于未提交的运单ETA,后台统一取timestamp时间计算。
    三、 评估标准
    选手提交结果的评估指标是MSE,即ARRIVAL AT PORT预测时间ETA与真实时间ATA的差距的平方和,计算如下:

其中:
(1) hETA为同一个货物运单到达目的港口的预测所需时间。选手提供DATE时间,评测程序转换为单位所需时间,单位:小时。
(2) hATA为同一个货物运单到达目的港口的实际所需时间,大赛测评程序后台保存,用于测评运算。
(3) ETA_NUM为预测的ETA数量,测评程序后台运算,大赛测评程序后台保存,用于测评运算。
最终使用MSE值作为参赛选手得分,MSE值越小,排名越靠前。

示例说明:
如某一货物运单路由CNSHK-MYPKG-MYTPP,已离开起运港CNSHK,SHIPMENT ONBOARD DATE为2019/09/05 16:33:17,通过经纬度等信息判断船位置在CNSHK与MYPKG之间,根据,预测目的港口MYTPP的时间,提交的ETA:”2019/09/18 22:28:46”。

ARRIVAL AT PORT实际到达目的港MYTPP为2019/09/18 13:28:46;选手预测为2019/09/18 22:28:46;
目的港MYTPP的hETA1为(2019/09/18 22:28:46) – (2019/09/05 16:33:17) = 317.9925 (单位:小时),hATA1为(2019/09/18 13:28:4) – (2019/09/05 16:33:17) = 308.9925 (单位:小时)。
只算此条路径MSE:


刚开始看到赛题确实给我整晕了,“脱敏”“路由”“识别码”…啥玩意儿?

还好官方给了赛题讲解直播,看完直播再结合赛题,总算让我对题目有了一个大概的认知以及初期处理的思路。

官方给的数据有三个维度,历史运单GPS数据、历史运单事件数据、港口坐标数据,一共二十六个字段,着实看得人眼花缭乱。但是仔细分析测试运单数据之后,瞬间就理清了思绪。测试数据中一共十个字段,分别是
loadingOrder、timestamp、longitude、latitude、speed、direction、carrierName、vesselMMSI、onboardDate、TRANSPORT_TRACE
对比历史运单GPS数据,其中vesselNextportETA、vesselStatus、vesselDatasource,这三个字段是测试运单数据没有的,那么也就意味着这三个字段的数据以及历史运单事件数据、港口坐标数据的作用不是用作特征工程,而是用于清洗数据。
(测试运单数据中的onboardDate字段指:船舶离开起运港时间,官方未给直接给出,后续需要自己从历史运单GPS数据挖掘)

1.用合适的方法从给出的数据里提取需要的数据(数据清洗等)

由于这次比赛的数据量很大,历史运单GPS数据大概有七亿条(记不太清了),脏数据会给预测结果带来很大的偏差,所以清洗数据是重中之重,听说有某大佬只清洗数据使用姜大佬的baseline就打到了三位数tql!
由于近二十个G的历史运单GPS数据都在一个csv文件中,一般的机器是无法一次性读取出来的,我使用的是pandas库的read_csv()函数,通过设置chunksize参数分块读取并处理数据:

为了降低如此庞大的数据所占的内存,在读取数据时对数据进行了字典压缩以及通过改变数据格式来降低内存的操作:

  • 1.以下是通过改变数据格式来降低内存的函数:
#降内存函数
def reduce_mem_usage(props):# 计算当前内存start_mem_usg = props.memory_usage().sum() / 1024 ** 2print("Memory usage of the dataframe is :", start_mem_usg, "MB")# 哪些列包含空值,空值用-999填充。why:因为np.nan当做float处理NAlist = []for col in props.columns:# 这里只过滤了objectd格式,如果你的代码中还包含其他类型,请一并过滤if (props[col].dtypes != object):print("**************************")print("columns: ", col)print("dtype before", props[col].dtype)# 判断是否是int类型isInt = Falsemmax = props[col].max()mmin = props[col].min()# # Integer does not support NA, therefore Na needs to be filled# if not np.isfinite(props[col]).all():#     NAlist.append(col)#     props[col].fillna(-999, inplace=True) # 用-999填充# test if column can be converted to an integerasint = props[col].fillna(0).astype(np.int64)result = np.fabs(props[col] - asint)result = result.sum()if result < 0.01: # 绝对误差和小于0.01认为可以转换的,要根据task修改isInt = True# make interger / unsigned Integer datatypesif isInt:if mmin >= 0: # 最小值大于0,转换成无符号整型if mmax <= 255:props[col] = props[col].astype(np.uint8)elif mmax <= 65535:props[col] = props[col].astype(np.uint16)elif mmax <= 4294967295:props[col] = props[col].astype(np.uint32)else:props[col] = props[col].astype(np.uint64)else: # 转换成有符号整型if mmin > np.iinfo(np.int8).min and mmax < np.iinfo(np.int8).max:props[col] = props[col].astype(np.int8)elif mmin > np.iinfo(np.int16).min and mmax < np.iinfo(np.int16).max:props[col] = props[col].astype(np.int16)elif mmin > np.iinfo(np.int32).min and mmax < np.iinfo(np.int32).max:props[col] = props[col].astype(np.int32)elif mmin > np.iinfo(np.int64).min and mmax < np.iinfo(np.int64).max:props[col] = props[col].astype(np.int64)  else: # 注意:这里对于float都转换成float16,需要根据你的情况自己更改props[col] = props[col].astype(np.float16)print("dtype after", props[col].dtype)print("********************************")print("___MEMORY USAGE AFTER COMPLETION:___")mem_usg = props.memory_usage().sum() / 1024**2 print("Memory usage is: ",mem_usg," MB")print("This is ",100*mem_usg/start_mem_usg,"% of the initial size")return props#, NAlist
  • 2.获取数据字典
import pandas as pd
from tqdm import tqdm
import numpy as npfrom sklearn.metrics import mean_squared_error,explained_variance_score
from sklearn.model_selection import KFold
import lightgbm as lgbimport warnings
warnings.filterwarnings('ignore')
train_gps_path = 'D:/datalab/train0523/train0523.csv'dir_loadingOrder={}
loadingOrder_flux=pd.read_csv(train_gps_path,chunksize=1000000,header=None,usecols=[0])
for data in tqdm(loadingOrder_flux):for i,element in enumerate(data[0].unique()):if(element not in list(dir_loadingOrder.keys())):dir_loadingOrder[element]=len(dir_loadingOrder)+1#保存为csv文件
data_loadingOrder={'key':list(dir_loadingOrder.keys()),'value':list(dir_loadingOrder.values())}
df_loadingOrder=pd.DataFrame(data_loadingOrder)
df_loadingOrder.to_csv('df_loadingOrder.csv', index=False)

对carrierName、vesselMMSI、TRANSPORT_TRACE进行同样操作

  • 接下来开始分三次读取数据:
    以下是获取0-4chunk的数据,对于5-9chunk、10-15chunk以同样方式处理
import pandas as pd
from tqdm import tqdm
import numpy as npfrom sklearn.metrics import mean_squared_error,explained_variance_score
from sklearn.model_selection import KFold
import lightgbm as lgbimport warnings
warnings.filterwarnings('ignore')#获取数据字典
df_loadingOrder=pd.read_csv('./df_loadingOrder.csv')
dict_loadingOrder={}
for key,value in enumerate(df_loadingOrder.key):dict_loadingOrder[value]=keydf_carrierName=pd.read_csv('./df_carrierName.csv')
dict_carrierName={}
for key,value in enumerate(df_carrierName.key):dict_carrierName[value]=keydf_vesselMMSI=pd.read_csv('./df_vesselMMSI.csv')
dict_vesselMMSI={}
for key,value in enumerate(df_vesselMMSI.key):dict_vesselMMSI[value]=keydf_TRANSPORT_TRACE=pd.read_csv('./df_TRANSPORT_TRACE.csv')
dict_TRANSPORT_TRACE={}
for key,value in enumerate(df_TRANSPORT_TRACE.key):dict_TRANSPORT_TRACE[value]=key#字典压缩
def dict_compress(data):data['loadingOrder_factorize']=data.loadingOrder.map(dict_loadingOrder)data['carrierName_factorize']=data.carrierName.map(dict_carrierName)data['vesselMMSI_factorize']=data.vesselMMSI.map(dict_vesselMMSI)data['TRANSPORT_TRACE_factorize']=data.TRANSPORT_TRACE.map(dict_TRANSPORT_TRACE)data.drop(['loadingOrder','carrierName','vesselMMSI','TRANSPORT_TRACE'],axis=1,inplace=True)return datatrain_gps_path = 'D:/datalab/train0523/train0523.csv'
#开始处理数据0-4chunk
train_flux=pd.read_csv(train_gps_path,chunksize=10000000,header=None)
## 注意 train里面的运单不是按顺序放好的,所以要读取完整的loadingOrder数据最好从头到尾扫描一遍
count=0
loadingOrders=[] # 可以用来记录读取了多少个loadingOrders
train_data_raw=pd.DataFrame(columns=['loadingOrder_factorize','carrierName_factorize','timestamp','longitude','latitude','vesselMMSI_factorize','speed','direction','TRANSPORT_TRACE_factorize'])
for data in tqdm(train_flux):if(count<5):data.columns = ['loadingOrder','carrierName','timestamp','longitude','latitude','vesselMMSI','speed','direction','vesselNextport','vesselNextportETA','vesselStatus','vesselDatasource','TRANSPORT_TRACE']data=reduce_mem_usage(data)#转换数据格式data=dict_compress(data)#字典压缩data=data.drop_duplicates(['loadingOrder_factorize','timestamp'])#根据运单号和时间戳去重train_data_raw=train_data_raw.append(data)count=count+1else:breaktrain_data_raw=train_data_raw.drop_duplicates(['loadingOrder_factorize','timestamp'])#再次去重
train_data_raw.info()
0it [00:00, ?it/s]
Memory usage of the dataframe is : 991.8214111328125 MB
**************************
columns:  longitude
dtype before float64
dtype after float16
********************************
**************************
columns:  latitude
dtype before float64
dtype after float16
********************************
**************************
columns:  speed
dtype before int64
dtype after uint8
********************************
**************************
columns:  direction
dtype before int64
dtype after int32
********************************
___MEMORY USAGE AFTER COMPLETION:___
Memory usage is:  772.476318359375  MB
This is  77.88461810650854 % of the initial size
1it [00:24, 24.13s/it]
Memory usage of the dataframe is : 991.8214149475098 MB
**************************
columns:  longitude
dtype before float64
dtype after float16
********************************
**************************
columns:  latitude
dtype before float64
dtype after float16
********************************
**************************
columns:  speed
dtype before int64
dtype after uint8
********************************
**************************
columns:  direction
dtype before int64
dtype after int32
********************************
___MEMORY USAGE AFTER COMPLETION:___
Memory usage is:  772.4763221740723  MB
This is  77.8846181915677 % of the initial size
2it [00:50, 24.89s/it]
Memory usage of the dataframe is : 991.8214149475098 MB
**************************
columns:  longitude
dtype before float64
dtype after float16
********************************
**************************
columns:  latitude
dtype before float64
dtype after float16
********************************
**************************
columns:  speed
dtype before int64
dtype after uint8
********************************
**************************
columns:  direction
dtype before int64
dtype after int32
********************************
___MEMORY USAGE AFTER COMPLETION:___
Memory usage is:  772.4763221740723  MB
This is  77.8846181915677 % of the initial size
3it [01:20, 26.40s/it]
Memory usage of the dataframe is : 991.8214149475098 MB
**************************
columns:  longitude
dtype before float64
dtype after float16
********************************
**************************
columns:  latitude
dtype before float64
dtype after float16
********************************
**************************
columns:  speed
dtype before int64
dtype after uint8
********************************
**************************
columns:  direction
dtype before int64
dtype after int32
********************************
___MEMORY USAGE AFTER COMPLETION:___
Memory usage is:  772.4763221740723  MB
This is  77.8846181915677 % of the initial size
4it [01:59, 30.02s/it]
Memory usage of the dataframe is : 991.8214149475098 MB
**************************
columns:  longitude
dtype before float64
dtype after float16
********************************
**************************
columns:  latitude
dtype before float64
dtype after float16
********************************
**************************
columns:  speed
dtype before int64
dtype after uint8
********************************
**************************
columns:  direction
dtype before int64
dtype after int32
********************************
___MEMORY USAGE AFTER COMPLETION:___
Memory usage is:  772.4763221740723  MB
This is  77.8846181915677 % of the initial size
5it [02:58, 38.85s/it]
<class 'pandas.core.frame.DataFrame'>
Int64Index: 26073281 entries, 0 to 49999999
Data columns (total 13 columns):
TRANSPORT_TRACE_factorize    object
carrierName_factorize        object
direction                    object
latitude                     float16
loadingOrder_factorize       object
longitude                    float16
speed                        object
timestamp                    object
vesselDatasource             object
vesselMMSI_factorize         object
vesselNextport               object
vesselNextportETA            object
vesselStatus                 object
dtypes: float16(2), object(11)
memory usage: 2.4+ GB

保存0-4chunk

train_data_raw.to_csv('D:/datalab/train_propress_bychunk/train_propress_0-4chunk.csv', index=False)

经过上述操作后,20G+的数据被压缩成了8G+,还是很有效果的哈哈
得到的数据中包含以下字段:

'loadingOrder_factorize','carrierName_factorize','timestamp','longitude',
'latitude','vesselMMSI_factorize','speed','direction','vesselNextport','vesselNextportETA','vesselStatus','vesselDatasource','TRANSPORT_TRACE_factorize'

我的清洗数据的处理思路是尽可能保留完整的数据,这样后续如果要用到的话不至于再花时间重新从原始csv文件中读,以上代码大概花了超过二十四小时运行。。

以上是清洗历史运单GPS数据,历史运单事件数据、港口坐标数据是用于清洗历史运单GPS数据中的错误数据,但由于这两个数据文件是人工录入的,所以有很多数据是有误的。所以用之前还得自己纠正,比较耗费时间,当时我就没有考虑这块(据说用了跟没用差不多)

2.根据需要进行特征工程

特征工程这块,当时正式赛开赛第二天好像,姜大佬在群里分享了他做的baseline(十分感谢)。这块可以说我是站在“巨人”的肩膀上进行的哈哈

先进行字典解压缩:

#获取数据字典
df_loadingOrder=pd.read_csv('./df_loadingOrder.csv')
dict_loadingOrder={}
for key,value in enumerate(df_loadingOrder.key):dict_loadingOrder[key]=valuedf_carrierName=pd.read_csv('./df_carrierName.csv')
dict_carrierName={}
for key,value in enumerate(df_carrierName.key):dict_carrierName[key]=valuedf_vesselMMSI=pd.read_csv('./df_vesselMMSI.csv')
dict_vesselMMSI={}
for key,value in enumerate(df_vesselMMSI.key):dict_vesselMMSI[key]=valuedf_TRANSPORT_TRACE=pd.read_csv('./df_TRANSPORT_TRACE.csv')
dict_TRANSPORT_TRACE={}
for key,value in enumerate(df_TRANSPORT_TRACE.key):dict_TRANSPORT_TRACE[key]=value#字典压缩
def dict_discompress(data):data['loadingOrder']=data.loadingOrder_factorize.map(dict_loadingOrder)data['carrierName']=data.carrierName_factorize.map(dict_carrierName)data['vesselMMSI']=data.vesselMMSI_factorize.map(dict_vesselMMSI)data['TRANSPORT_TRACE']=data.TRANSPORT_TRACE_factorize.map(dict_TRANSPORT_TRACE)data.drop(['loadingOrder_factorize','carrierName_factorize','vesselMMSI_factorize','TRANSPORT_TRACE_factorize'],axis=1,inplace=True)return data#train数据集字典解压缩
train_data=dict_discompress(train_data)
  1. loadingOrder

loadingOrder订单编号,官方给出的解释是类似于快递单号,是字母与数字的组合

我的猜想是不同的字母指的是不同的海运公司或是不一样的航线(网上也没有明确的解释),数字的部分应该是仅作区别,字母部分可以作为一个特征。但是这样做的效果不好,原因是测试集的数据里的loadingOrder字母部分与训练集极少重叠,所以这个想法只能作罢。
最终我选择在feature数组中删除loadingOrder。

  1. carrierName_factorize

carrierName,承运商名称,类似快递公司名称。同loadingOrder,这种方法只有在测试集字段的unique值是训练集的字段的unique值的子集时才会有很好效果,但是此题不符合这个要求。

  1. vesselMMSI

vesselMMSI,唯一标识每一艘船,同上,删除。

  1. TRANSPORT_TRACE

TRANSPORT_TRACE,船的路由,由“-”连接组成,例如CNSHK-MYPKG-MYTPP。
相当于是船的路线图,很容易联想到经过的地方越多,路由越长,那么一般来讲船的到达时间会越长。但是很可惜,测试数据中的TRANSPORT_TRACE字段只保留了重点和起点。
无法提取特征,那么还可以用来清洗数据。
这块我的思路是只在训练集中选择TRANSPORT_TRACE字段值包含在测试集的TRANSPORT_TRACE中的记录,在正式赛阶段二中实现了

  1. timestamp

这道题并没有直接给出label需要自己计算。
我使用的是姜大佬的baseline中确定label的方法,对训练集的loadingOrder进行分组,然后根据timestamp进行排序,再用最后一条记录的timestamp减去第一条记录的timestamp,从而得到label。当然此方法是有缺陷的,因为无法排除塞港等等其他的影响,所以后续我有做一些改进,在正式赛篇阶段二中补充。

  1. latitude/longitude/direction/speed

latitude:纬度坐标
longitude:经度坐标
direction:当前船舶的行驶方向,正北是0度,31480代表西北方向314.80度,900代表正北偏东9度
speed:单位km/h,货物在运输过程中,当前船舶的瞬时速度
这块我的处理是把同一订单的经纬度、速度、方向的min,max,mean, median作为特征

def get_feature(df):group_df = df.groupby('loadingOrder_factorize')['timestamp'].agg(mmax='max', count='count', mmin='min').reset_index()# 读取数据的最大值-最小值,即确认时间间隔为labelgroup_df['label'] = (group_df['mmax'] - group_df['mmin']).dt.total_seconds()agg_function = ['min', 'max', 'mean', 'median']agg_col = ['latitude', 'longitude', 'speed', 'direction']group = df.groupby('loadingOrder_factorize')[agg_col].agg(agg_function).reset_index()#根据loadingOrder对agg_col分组,应用agg_function聚合group.columns = ['loadingOrder_factorize'] + ['{}_{}'.format(i, j) for i in agg_col for j in agg_function]#生成新列,共十七列temp=df[['loadingOrder_factorize','TRANSPORT_TRACE_factorize','carrierName_factorize','vesselMMSI_factorize']]group_df=group_df.merge(temp,on='loadingOrder_factorize',how='left')group_df = group_df.merge(group, on='loadingOrder_factorize', how='left')#连接表group_df.drop_duplicates(inplace=True)#去重return group_df

或许可以试试用latitude_max-latitude_min得到一个船只航行路线所跨的经度值,这样或许可以更好的描述航行路线,longitude也是一样(为什么不早点想到o(╥﹏╥)o)

  1. vesselNextportETA/vesselNextport

vesselNextportETA:船运公司给出的到“下一个港口”预计到达时间
vesselNextport:船舶将要到达的下一港口
这两个字段测试数据并没有包含,所以无法作为特征,作为清洗数据的依据我也没有想出什么可行的方法,能力有限,就先删除吧,嘻嘻

  1. vesselStatus

vesselStatus,当前船舶航行状态,主要包括:moored(系泊:靠于码头、浮筒或他船),under way using engine(动力机在航),not under command(不受指挥/控制),at anchor(抛锚:指投锚于水中使船泊定),under way sailing(帆船在航 ),constrained by her draught(受吃水的限制)

很明显这个字段的数据是用于清洗数据的 ,我尝试过删除包含moored、not under command、at anchor的记录,但是效果不明显,与别人交流之后得知大家似乎都对这个字段束手无策,没有什么好的方法利用起来,最终我也是选择了删除。

  1. vesselDatasource

vesselDatasource,船舶数据来源(岸基/卫星):Coastal AIS,Satellite
这块我的想法是可以删除数据不准确的记录,但是网上找不到岸基和卫星数据准确性比较的资料,所以这个字段我选择从feature数组中删除。

  1. disease

考虑到这次新冠疫情对海运的影响,我增加了一个disease特征,数据类型为category。在网上查阅了资料,我选定二月一号为时间分割点,二月一号以前的disease置0,二月一号以后的disease置1

#增加disease特征
def get_d_test(x):timeindex=pd.to_datetime('2019-02-01 00:00:00', infer_datetime_format=True)if x<timeindex:return 0else:return 1
train_data['mmin']=pd.to_datetime(train_data['mmin'], infer_datetime_format=True)
train_data['disease']=train_data.apply(lambda row:get_d_test(row['mmin']),axis=1)temp=test_data[['loadingOrder','onboardDate']]
temp.drop_duplicates(subset=['loadingOrder'],inplace=True)
test=test.merge(temp,on='loadingOrder', how='left')
test['disease']=test.apply(lambda row:get_d_test(row['onboardDate']),axis=1)train['disease']=train['disease'].astype('category')
test['disease']=test['disease'].astype('category')

当然二月一号这个时间肯定是不准确的,后续可以尝试不同的时间分界线

3.模型融合

这块大概分三步走:

  • 1.特征筛选
    贴一下特征筛选的函数吧(参考大树先生的博客):
def get_top_n_features(train_data_X, train_data_Y, top_n_features):
#训练集特征:train_data_X,训练集label:train_data_Y,限定特征个数:top_n_features# random forestrf_est = RandomForestClassifier(random_state=0)rf_param_grid = {'n_estimators': [500], 'min_samples_split': [2, 3], 'max_depth': [20]}rf_grid = model_selection.GridSearchCV(rf_est, rf_param_grid, n_jobs=25, cv=10, verbose=1)rf_grid.fit(train_data_X, train_data_Y)print('Top N Features Best RF Params:' + str(rf_grid.best_params_))print('Top N Features Best RF Score:' + str(rf_grid.best_score_))print('Top N Features RF Train Score:' + str(rf_grid.score(train_data_X, train_data_Y)))feature_imp_sorted_rf = pd.DataFrame({'feature': list(train_data_X),'importance': rf_grid.best_estimator_.feature_importances_}).sort_values('importance', ascending=False)features_top_n_rf = feature_imp_sorted_rf.head(top_n_features)['feature']print('Sample 10 Features from RF Classifier')print(str(features_top_n_rf[:10]))# AdaBoostada_est =AdaBoostClassifier(random_state=0)ada_param_grid = {'n_estimators': [500], 'learning_rate': [0.01, 0.1]}ada_grid = model_selection.GridSearchCV(ada_est, ada_param_grid, n_jobs=25, cv=10, verbose=1)ada_grid.fit(train_data_X, train_data_Y)print('Top N Features Best Ada Params:' + str(ada_grid.best_params_))print('Top N Features Best Ada Score:' + str(ada_grid.best_score_))print('Top N Features Ada Train Score:' + str(ada_grid.score(train_data_X, train_data_Y)))feature_imp_sorted_ada = pd.DataFrame({'feature': list(train_data_X),'importance': ada_grid.best_estimator_.feature_importances_}).sort_values('importance', ascending=False)features_top_n_ada = feature_imp_sorted_ada.head(top_n_features)['feature']print('Sample 10 Feature from Ada Classifier:')print(str(features_top_n_ada[:10]))# ExtraTreeet_est = ExtraTreesClassifier(random_state=0)et_param_grid = {'n_estimators': [500], 'min_samples_split': [3, 4], 'max_depth': [20]}et_grid = model_selection.GridSearchCV(et_est, et_param_grid, n_jobs=25, cv=10, verbose=1)et_grid.fit(train_data_X, train_data_Y)print('Top N Features Best ET Params:' + str(et_grid.best_params_))print('Top N Features Best ET Score:' + str(et_grid.best_score_))print('Top N Features ET Train Score:' + str(et_grid.score(train_data_X, train_data_Y)))feature_imp_sorted_et = pd.DataFrame({'feature': list(train_data_X),'importance': et_grid.best_estimator_.feature_importances_}).sort_values('importance', ascending=False)features_top_n_et = feature_imp_sorted_et.head(top_n_features)['feature']print('Sample 10 Features from ET Classifier:')print(str(features_top_n_et[:10]))# GradientBoostinggb_est =GradientBoostingClassifier(random_state=0)gb_param_grid = {'n_estimators': [500], 'learning_rate': [0.01, 0.1], 'max_depth': [20]}gb_grid = model_selection.GridSearchCV(gb_est, gb_param_grid, n_jobs=25, cv=10, verbose=1)gb_grid.fit(train_data_X, train_data_Y)print('Top N Features Best GB Params:' + str(gb_grid.best_params_))print('Top N Features Best GB Score:' + str(gb_grid.best_score_))print('Top N Features GB Train Score:' + str(gb_grid.score(train_data_X, train_data_Y)))feature_imp_sorted_gb = pd.DataFrame({'feature': list(train_data_X),'importance': gb_grid.best_estimator_.feature_importances_}).sort_values('importance', ascending=False)features_top_n_gb = feature_imp_sorted_gb.head(top_n_features)['feature']print('Sample 10 Feature from GB Classifier:')print(str(features_top_n_gb[:10]))# DecisionTreedt_est = DecisionTreeClassifier(random_state=0)dt_param_grid = {'min_samples_split': [2, 4], 'max_depth': [20]}dt_grid = model_selection.GridSearchCV(dt_est, dt_param_grid, n_jobs=25, cv=10, verbose=1)dt_grid.fit(train_data_X, train_data_Y)print('Top N Features Best DT Params:' + str(dt_grid.best_params_))print('Top N Features Best DT Score:' + str(dt_grid.best_score_))print('Top N Features DT Train Score:' + str(dt_grid.score(train_data_X, train_data_Y)))feature_imp_sorted_dt = pd.DataFrame({'feature': list(train_data_X),'importance': dt_grid.best_estimator_.feature_importances_}).sort_values('importance', ascending=False)features_top_n_dt = feature_imp_sorted_dt.head(top_n_features)['feature']print('Sample 10 Features from DT Classifier:')print(str(features_top_n_dt[:10]))# merge the three modelsfeatures_top_n = pd.concat([features_top_n_rf, features_top_n_ada, features_top_n_et, features_top_n_gb, features_top_n_dt], ignore_index=True).drop_duplicates()features_importance = pd.concat([feature_imp_sorted_rf, feature_imp_sorted_ada, feature_imp_sorted_et, feature_imp_sorted_gb, feature_imp_sorted_dt],ignore_index=True)return features_top_n , features_importance
  • 2.依据筛选出的特征构建训练集和测试集
feature_to_pick = 15#根据需要调整
train_data_X=train[features]#feature为前面特征工程处理好的特征数组
train_data_Y=train['label']
feature_top_n, feature_importance = get_top_n_features(train_data_X, train_data_Y, feature_to_pick)
train_data_X=train[feature_top_n]
test_data_X=test[feature_top_n]
train_data_Y=train['label']
  • 3.模型融合
    转自大树先生-Kaggle_Titanic生存预测 – 详细流程吐血梳理
    常见的模型融合方法有:Bagging、Boosting、Stacking、Blending。
    (3-1):Bagging
    Bagging 将多个模型,也就是多个基学习器的预测结果进行简单的加权平均或者投票。它的好处是可以并行地训练基学习器。Random Forest就用到了Bagging的思想。
    (3-2): Boosting
    Boosting 的思想有点像知错能改,每个基学习器是在上一个基学习器学习的基础上,对上一个基学习器的错误进行弥补。我们将会用到的 AdaBoost,Gradient Boost 就用到了这种思想。
    (3-3): Stacking
    Stacking是用新的次学习器去学习如何组合上一层的基学习器。如果把 Bagging 看作是多个基分类器的线性组合,那么Stacking就是多个基分类器的非线性组合。Stacking可以将学习器一层一层地堆砌起来,形成一个网状的结构。
    相比来说Stacking的融合框架相对前面的二者来说在精度上确实有一定的提升,所以在下面的模型融合上,我们也使用Stacking方法。
    (3-4): Blending
    Blending 和 Stacking 很相似,但同时它可以防止信息泄露的问题。

Stacking框架融合:
热身赛我使用了两层的模型融合,Level 1使用了:RandomForestRegressor、AdaBoostRegressor、GradientBoostingRegressor、DecisionTreeRegressor、DecisionTree、SVM ,一共7个模型,Level 2使用了XGBoost使用第一层预测的结果作为特征对最终的结果进行预测。

Level 1:
Stacking框架是堆叠使用基础分类器的预测作为对二级模型的训练的输入。 然而,不能简单地在全部训练数据上训练基本模型,产生预测,输出用于第二层的训练。
如果在Train Data上训练,然后在Train Data上预测,就会造成标签。为了避免标签,需要对每个基学习器使用K-fold,将K个模型对Valid Set的预测结果拼起来,作为下一层学习器的输入。

这里是建立输出fold预测方法:

from sklearn.model_selection import KFold# Some useful parameters which will come in handy later on
ntrain = train_data_X.shape[0]
ntest = text_data_X.shape[0]
SEED = 0 # for reproducibility
NFOLDS = 7 # set folds for out-of-fold prediction
kf = KFold(n_splits = NFOLDS, random_state=SEED, shuffle=False)
#n_splits:分成几份,shuffle:在每次划分时,是否洗牌def get_out_fold(clf, x_train, y_train, x_test):oof_train = np.zeros((ntrain,))#np.zeros返回来一个给定形状和类型的用0填充的数组oof_test = np.zeros((ntest,))oof_test_skf = np.empty((NFOLDS, ntest))#np.empty依据给定形状和类型(shape,[dtype, order])返回一个新的随机数数组for i, (train_index, test_index) in enumerate(kf.split(x_train)):#https://www.runoob.com/python/python-func-enumerate.htmlx_tr = x_train[train_index]y_tr = y_train[train_index]x_te = x_train[test_index]clf.fit(x_tr, y_tr)oof_train[test_index] = clf.predict(x_te)oof_test_skf[i, :] = clf.predict(x_test)oof_test[:] = oof_test_skf.mean(axis=0)return oof_train.reshape(-1, 1), oof_test.reshape(-1, 1)#数据变成了一列

构建不同的基学习器:

from sklearn import ensemble
from sklearn import model_selection
from sklearn.ensemble import RandomForestRegressor
from sklearn.ensemble import AdaBoostRegressor
from sklearn.ensemble import GradientBoostingRegressor
from sklearn.tree import DecisionTreeRegressor
from sklearn.ensemble import ExtraTreesRegressor
rf = RandomForestRegressor(n_estimators=500, warm_start=True, max_features='sqrt',max_depth=6, min_samples_split=3, min_samples_leaf=2, n_jobs=-1, verbose=0)
ada = AdaBoostRegressor(n_estimators=500, learning_rate=0.1)
gb = GradientBoostingRegressor(n_estimators=500, learning_rate=0.008, min_samples_split=3, min_samples_leaf=2, max_depth=5, verbose=0)
dt = DecisionTreeRegressor(max_depth=8)

将pandas转换为arrays:

# Create Numpy arrays of train, test and target (Survived) dataframes to feed into our models
x_train = train_data_X.values # Creates an array of the train data
x_test = test_data_X.values # Creats an array of the test data
y_train = train_data_Y.values

使用KFOLD:

rf_oof_test = get_out_fold(rf, x_train, y_train, x_test) # Random Forest
ada_oof_test = get_out_fold(ada, x_train, y_train, x_test) # AdaBoost
gb_oof_test = get_out_fold(gb, x_train, y_train, x_test) # Gradient Boost
dt_oof_test = get_out_fold(dt, x_train, y_train, x_test) # Decision Tree

得到训练集和测试集:

x_train = np.concatenate((rf_oof_train, ada_oof_train, gb_oof_train, dt_oof_train), axis=1)
x_test = np.concatenate((rf_oof_train, ada_oof_train, gb_oof_train, dt_oof_train), axis=1)
y_train=train_data_Y

模型融合的方式有很多种,我还尝试了用五个算法(RandomForestRegressor、AdaBoostRegressor、GradientBoostingRegressor、DecisionTreeRegressor、ExtraTreesRegressor)预测的label的均值做为result,不过这个方法效果不太好哈哈

4.划分训练集和验证集,得到预测结果

def mse_score_eval(preds, valid):labels = valid.get_label()scores = mean_squared_error(y_true=labels, y_pred=preds)return 'mse_score', scores, Truetrain_pred = np.zeros((x_train.shape[0], ))
test_pred = np.zeros((x_test.shape[0], ))
n_splits = 5
# Kfold
fold = KFold(n_splits=n_splits, shuffle=True, random_state=1080)
kf_way = fold.split(x_train)#pred为除去['loadingOrder', 'label', 'mmin', 'mmax', 'count']的特征
# params
params = {'learning_rate': 0.01,'boosting_type': 'gbdt','objective': 'regression','num_leaves': 36,'feature_fraction': 0.6,'bagging_fraction': 0.7,'bagging_freq': 6,'seed': 8,'bagging_seed': 1,'feature_fraction_seed': 7,'min_data_in_leaf': 20,'nthread': 8,'verbose': 1,
}
# train
for n_fold, (train_idx, valid_idx) in enumerate(kf_way, start=1):#kf_way:返回样本切分之后训练集和验证集的index,即索引train_x, train_y = x_train[train_idx], train['label'].iloc[train_idx]#生成训练集_X,训练集_Yvalid_x, valid_y = x_train[valid_idx], train['label'].iloc[valid_idx]#生成验证集_X,验证集_Y# 数据加载n_train = lgb.Dataset(train_x, label=train_y)n_valid = lgb.Dataset(valid_x, label=valid_y)clf = lgb.train(params=params,train_set=n_train,#训练num_boost_round=3000,valid_sets=[n_valid],early_stopping_rounds=100,verbose_eval=100,feval=mse_score_eval)train_pred[valid_idx] = clf.predict(valid_x, num_iteration=clf.best_iteration)#交叉验证之后得到的训练集的labeltest_pred += clf.predict(x_test, num_iteration=clf.best_iteration)/fold.n_splits#每一次迭代(共十次)由n_train训练的clf对test_pred预测得到的label/10,并累加test['label'] = test_predresult = test[['loadingOrder', 'label']]
Training until validation scores don't improve for 100 rounds
[100]   valid_0's l2: 2.04768e+11 valid_0's mse_score: 2.04768e+11
Early stopping, best iteration is:
[1] valid_0's l2: 8.22707e+11 valid_0's mse_score: 8.22707e+11
Training until validation scores don't improve for 100 rounds
[100]   valid_0's l2: 4.60331e+11 valid_0's mse_score: 4.60331e+11
Early stopping, best iteration is:
[1] valid_0's l2: 1.27532e+12 valid_0's mse_score: 1.27532e+12
Training until validation scores don't improve for 100 rounds
[100]   valid_0's l2: 2.72004e+11 valid_0's mse_score: 2.72004e+11
Early stopping, best iteration is:
[1] valid_0's l2: 8.924e+11   valid_0's mse_score: 8.924e+11
Training until validation scores don't improve for 100 rounds
[100]   valid_0's l2: 5.88297e+11 valid_0's mse_score: 5.88297e+11
Early stopping, best iteration is:
[1] valid_0's l2: 1.49435e+12 valid_0's mse_score: 1.49435e+12
Training until validation scores don't improve for 100 rounds
[100]   valid_0's l2: 3.07061e+11 valid_0's mse_score: 3.07061e+11
Early stopping, best iteration is:
[1] valid_0's l2: 9.27033e+11 valid_0's mse_score: 9.27033e+11

再一次使用KFOLD,这样就得到了最终的预测结果,最后按照比赛要求的格式生成result文件就好咯

test_data = test_data.merge(result, on='loadingOrder', how='left')
test_data['ETA'] = (test_data['onboardDate'] + test_data['label'].apply(lambda x:pd.Timedelta(seconds=x))).apply(lambda x:x.strftime('%Y/%m/%d  %H:%M:%S'))
test_data.drop(['direction','TRANSPORT_TRACE'],axis=1,inplace=True)
test_data['onboardDate'] = test_data['onboardDate'].apply(lambda x:x.strftime('%Y/%m/%d  %H:%M:%S'))
test_data['creatDate'] = pd.datetime.now().strftime('%Y/%m/%d  %H:%M:%S')
test_data['timestamp'] = test_data['temp_timestamp']
# 整理columns顺序
result = test_data[['loadingOrder', 'timestamp', 'longitude', 'latitude', 'carrierName', 'vesselMMSI', 'onboardDate', 'ETA', 'creatDate']]

保存为.csv文件

result.to_csv('result.csv', index=False)

这是我在初赛A阶段的排名
**
**
B阶段因为一些个人原因没有打,也就直接失去了进入复赛的机会了

整理一下第一次参加华为大数据挑战赛自己的一些收获吧(正式赛篇阶段一)相关推荐

  1. 2021中国高校大数据挑战赛A题复盘+解题思路

    引言 由于个人安排的原因,没有时间参加微信大数据挑战赛,倒是参加了2021年中国高校大数据挑战赛.这次比赛做的是中国电信提供数据集的A题,是一个异常检测的题目,一个人做的本科组二等奖,觉得还是不错的. ...

  2. 2020中国高校计算机大赛·华为云大数据挑战赛热身赛 之智慧交通预测挑战赛冠军团队师傅被妖怪抓走了-建模思路学习整理

    2020中国高校计算机大赛·华为云大数据挑战赛热身赛 之智慧交通预测挑战赛冠军团队师傅被妖怪抓走了-建模思路学习整理 创新点: 首次将Graph Convolution针对交通路况预测问题进行改进,使 ...

  3. 2020中国高校计算机大赛——华为云大数据挑战赛比赛总结

    比赛背景 2016年,教育部高等学校计算机类专业教学指导委员会.教育部高等学校软件工程专业教学指导委员会.教育部高等学校大学计算机课程教学指导委员会.全国高等学校计算机教育研究会联合创办了" ...

  4. 快手大数据挑战赛---总结

    2018中国高校计算机大数据挑战赛-快手活跃用户预测 非常高兴参加了这次大数据比赛,严格来说是第一次参加机器学习的比赛,学到了许多,最后进入了复赛拿到了50/1392(top5%),止步决赛,获得了快 ...

  5. 2016中国高校计算机大赛——大数据挑战赛极客奖:COM团队

    摘要:2016中国高校计算机大赛--大数据挑战赛是由教育部和全国高等学校计算机教育研究会联合主办,清华大学和阿里云联合承办,在"天池大数据众智平台"上开展的高端算法竞赛.本次赛题是 ...

  6. 2020中国高校计算机大赛·华为云大数据挑战赛-数据分析(一)

    2020中国高校计算机大赛·华为云大数据挑战赛–数据分析(一) 正式赛已经开始几天了,但这几天有很多事要忙,所以每什么时间来做比赛,昨天把数据下下来,结合论坛里某个小伙伴的baseline简单分析了下 ...

  7. 重磅开启!2020中国高校计算机大赛——华为云大数据挑战赛

    2016年,教育部高等学校计算机类专业教学指导委员会.教育部高等学校软件工程专业教学指导委员会.教育部高等学校大学计算机课程教学指导委员会.全国高等学校计算机教育研究会联合创办了"中国高校计 ...

  8. 【大数据竞赛】2022MathorCup大数据挑战赛 B题 北京移动用户体验影响因素研究 题目分析

    系列文章目录 第一章 [大数据竞赛]2022MathorCup大数据竞赛 B题 北京移动用户体验影响因素研究 题目分析 第二章[大数据竞赛]2022MathorCup大数据挑战赛 B题 北京移动用户体 ...

  9. 2020中国高校计算机大赛·华为云大数据挑战赛--热身赛 Rank7 思路及代码分享

    队名:无能万金油 2020中国高校计算机大赛·华为云大数据挑战赛–热身赛 热身赛:Rank 7 CSDN博客:我的博客 数据相关知识和分析参考:[上分指南] 2020华为云大数据挑战赛热身赛如何&qu ...

  10. 华为大数据平台凭什么成为行业领跑者?

    每一次研究机构的调研报告总是能爆出大新闻.这不,在最近一期IDC MarketScape的中国大数据管理平台厂商评估中,将华为FusionInsight评为领导者象限第一名.这次评奖,简直是对中国大数 ...

最新文章

  1. http://demo.netfoucs.com/jianglonghuang/article/details/44888133
  2. 前端新手学习记录2 -使用vscode编写个人网站首页
  3. linux上寻找并杀死僵尸进程
  4. 电脑格式化后需要重装系统吗_电脑经常重装系统对电脑有影响吗
  5. 分页offset格式_Thinkphp5 原生sql分页操作
  6. How to use price determination in Quotation scenario
  7. 操作系统:Linux环境变量相关知识总结
  8. 基于Keras的卷积神经网络(CNN)可视化
  9. 服务器root账号用户名和密码忘记了,宝塔忘记后台管理员账号密码怎么办?教你用这条命令轻松搞定...
  10. 区分错误类型_形象解释 Python 新手最容易犯的错误
  11. 安卓手机如何防盗_iphone手机换成安卓手机后如何转移便签备忘录数据?
  12. HDU 5857 - Median ( 查询模拟 + 思路 )
  13. 挖一挖那些让公司网站瘫痪的SQL“终结者”
  14. 鼎力加密狗驱动程序_怎么安装加密狗驱动程序
  15. android 信鸽推送平台,信鸽推送平台Android常见相关问题
  16. Swing游戏开发——飞机大战
  17. 流程图制作之Giffy Diagrams
  18. linux复制操作 cp: -r not specified; omitting directory XXX 错误
  19. 技术至简-2:分集与交织
  20. 提高晚上学习效率的10个方法

热门文章

  1. python tts 离线 linux_ubuntu16.04安装科大讯飞Linux SDK实现离线语音合成(TTS)
  2. 瑞星杀毒软件网络版各版本功能差异
  3. Windows清理助手ARSWP
  4. 高等数学|微积分(上)知识点总结
  5. FPS游戏方框透视基本原理
  6. 我的ROS2开发环境
  7. JavaScript混淆器
  8. 下载并安装 J2SDK以及运行第一个java程序
  9. 神经元模型和BP网络
  10. oracle汉字转换成拼音首字母和五笔首字母