2021年MathorCup高校数学建模挑战赛——大数据竞赛赛道A -思路分享

Posted 开始King

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2021年MathorCup高校数学建模挑战赛——大数据竞赛赛道A -思路分享相关的知识,希望对你有一定的参考价值。

4.8号公布了复赛获奖名单,比赛正式告一段落,为什么现在才开始写呢?其实一是最近一直很忙,二是感觉自己做的不咋地,趁今天有空就写写吧,时间一长就又不想写了。
好了胡扯到此结束,言归正传,这次比赛题目和数据下载
链接:https://pan.baidu.com/s/1RsQkTcERxgmHisMEGt62vA
提取码:60t9

初赛

1 读入数据和数据清洗

导入需要的包

import re
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestRegressor
from sklearn.preprocessing import StandardScaler
from category_encoders.leave_one_out import LeaveOneOutEncoder
from sklearn.model_selection import cross_val_score
from sklearn.decomposition import PCA
import xgboost as xgb

1 给变量命名
首先读入数据,官方给的数据是没有列名的,为了方便后续的数据处理,首先读入数据,并给数据加上列名,注意到1,11,12列数据是时间类型的,直接在读入时设置parse_dates参数传化为时间类型

df = pd.read_table(r'D:\\比赛\\2021年MathorCup大数据竞赛赛道A\\附件\\附件1:估价训练数据.txt',
                    parse_dates=[1, 11, 12],sep='\\t', header=None, encoding='gbk')
# df = pd.read_table('../data/附件1:估价训练数据.txt', sep='\\t', header=None, encoding='gbk')
data = pd.DataFrame(data=df)
columns = ['carid', 'tradeTime', 'brand', 'serial', 'model', 'mileage', 'color', 'cityId', 'carCode',
           'transferCount', 'seatings', 'registerDate', 'licenseDate', 'country', 'maketype', 'modelyear',
           'displacement', 'gearbox', 'oiltype', 'newprice']
for i in range(1, 16):
    str_ = 'anonymousFeature'+str(i)
    columns.append(str_)
columns.append('price')
data.columns = columns

2 查看数据的类型和缺失情况

data.info()
data.isnull().sum()

3.
前五列’carid’, ‘tradeTime’, ‘brand’, ‘serial’, 'model’都是id和时间,不能做中心化操作,而且通过data.iloc[:, :5].isnull().sum()

发现没有缺失值,因此不需要处理。
其他列缺失值的情况

可以看出除了15个匿名特征外,carCode有9个缺失值,country有3757个缺失值maketype有3641个缺失值,modelyear有312个缺失值。gearbox有一个缺失值。

对于缺失值较少的列,如carCode和gearbox直接进行删除操作。country ,maketype 和modelyear由于缺失值较多,根据数据类型,定类数据选择使用众数填充比较适合。


nn_i = ['country', 'maketype', 'modelyear']
for i in nn:
    x = int(data[i].mode())
    data[i].fillna(x, inplace=True)
    #data[i]=data[i].dropna()

data_1[‘country’].unique() 可以发现

这个0导致数据量纲较大,会对后期模型的性能产生影响,因此结合所给的信息将其改为779410

data.loc[data['country']==0,'country'] = 779410

4.对15个匿名变量的分析
上图圈出的特征由于数据缺失严重直接删除这些特征

data=data.drop(['anonymousFeature4','anonymousFeature7','anonymousFeature10','anonymousFeature15'
             ,'anonymousFeature1','anonymousFeature8','anonymousFeature9'],axis=1)

2特征构造

4.1对anonymousFeature11
根据显示的信息猜测此特征是某种物品的个数,预测应该为字符串中所有数字之和,使用正则匹配提取所有数值并求和作为新的anonymousFeature11。

def deal_11(x):
    return sum([float(x) for x in re.findall("\\d",x)])
data_1['anonymousFeature11']=data_1['anonymousFeature11'].map(deal_11)

4.2对anonymousFeature12
根据其值的表现形式估计其为汽车的体积,所以其值应为三个数的乘积,使用*切分数据并求出乘积做为新的anonymousFeature12,并用切分得到的三个数据列构建三个新的特征anonymousFeature12_length,anonymousFeature12_width,anonymousFeature12_height。

# 处理匿名特征12
def deal_12(x):
    li=[float(x) for x in re.findall("\\d+",x)]
    return li[0]*li[1]*li[2]
data_1['anonymousFeature12_length']=data_1['anonymousFeature12'].apply(lambda x:int(x.split('*')[0]))
data_1['anonymousFeature12_width']=data_1['anonymousFeature12'].apply(lambda x:int(x.split('*')[1]))
data_1['anonymousFeature12_height']=data_1['anonymousFeature12'].apply(lambda x:int(x.split('*')[2]))
data_1['anonymousFeature12']=data_1['anonymousFeature12'].map(deal_12)

