一年半前写了二手车数据获取的博客,期间经历了给导师做项目、找工作、实习、写论文,终于闲下来可以继续更新数据处理部分了。
数据获取部分请看这里:Python数据分析-二手车数据用于机器学习二手车价格预测
相关代码我放在我码云仓库中,感兴趣的同学可以clone、star,链接:码云仓库
同时,分享我的秘密基地,懂得同学已经拿出手机了,顺便贴个链接:吉吉的机器学习乐园
不想执行代码获取数据的同学,或着急使用的同学,我都把数据放在秘密基地了。
原始数据维度为:93738*212
获取时间为:2020年7月25日
数据处理十分重要,一个机器学习模型预测结果的好坏与数据处理有直接关联。每个人处理数据的思维和方式都不一样,因此本文只是依据我的一些学习经验进行数据处理,给大家当个baseline~
代码如下:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import sys
import seaborn as sns
import warnings
import datetime
warnings.filterwarnings("ignore")
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
# 设置可显示的最大行列数量
pd.set_option('display.max_rows', 100,'display.max_columns', 100,"display.max_colwidth",1000,'display.width',1000)
代码如下:
# 读取数据
data = pd.read_excel("cars_info.xlsx", na_values=np.nan)
数据的许多列包含大量的空值和单一数据,当这些数据超过一定量时,对模型来说是无效的,因此先将这一部分数据进行删除。
代码如下:
# 每列数据为空的列,数量大于80000,删除该列(无参考价值)
for c in data.columns:
if data[c].isna().sum() > 80000:
data.drop([c], axis=1, inplace=True)
# 每列数据为“无”的列,数量大于60000,删除该列(无参考价值)
for c in data.columns:
if data[c].isin(["无"]).sum() > 60000:
data.drop([c], axis=1, inplace=True)
# 因为数据本身含有长宽高的单独列,因此“长*宽*高(mm)”列删除
data.drop(['长*宽*高(mm)'], axis=1, inplace=True)
# 数据中许多列都包含“标配”,数量大于60000时无参考价值
for c in data.columns:
if data[c].isin(["标配"]).sum() > 60000:
print(c, data[c].isin(["标配"]).sum())
data.drop([c], axis=1, inplace=True)
# 删除 “售价” 和 “排量” 为空的行
data.dropna(axis=0,subset = ["售价", "排量(L)"], inplace=True)
# 该列含有大量范围值,且已有新车售价,删除处理
data.drop(['厂商新车指导价'], axis=1, inplace=True)
# “过户记录”许多为空,我们认为可能无过户记录,因此填充0;“载客/人”按照该列平均值进行填充
data['过户记录'].fillna(0, inplace=True)
data['载客/人'].fillna(int(data['载客/人'].mean()), inplace=True)
通过Excel表我们可以发现,有些列可以完全处理成数值型数据。剔除这些列中的异常数据,并且为空值进行填充,可以使用平均值或众数进行填充。
代码如下:
# 筛选出可以转化为数值型数据的列
numerical_col = ['售价', '新车售价', '行驶里程', '过户记录',
'载客/人', '排量(L)', '最高车速(km/h)', '官方0-100km/h加速(s)',
'工信部综合油耗(L/100km)', '长度(mm)', '宽度(mm)', '高度(mm)',
'轴距(mm)', '前轮距(mm)', '后轮距(mm)', '车门数', '油箱容积(L)',
'整备质量(kg)', '最小离地间隙(mm)', '排量(mL)', '气缸数(个)',
'每缸气门数(个)', '压缩比', '最大马力(Ps)', '最大功率(kW)',
'最大扭矩(N·m)'
]
numerical_df = data[numerical_col]
# 将非数值型数据替换为np.nan
for c in numerical_col[5:]:
numerical_df[c] = numerical_df[c].replace("无", np.nan).replace("false", np.nan).replace("未知", np.nan)
# 空值填充
mean_fill_col = ['排量(L)', '最高车速(km/h)', '官方0-100km/h加速(s)',
'工信部综合油耗(L/100km)', '长度(mm)', '宽度(mm)', '高度(mm)',
'轴距(mm)', '前轮距(mm)', '后轮距(mm)', '油箱容积(L)',
'整备质量(kg)', '最小离地间隙(mm)', '排量(mL)', '压缩比',
'最大马力(Ps)', '最大功率(kW)', '最大扭矩(N·m)'
]
many_fill_col = ['车门数', '气缸数(个)', '每缸气门数(个)'] # 多数都为4
# 将dataframe转化成float类型
numerical_df = numerical_df.astype(float)
# 进行填充
for c in mean_fill_col:
numerical_df[c].fillna(numerical_df[c].mean(), inplace=True)
for c in many_fill_col:
numerical_df[c].fillna(4, inplace=True)
# 将处理完的数据更新至data中
data[ numerical_col ] = numerical_df
# 处理 ['座位数', '行李厢容积(L)', '最大功率转速(rpm)', '最大扭矩转速(rpm)'] 中的异常值
# 异常值处理函数
def pickNum(df, c):
if '-' in df[c]:
num_list = df[c].split('-')
return num_list[0]
elif '―' in df[c]:
num_list = df[c].split('―')
return num_list[0]
elif '~' in df[c]:
num_list = df[c].split('~')
return num_list[0]
elif '/' in df[c]:
num_list = df[c].split('/')
return num_list[0]
else:
return df[c]
pickNum_col = ['座位数', '行李厢容积(L)', '最大功率转速(rpm)', '最大扭矩转速(rpm)']
# 转化为str类型
data[pickNum_col] = data[pickNum_col].astype(str)
# 异常值处理
for c in pickNum_col:
data[c] = data.apply(lambda x:pickNum(x, c), axis=1)
# 将“无”、“false”、“未知” 等数据替换为空
for c in pickNum_col:
data[c] = data[c].replace("无", np.nan).replace("false", np.nan).replace("未知", np.nan)
data[pickNum_col] = data[pickNum_col].astype(float)
# 众数填充
data['座位数'].fillna(5, inplace=True)
# 均值填充
for c in pickNum_col[1:]:
data[c].fillna(data[c].mean(), inplace=True)
数据中包含许多日期数据,我将它们转换成天数差,即用数据获取的时间减去对应的时间。
代码如下:
# 处理日期型数据
date_col = ['商业险过期日期','交强险过期日期', '注册日期', '出厂日期', '车船税过期日期']
data['数据获取日期'] = '2020-07-25'
date_col.append('数据获取日期')
# 处理日期型数据函数
def calDate(df, c):
if pd.isnull(df['出厂日期']):
return np.nan
else:
d1=datetime.datetime.strptime('2020-07-25',"%Y-%m-%d")
d2=datetime.datetime.strptime(df[c],"%Y-%m-%d")
diff_days=d1-d2
# print(diff_days)
return diff_days.days
# 处理数据中的异常值
for c in date_col[:-1]:
data[c] = data[c].replace("--", np.nan)
# 生成时间差的列
for c in date_col[:-1]:
data[c+'差(天)'] = data.apply(lambda x:calDate(x, c), axis=1)
new_date_col = ['商业险过期日期差(天)','交强险过期日期差(天)', '注册日期差(天)', '出厂日期差(天)', '车船税过期日期差(天)']
# 均值填充
for c in new_date_col:
data[c].fillna(data[c].mean(), inplace=True)
# 删除之前的日期列
data.drop(date_col, axis=1, inplace=True
许多列的数据要么为"有",要么为"无"。类似这种的数据我们称为"二值型"数据,可以将其转化为0-1的形式。
代码如下:
# 处理0-1型数据
zero_one_col_names = ['前排侧气囊', '无钥匙启动系统', 'TRC牵引力控制系统', '上坡辅助', '电动天窗',
'真皮方向盘', '日间行车灯', '自动头灯', '后视镜加热', '后雨刷', '后座出风口',
'4S店保养', '原始购车/过户发票', '车辆购置税完税证明']
# 异常值替换及空值填充
for c in zero_one_col_names:
data[c] = data[c].replace("无", 0).replace("false", 0).replace("true", 1).replace("标配", 1).replace("false", 0).replace("否", 0).replace("是", 1).replace("有(已见发票)", 1).replace("有(未见发票)", 0).replace("已缴税(未见证明)", 0).replace("已缴税(已见证明)", 1).replace("K请问欺负我测了没人粉粉嫩嫩妇女。。佛方法v。。", 0)
data[c].fillna(0, inplace=True)
当一列值可以被分成多个类别时,我们可以将数据处理成独热编码(One-Hot)的形式,建议类别的个数超过10的时候就不要使用独热编码了,因为会导致数据过于稀疏,它的详细作用就不介绍了,朋友们自行百度。
代码如下:
one_hot_col_names = ['进气形式', '气缸排列形式', '配气机构', '燃油标号', '供油方式', '缸盖材料',
'缸体材料', '燃油形式', '变速箱类型', '驱动方式', '助力类型', '车体结构',
'前制动', '后制动', '驻车制动类型', '备胎规格', '定速巡航', '真皮座椅',
'变速器类型', '燃料类型', '车身颜色', '挡位个数']
# 这些是在excel筛选时发现的一些异常值,进行替换
data['挡位个数'] = data['挡位个数'].replace("无", "无级变速")
data['车身颜色'] = data['车身颜色'].replace("--", np.nan)
data['真皮座椅'] = data['真皮座椅'].replace(",", np.nan)
data['定速巡航'] = data['定速巡航'].replace("/", np.nan).replace("田......看", np.nan)
data['助力类型'] = data['助力类型'].replace("无助力", "无")
for c in one_hot_col_names:
data[c] = data[c].replace("false", "无")
data[c].fillna("无", inplace=True)
# 使用pandas中的get_dummies方法,直接将想要转换成独热编码额数据进行转换
one_hot_data = pd.get_dummies(data[one_hot_col_names])
# 合并独热编码数据,并删除之前的列
data = pd.concat([data,one_hot_data],axis = 1)
data.drop(one_hot_col_names, axis=1, inplace=True)
# 获取当前数据类型为数值型的列
final_col = list(data.describe().columns)
final_data = data[final_col]
# 有22列数据形式较为复杂,在这里就不进行处理了
# 有兴趣的同学可以自己尝试进行处理
# 保存处理后的数据
final_data.to_excel("final_data.xlsx", index=False)
经过以上步骤的数据处理,我们将原始数据转换成可以放入机器学习模型的数据了。这里的数据处理过程比较简单,其中的一些操作也可以简单的理解成特征工程的过程(毕竟只是baseline),有能力的同学也可以按照自己的想法进行数据处理和特征工程。
最后的数据维度为:93738*190
下一期将进行机器学习的模型训练,以及实验结果的分析。