二手车市场探索性数据分析
项目描述:近年随着汽车工业发展,二手车市场越来越火热。根据获取的二手车市场数据,对影响二手车价格的因素进行研究与分析
项目职责:1.二手车市场的数据采集和数据集的预处理
2.可视化分析,确定二手车价格的影响因素
3.针对关键因素,分析对二手车价格的影响规律
4.分析图表的制作及分析报告的输出。
第一步:数据获取。抓取所有二手车对应的信息。1.找到各品牌车,对应的目标链接。2.获取所有页面下面,二手车的目标链接。3.通过该目标链接,获取所有二手车的各类信息。4.对各二手车及对应信息进行保存。以便后续分析。
## ********************************** 第一步:抓取二手车的所有品牌 **********************************
# 导入第三方包
import requests
from bs4 import BeautifulSoup
import time
# 设置头
headers = {
'Accept':'*/*',
'Accept-Encoding':'gzip, deflate, br',
'Accept-Language':'zh-CN,zh;q=0.8',
'Connection':'keep-alive',
'User-Agent':'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36'
}
# 二手车主页的链接及解析html
url = 'http://shanghai.taoche.com/all/'
res = requests.get(url, headers = headers).text
soup = BeautifulSoup(res,'html.parser')
# 抓取二手车名称及对应的链接
car_brands = soup.findAll('div',{'class':'brand-name'})
car_brands = [j for i in car_brands for j in i]
brands = [i.text for i in car_brands]
urls = ['http://shanghai.taoche.com' + i['href'] for i in car_brands]
## ********************************** 第二步:抓取所有页面下二手车的目标链接 **********************************
# 构建空列表,生成所需抓取的目标链接
target_urls = []
target_brands = []
for b,u in zip(brands,urls):
# 抓取各品牌二手车主页下的所有页码
res = requests.get(u, headers = headers).text
soup = BeautifulSoup(res,'html.parser')
if len(soup.findAll('div',{'class':'the-pages'})) == 0:
pages = 1
else:
pages = int([page.text for page in soup.findAll('div',{'class':'the-pages'})[0].findAll('a')][-2])
time.sleep(3)
for i in range(1,pages + 1):
target_brands.append(b)
target_urls.append(u+'?page='+str(i)+'#pagetag')
## ********************************** 第三步:对二手车信息进行采集 **********************************
# 构建空列表,用于数据的存储
brand = []
title = []
boarding_time = []
km = []
discharge = []
sec_price = []
new_price = []
# 对每个链接发生请求
for b,u in zip(target_brands,target_urls):
res = requests.get(u, headers = headers).text
soup = BeautifulSoup(res,'html.parser')
# 每页车子的数量
N = len([i.findAll('a')[0]['title'] for i in soup.findAll('div',{'class':'item_details'})])
try:
#车名称
brands = (b+'-')*N
brand.extend(brands.split('-')[:-1])
title.extend([i.findAll('a')[0]['title'] for i in soup.findAll('div',{'class':'item_details'})])
# 二手车的上牌时间、行驶里程数等信息
info = [i.findAll('li') for i in soup.findAll('ul',{'class':'ul_news'})]
boarding_time.extend([i[0].text[4:] for i in info])
km.extend([i[1].text[4:] for i in info])
discharge.extend([i[3].text[4:] for i in info])
sec_price.extend([float(i.findAll('h2')[0].text[:-1]) for i in soup.findAll('div',{'class':'item_price'})])
new_price.extend([i.findAll('p')[0].text.split('\xa0')[0][5:].strip() for i in soup.findAll('div',{'class':'item_price'})])
except IndexError:
pass
# 每3秒停顿一次
time.sleep(3)
## ********************************** 第四步:将采集来的数据进行存储 **********************************
# 数据导出
import pandas as pd
cars_info = pd.DataFrame([brand,title,boarding_time,km,discharge,sec_price,new_price]).T
cars_info = cars_info.rename(columns={0:'Brand',1:'Name',2:'Boarding_time',3:'Km',4:'Discharge',5:'Sec_price',6:'New_price'})
cars_info.to_csv('second_cars_info.csv', index=False)
第二步:数据清洗。对抓取的数据,进行预处理。
通过上表,可以清楚看到整个数据结构,各变量包括代表汽车品牌、汽车款式、上牌时间、行驶里程数、排放标准、二手价格和同款新车的参考价格。从中也发现一些问题:1.二手车上牌时间,存在‘’未上牌‘’,行驶里程、新车价格、上牌时间为字符串,所以需要进行数据预处理工作。
# 导入第三方模块
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
plt.style.use('ggplot')
# 读取数据
cars = pd.read_csv('C:/Users/Administrator/Desktop/second_cars_info.csv')
#********************第一部分:数据预处理*****************************
# “未上牌”的二手车占比
N = np.sum(cars.Boarding_time == '未上牌')
Ratio = N/cars.shape[0]
Ratio
# 由于未上牌的汽车数量占比极少,仅千分之八,这里不妨考虑将其删除
cars = cars.loc[cars.Boarding_time != '未上牌',:]
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))
# 计算上牌日期距离2018年03月份的月数
cars['diff_months'] = (2018-cars.year)*12 + (3-cars.month) + 1
# 显示数据的前5行
cars.head()
# 数值类型转换
# “百公里内”的样本量
N = np.sum(cars.Km == '百公里内')
Ratio = N/cars.shape[0]
Ratio
# 剔除“万公里”三个字
cars['Km_new'] = cars.Km.str[:-3]
# 将“百”字替换为0.005
cars.Km_new.replace('百','0.005', inplace=True)
# 数据类型转换
cars.Km_new = cars.Km_new.astype('float')
cars.head()
#cars.New_price_new.astype('float')
# 这里无法实现数据类型的转换,因为该字段中含有“暂无”这样的值。
# “暂无”的样本量
N = np.sum(cars.New_price == '暂无')
Ratio = N/cars.shape[0]
Ratio
# 删除字段中的“万”字
cars['New_price_new'] = cars.New_price.str[:-1]
cars = cars.loc[cars.New_price != '暂无',:]
# 数据类型的转换
cars.New_price_new = cars.New_price_new.astype('float')
# 数据集的概览信息
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()
# 指定任意的切割点,将数据分段
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()
# 行驶公里数的饼图展现
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()
# 取出奥迪和大众两种车辆
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()
from sklearn.linear_model import LinearRegression
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()