4.3对anonymousFeature13
把anonymousFeature13转换成字符串进行切片,取前四位为anonymousFeature13_year,其他为anonymousFeature13_month。

# 处理匿名特征13
def deal_13(x):
    return x[:4], x[4:6]
data_1['anonymousFeature13']=data_1['anonymousFeature13'].astype('string')
data_1['anonymousFeature13_year']=data_1['anonymousFeature13'].map(deal_13)    #  (2017, 09)
data_1['anonymousFeature13_month']=data_1['anonymousFeature13_year'].apply(lambda x: int(x[1]))
data_1['anonymousFeature13_year']=data_1['anonymousFeature13_year'].apply(lambda x: int(x[0]))
data_1['anonymousFeature13']=data_1['anonymousFeature13'].astype('float')

经过以上处理,数据已经全部变成数值型特征和时间类型的特征

(5)处理定类特征
对color,carCode,country,modelyear这些类别行的特征,因为是定类数据,数据之间本来应该是没有相对大小的,但是转为数值后比如1,2,3会有相对大小,会对树模型产生不好的影响。因此本组选择使用Frequency编码对定类数据进行转换,Frequency编码通过计算特征变量中每个值的出现次数来表示该特征的信息。

# 处理定类数据(Frequency编码)
data['color'] = data['color'].map(data['color'].value_counts())
data['carCode'] = data['carCode'].map(data['carCode'].value_counts())
# data['country'] = data['country'].map(data['country'].value_counts())
data['modelyear'] = data['modelyear'].map(data['modelyear'].value_counts())

(6)再用时间类型的特征构建新特征
①基础周期特征的拆除(年月日特征拆解)

data['tradeTime_year']=data['tradeTime'].dt.year
data['tradeTime_month']=data['tradeTime'].dt.month
data['tradeTime_day']=data['tradeTime'].dt.day
data['registerDate_year']=data['registerDate'].dt.year
data['registerDate_month']=data['registerDate'].dt.month
data['registerDate_day']=data['registerDate'].dt.day

②时间差
‘old_year’=‘tradeTime’-‘registerDate’ (汽车的使用时间)
‘old_year_1’=‘tradeTime’-‘licenseDate’

# 构建新特征
data_1['old_year']=data_1['tradeTime']-data_1['registerDate']
data_1['old_year']=data_1['old_year'].apply(lambda x:str(x).split(' ')[0])
data_1['old_year']=data_1['old_year'].astype(int)

data_1['old_year_1']=data_1['tradeTime']-data_1['licenseDate']
data_1['old_year_1']=data_1['old_year_1'].apply(lambda x:str(x).split(' ')[0])
data_1['old_year_1']=data_1['old_year_1'].astype(int)

经过以上处理后,特征以全部转换成数据值

(7)数值特征的处理
数据分桶,对mileage特征进行数据分桶,mileage的均值为7.14,其余统计量如下

因此分组区间为 (0,1],(1,4],(4,7.15],(7.15,10],(10,50]

# 数据分桶
bin=[0, 1, 4, 7.15, 10, 50]
data_1['mileage_bin']=pd.cut(data_1['mileage'],bins=bin,labels=False)


(8)清除数据中的异常值
首先查看数据的分布,找出异常值
data_2.describe()

可以明显的看出,price有异常值,接下来用箱线图检测异常值,并构造函数处理异常值

# 看看有没有异常值
data_2.describe()
data_2=data_2[data_2['price']<80]

3特征选择

(1)从这些特征中选择出最合适的特征(特征选择),为模型构建做准备。

皮尔逊相关系数的特征选择

# 基于皮尔逊相关系数
pearson = data_2.corr()
index = pearson['price'][:-1].abs() > 0.1
X = data_2.iloc[:,:-1]
X_subset = X.loc[:, index]
# X_subset.columns

3模型训练预测

构建评估函数

def estimate(y_true=None,y_pred=None):
    y_true=np.array(list(y_true))
    y_pred=np.array(list(y_pred))
    Ape=np.abs(y_pred-y_true)/y_true
    Mape=sum(Ape)/len(y_true)
    # Ape_count=[np.nan if x <=0.05 else x for x in Ape]
    Ape_count=len(np.where(Ape<=0.05)[0])/len(Ape)
    return 0.2*(1-Mape)+0.8*Ape_count

训练预测

X_train, X_test, y_train, y_test = train_test_split(X_subset,data_2['price'].to_numpy() , test_size=0.2,random_state=3)

random_model = RandomForestRegressor(n_estimators=500,random_state=33,n_jobs=-1)
random_model.fit(X_train,y_train)
y_pred = random_model.predict(X_test)
score = estimate(y_true=y_test,y_pred=y_pred)

