使用Python探索二手车市场(含代码)
往期回顾
从零开始学Python【15】--matplotlib(散点图)
从零开始学Python【14】--matplotlib(折线图)
从零开始学Python【13】--matplotlib(直方图)
从零开始学Python【12】--matplotlib(箱线图)
从零开始学Python【11】--matplotlib(饼图)
从零开始学Python【10】--matplotlib(条形图)
前言
上一期中,我们已经手把手的分享了如何使用Python实现某二手车平台数据的抓取,并成功的完成11,281条二手车数据的搜集。基于此,我们需要对获取的数据进行探索性分析,这里就采用我们之前学习的pandas模块和matplotlib模块的知识点做一个小小的数据分析项目。
数据清洗
我们再来看一下爬虫后获取数据的前几行,便于读者清楚的了解数据结构,各变量分别代表汽车品牌、汽车款式、上牌时间、行驶里程数、排放标准、二手价格和同款新车的参考价格:
  30 30825 30 9306 0 0 2665 0 0:00:11 0:00:03 0:00:08 2664;从数据的前6行我们可以发现数据的一些问题,包括二手车的上牌时间,有的样本记录为“未上牌”,需要特殊处理、行驶里程数为字符串,需要类型的转化、新车原价也是字符串,也需要处理、上牌时间为字符串,如何计算上牌时间与当前10月份的时间差(月数)。而这些问题的解决,就需要我们进行数据清洗工作。