以上基本就是初赛的模型构造模型过程,对附件二的预测已经任务二的模型等其他过程这里没有详细描述,完整的代码在附录获取。下面是复赛的模型

复赛

数据分析

导入包,并构造训练集和测试集

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import re
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import mean_absolute_error
from sklearn.feature_selection import SelectKBest
import xgboost as xgb
import lightgbm as lgb
import warnings
warnings.filterwarnings('ignore')

"""
评估数据集的构造
"""
df_5 = pd.read_csv('./data/附件5:门店交易验证数据.txt', sep='\\t', parse_dates=[1], header=None, encoding='utf-8')   # 附件五
df_2 = pd.read_csv('../data/附件2:估价验证数据.txt', sep='\\t', parse_dates=[1, 11, 12], header=None, encoding='utf-8')   # 附件二的信息

columns = ['carid', 'tradeTime', 'brand', 'serial', 'model', 'mileage', 'color', 'cityId', 'carCode',
           'transferCount', 'seatings', 'registerDate', 'licenseDate', 'country', 'maketype', 'modelyear',
           'displacement', 'gearbox', 'oiltype', 'newprice']
for i in range(1,16):
    columns.append('anonymousFeature'+str(i))
# 重命名数据列名
df_2.columns = columns
df_5.rename(columns=0:'carid',1:'pushDate',2:'pushPrice',3:'updatePriceTimeJson' ,inplace=True)
# df_5.to_csv(r'D:\\比赛\\2021年MathorCup大数据竞赛赛道A\\复赛\\附件五.csv', encoding='utf-8')
df_test = pd.merge(df_5, df_2, on='carid', how='left')
# df_test.to_csv(r'D:\\比赛\\2021年MathorCup大数据竞赛赛道A\\复赛\\任务一test.csv')
"""
训练集的构造
"""
df_1 = pd.read_csv('../data/附件1:估价训练数据.txt', sep='\\t', parse_dates=[1, 11, 12], encoding='utf-8', header=None)  # 附件一
df_4 = pd.read_csv('../data/附件4:门店交易训练数据.txt', sep='\\t', parse_dates=[1, 4, 5], encoding='utf-8', header=None)   # 附件四

columns.append('price')
df_1.columns=columns
df_4.rename(columns=0:'carid',1:'pushDate',2:'pushPrice',3:'updatePriceTimeJson',4:'pullDate',5:'withdrawDate',inplace=True)
df_train = pd.merge(df_4, df_1, how='left', on='carid')

# print(df_train.columns)
# print(df_test.columns)

# 调整trian的数据特征,保持与test一致

df_train['cycle'] = df_train['withdrawDate']-df_train['pushDate']
df_train=df_train.drop(['price', 'pullDate', 'withdrawDate'], axis=1)
df_train['cycle']=df_train['cycle'].astype('timedelta64[D]')
# df_train.to_csv(r'D:\\比赛\\2021年MathorCup大数据竞赛赛道A\\复赛\\任务一train.csv')

1 任务分析
根据题意,附件1和附件4合并作为训练集,附件5和附件2合并作为测试集根据所给的变量信息,初步判断,tradeTime, registerDate, licenseDate, pushDate,pullDate,withdrawDate为时间类型,因此在读入数据的时就直接通过设置parse_dates参数转化成时间类型(datetime64[ns] )
查看训练集的数据情况

df_train.info()

测试集的数据情况

df_test.info()

训练集的缺失值

df_train.isnull().sum()


测试集的缺失值

df_test.isnull().sum()

画图显示缺失情况,训练集

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import missingno as msno

missing=df_train.isnull().sum()
missing=missing[missing>0]
missing.sort_values(inplace=True)
missing.plot.bar()

测试集

missing=df_test.isnull().sum()
missing=missing[missing>0]
missing.sort_values(inplace=True)
missing

以上是关于2021年MathorCup高校数学建模挑战赛——大数据竞赛赛道A -思路分享的主要内容,如果未能解决你的问题,请参考以下文章

2021 年 MathorCup 高校数学建模挑战赛—赛道A二手车估价问题4 问题三思路和数据及参考资料

数学建模MATLAB应用实战系列(138)-2021年MathorCup高校数学建模挑战赛A题思路解析(附代码)

2021年MathorCup高校数学建模挑战赛——大数据竞赛A题

2021年MathorCup高校数学建模挑战赛A二手车估价问题数学建模

2021年MathorCup高校数学建模挑战赛——大数据竞赛赛道A -思路分享

2021 年 MathorCup 高校数学建模挑战赛—赛道A数据预处理特征工程及模型训练Baseline 和数据