上牌时间字段的处理
由于上牌时间字段存在“未上牌”的值,我们需要看看这样的值占多大比例,如果比例不大,可以考虑将其删除。
# 导入第三方模块
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
# 可视化的中文处理
plt.rcParams['font.sans-serif'] = 'Microsoft YaHei'
plt.rcParams['axes.unicode_minus'] = False
# 设置类似R语言中的ggplot2风格
plt.style.use('ggplot')
# 读取数据(记得需要将csv文件先另存为UTF-8的编码,否则会报编码错误)
cars = pd.read_csv('second_cars_info.csv')
# “未上牌”的二手车占比
N = np.sum(cars.Boarding_time == '未上牌')
Ratio = N/cars.shape[0]
Ratio
# 由于未上牌的汽车数量占比极少,仅千分之八,这里不妨考虑将其删除
cars = cars.loc[cars.Boarding_time != '未上牌',:]
由于上面的清洗步骤,导致一些观测被删除,此时的行索引就不再是一个连续的自然数了,故需要重新设置索引,以免产生不必要的错误(因为pandas模块中数据结构的行索引会自动对齐)。
# 重新设置行索引
cars.index = range(0,cars.shape[0])
# 取出上牌时间变量中的年和月
cars['year'] = cars.Boarding_time.str[:4].astype('int')
month = cars.Boarding_time.str.findall('年(.*?)月')
print(month.head(10))
# 由于month是列表构成的序列,所以需要非列表化,再序列化
month = pd.Series([i[0] for i in month]).astype('int')
cars['month'] = month
# print(month.head(10))
# 计算上牌日期距离2017年10月份的月数
cars['diff_months'] = (2017-cars.year)*12 + (10-cars.month) + 1
# 显示数据的前5行
cars.head()
行驶里程数字段的处理
行驶里程数为字符串,要实现字符串转数值,需要先将字段中的“万公里”字样去除,然后再进行数据类型的转换。
# 剔除“万公里”三个字
cars['Km_new'] = cars.Km.str[:-3]
# 数值类型转换
cars.Km_new = cars.Km_new.astype('float')
哎?这里报错了,原因是行驶公里数字段中含“百公里内”这样的值,所以剔除该字段末尾3个字符之后,还剩余“百”字,导致了无法将“百”实现数值的转化。
# “百公里内”的样本量
N = np.sum(cars.Km == '百公里内')
Ratio = N/cars.shape[0]
Ratio
行驶里程数为“百公里内”的样本量仅仅只有63条,占比为千分之六,我们不妨将“百公里内”的样本替换成50公里,即0.005万公里。
# 将“百”字替换为0.005
cars.Km_new.replace('百','0.005', inplace=True)
# 数据类型转换
cars.Km_new = cars.Km_new.astype('float')
cars.head()
同款新车价格字段的处理
从上面的数据结构可知,同款新车的参考价格都存在“万”字,要想实现数据类型的转换,同样先是将“万”字去掉,然后进行数值转换。
# 删除字段中的“万”字
cars['New_price_new'] = cars.New_price.str[:-1]
#cars.New_price_new.astype('float')
# 这里无法实现数据类型的转换,因为该字段中含有“暂无”这样的值。
# “暂无”的样本量
N = np.sum(cars.New_price == '暂无')
Ratio = N/cars.shape[0]
Ratio
由于本人对车子的了解情况甚少,无法对“暂无”的价格进行评估,不妨就将这1.3%的样本作剔除处理。
cars = cars.loc[cars.New_price != '暂无',:]
# 数据类型的转换
cars.New_price_new = cars.New_price_new.astype('float')
cars.head()
经过上面的数据清洗工作,数据中有一部分被删除,也有一部分值被替换。通过数据框结构的shape方法,可知目前数据集还剩下11,047个样本,一共减少了234条二手车样本。最后,我们再来看看数据集的概览情况:
# 数据集的概览信息
cars.describe()
采用可视化方法进行数据探索
直方图的绘制
我想大家一定也和我一样,最关心的是二手车的价格分布吧,看看二手车的价格都落在什么范围内?首先,我们对二手车的价格绘制一张直方图,看看其分布形状。
# 二手车价格分布情况
min_price = cars.Sec_price.min()
max_price = cars.Sec_price.max()
# 直方图
plt.hist(cars.Sec_price, # 二手车价格数据
bins = np.arange(min_price,max_price+10,10), # 以10万元为组距
color = 'steelblue', # 指定填充色
)
# 设置坐标轴标签和标题
plt.title('二手车价格分布直方图')
plt.xlabel('价格')
plt.ylabel('频数')
# 去除图形顶部边界和右边界的刻度
plt.tick_params(top='off', right='off')
# 图形显示
plt.show()
图形明显呈现偏态分布,并且是右偏长尾,即随着二手车价格越高,车子的数量就下降的越多。那请问,多少比例的二手车会落在某个价格范围内呢?这个问题的回答,可以借助于累计频率直方图来展示。
# 累积频率直方图
plt.hist(cars.Sec_price, # 二手车价格数据
bins = np.arange(min_price,max_price+10,10), # 以10万元为组距
normed = True, # 设置为频率直方图
cumulative = True, # 积累直方图
color = 'steelblue', # 指定填充色
)
# 添加水平参考线
plt.axhline(y = 0.5, color = 'blue', linestyle = '--', linewidth = 2)
plt.axhline(y = 0.8, color = 'red', linestyle = '--', linewidth = 2)
# 设置坐标轴标签和标题
plt.title('二手车价格累积分布直方图')
plt.xlabel('价格')
plt.ylabel('累积频率')
# 去除图形顶部边界和右边界的刻度
plt.tick_params(top='off', right='off')
# 图形显示
plt.show()
从这个累积直方图可知,一半的二手车价格在10万以内,就整体而言,80%的二手车均在30万以内。哈哈,如果你有想法,可以去二手车市场观望观望哦。
条形图的绘制
上面的直方图,其实质反映的就是等间距的条形图。如果需要自定义价格范围,然后基于这个范围再作条形图的绘制,该如何操作呢?这里需要借助于pandas模块中的cut函数,将连续的数值切割成不同的数据段:
# 指定任意的切割点,将数据分段
price_cuts = pd.cut(cars.Sec_price, bins = [min_price,3,5,8,10,15,20,30,50,max_price])
# 按照数据段,进行数据的统计,即频数统计
price_stats = price_cuts.value_counts()
x = range(len(price_stats))
# 将索引用作绘图的刻度标签
label = price_stats.index
# 占比用于绘图的数值标签
percent = [str(round(i*100,2))+'%' for i in price_stats/price_stats.sum()]
# 绘图plt.bar(x, # x轴数据
price_stats, # y轴数据
align = 'center', # 刻度居中对齐
color='steelblue', # 填充色
alpha = 0.8 # 透明度
)
# 设置y轴的刻度范围plt.ylim(0,2200)
# x轴刻度标签
plt.xticks(x,label)
# 设置坐标轴标签和标题
plt.title('二手车价格区间条形图')
plt.xlabel('价格区间')
plt.ylabel('频数')
# 去除图形顶部边界和右边界的刻度
plt.tick_params(top='off', right='off')
# 为每个条形图添加数值标签
for x,y,z in zip(x,price_stats,percent):
plt.text(x, y+30,'%s' %z,ha='center')
# 显示图形
plt.show()
很显然,从这个自定义的分段数据中,5~8万的二手车最多,占了17.72%,其次是3~5万,也占了16.21%。从总体的排序来看,10万以内的二手车数量,排在了前3。
饼图的绘制
关心完了二手车的价格,是不是该关心一下这辆车跑了多少路了?毕竟二手车的价格跟行驶里程数也是有关系的嘛。我们按照二手车网站的筛选条件,将行驶里程数分割为5段,即1万公里以内、1~3万公里、3~5万公里、5~10万公里及10万公里以上。
# 行驶公里数的饼图展现
km_min = cars.Km_new.min()
km_max = cars.Km_new.max()
# 指定任意的切割点,将数据分段
km_cuts = pd.cut(cars.Km_new, bins = [km_min, 1,3,5,10,km_max])
km_stats = km_cuts.value_counts()
km_stats
# 绘制饼图
# 将横、纵坐标轴标准化处理,保证饼图是一个正圆,否则为椭圆
plt.axes(aspect='equal')
# 提取出索引作为标签
labels = km_stats.index
# 自定义颜色
colors=['#9999ff','#ff9999','#7777aa','#2442aa','#dd5555']
# 绘制饼图
plt.pie(km_stats.values,
labels=labels,
colors = colors, # 设置颜色
autopct='%.1f%%', # 设置百分比的格式,这里保留一位小数
counterclock = False, # 设置为顺时针方向
wedgeprops = {'linewidth': 1.5, 'edgecolor':'green'},# 设置饼图内外边界的属性值
textprops = {'fontsize':12, 'color':'k'} # 设置文本标签的属性值
)
# 添加图标题
plt.title('二手车行驶公里数分布(万公里)')
# 显示图形
plt.show()
从饼图返回的信息可知,超过一半的车,其行驶里程数在5~10万公里,而占比最少的是1万公里以内的二手车,6%不到。
散点图的绘制
影响二手车价格的因素可能二手车的行驶时长、行驶公里数和汽车品牌,直观感觉,二手车的价格应该与行驶时长、行驶公里数成反向关系,即行驶时长或行驶公里数越大,则二手车的价格应该越便宜。接下来我们来探索一下,是不是这么个道理,由于二手车的品牌也严重影响价格,故这里以奥迪和大众为例,绘制散点图。
# 取出奥迪和大众两种车辆
index = cars['Brand'].isin(['奥迪','大众'])
some_cars = cars.loc[index,:]
# 散点图的绘制
brands = some_cars['Brand'].unique()
colors = ['steelblue', '#ff9999']
for i in range(len(brands)):
plt.scatter(some_cars.loc[some_cars['Brand'] == brands[i],'Km_new'], # x轴数据为二手车行驶公里数
some_cars.loc[some_cars['Brand'] == brands[i],'Sec_price'], # y轴数据为二手车价格
s = 20, # 设置点的大小
c = colors[i], # 设置点的颜色
marker = 'o', # 设置点的形状
alpha = 0.9, # 设置点的透明度
linewidths = 0.3, # 设置散点边界的粗细
edgecolors = 'k', # 设置散点边界的颜色
label = brands[i] # 添加标签
)
# 添加轴标签和标题
plt.title('二手车行驶公里数与价格的关系')
plt.xlabel('行驶公里数')
plt.ylabel('价格')
# 去除图边框的顶部刻度和右边刻度
plt.tick_params(top = 'off', right = 'off')
# 显示图例
plt.legend()
# 显示图形
plt.show()
很显然,从图中可知,二手车的行驶里程数与价格成反向关系,似乎奥迪车显示的更为明显。接下来我们再来看看二手车的行驶时长与价格之间是否也是反向关系,这里就以奥迪车为例了,同时,我们也在散点图的基础上再添加一条回归线。
# 取出奥迪车的数据子集
aodi_car = cars.loc[cars['Brand'] == '奥迪',:]
plt.scatter(aodi_car.diff_months,
aodi_car.Sec_price,
s = 30, # 设置点的大小
c = 'steelblue', # 设置点的颜色
marker = 'o', # 设置点的形状
alpha = 0.9, # 设置点的透明度
linewidths = 0.3, # 设置散点边界的粗细
label = '观测点')
# 建模
reg = LinearRegression().fit(aodi_car.diff_months.reshape(-1,1), aodi_car.Sec_price)
# 回归预测值
pred = reg.predict(aodi_car.diff_months.reshape(-1,1))
# 绘制回归线
plt.plot(aodi_car.diff_months, pred, linewidth = 2, label = '回归线')
# 添加轴标签和标题
plt.title('二手车的行驶时长与价格的关系')
plt.xlabel('行驶时长(月)')
plt.ylabel('价格(万元)')
# 去除图边框的顶部刻度和右边刻度
plt.tick_params(top = 'off', right = 'off')
# 显示图例
plt.legend(loc = 'best')# 显示图形
plt.show()
OK,感觉上还是蛮准确的,即二手车的行驶时长与价格之间确实存在反向关系。从图中我们也发现一些远离总体的异常点,即价格在100万以上的,我想这些都是顶级高档的奥迪汽车了吧。但不管怎样,这些“异常点”也符合行驶时长与价格之间反向关系。
结语
OK,关于二手车的探索性分析案例,我们就分享到这里,但感兴趣的你,一定要动手操作一遍,否则还是走马观花的效果哦。如果你有问题,欢迎在公众号的留言区域表达你的疑问。同时,也欢迎各位朋友继续转发与分享文中的内容,让跟多的人学习和操作。最后,本文相关的Python脚本和PDF版本已存放到百度云盘,可以通过下面的链接获取:
链接: https://pan.baidu.com/s/1dFapkdV 密码: 9qv4
每天进步一点点2015
学习与分享,取长补短,关注小号!
长按识别二维码 马上关注