数据集网址:https://tianchi.aliyun.com/competition/entrance/231784/information
参考了:Datawhale 零基础入门数据挖掘,更多详情在https://tianchi.aliyun.com/notebook/95457
1. 相关库的引入
import warnings # #导入warnings包,利用过滤器来实现忽略警告语句。
warnings.filterwarnings('ignore')
import pandas as pd #
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import missingno as msno
- pandas库:Pandas是数据分析三大剑客之一,是Python的核心数据分析库,它提供了快速、灵活、明确的数据结构,能够简单、直观、快速地处理各种类型的数据
- numpy库:是Python科学计算中最重要的库之一,其主要目的是提供高效的数组和矩阵操作,为科学计算提供一个强大的基础工具库
- matplotlib库:是一个用于绘制图表和可视化数据的 Python库。它提供了丰富的绘图工具,可以用于生成各种静态、交互式和动画图表
- seaborn库:是一个基于matplotlib的Python数据可视化库,提供了高级的统计图形和美观的绘图风格,使得数据可视化更加简单和直观。它内置了许多常见的图表类型和颜色主题,能够帮助我们快速地创建漂亮且具有统计意义的图形
- missingno库:用于可视化和分析数据中的缺失值。它提供了简单而直观的方法来理解数据集中缺失值的分布和模式
2. 数据的加载
train_data=pd.read_csv(train_path,sep=' ')
test_data=pd.read_csv(test_path,sep=' ') # 列与列之间是通过空格分隔的
查看数据形状:
train_data.shape
查看数据前几行和后几行,对数据大致有一个了解
train_data.head()
train_data.tail()
3. 数据概况
3.1 统计值查看
查看每一列的个数count、平均值mean、方差std、最小值min、中位数25% 50% 75% 、以及最大值。看这个信息主要是瞬间掌握数据的大概的范围以及每个值的异常值的判断
train_data.describe()
3.2 查看数据类型
train_data.info()
返回了列名,每一列非空值数量以及列的类型
4. 判断缺失值
4.1 统计每一列空值的数量
train_data.isnull().sum()
4.2 可视化缺失值数量
missing = Train_data.isnull().sum() #统计缺失值
missing = missing[missing > 0] # 找到有缺失值的列
missing.sort_values(inplace=True) # 按照缺失值数量排序
missing.plot.bar() # 柱状图显示
排序后的结果:
可视化:
分析: 通过以上两句可以很直观的了解哪些列存在 “nan”, 并可以把nan的个数打印,主要的目的在于 nan存在的个数是否真的很大,如果很小一般选择填充,如果使用lgb等树模型可以直接空缺,让树自己去优化,但如果nan存在的过多、可以考虑删掉
- 用missingno库可视化缺失值
矩阵图展示:空白越多说明缺失越严重
msno.matrix(train_data)
柱状图展示:
msno.bar(train_data)
5. 判断异常值
5.1 异常值检测
train_data.info() # 查看结果发现发现除了notRepairedDamage 为object类型其他都为数字,猜测notRepairedDamage这一列中有异常值
train_data['notRepairedDamage'].value_counts() # 统计notRepairedDamage这一列的值及其对应的数量
notRepairedDamage的含义是汽车是否有损坏,-也是缺失值,鉴于很多模型对nan都有处理,这里先不做处理,转换成nan
train_data['notRepairedDamage'].replace('-',np.nan,inplace=True) # inplace=True表示在原始数据上直接操作
- 对于其他的列,同理我们使用value_counts()查看其列值的分布,查看是否有异常,比如seller列:
可以看到seller列值这两个类别特征严重倾斜,一般不会对预测有什么帮助,故可以先删掉,当然你也可以继续挖掘,但是一般意义不大
train_data['seller'].value_counts()
还有offerType列,所有值都一样,故也不会对结果产生什么影响,可以删去
train_data['offerType'].value_counts()
删除列:
del train_data['seller']
del train_data['offerType']
6. 了解预测值的分布
6.1 统计各预测值的分布
train_data['price'].value_counts()
6.2 总体分布概况
分别用无界约翰逊分布,正态分布、对数正态分布来拟合我们的数据
无界约翰逊分布拟合:
# Scipy是一个用于科学计算的Python库,它提供了许多用于数值计算、优化、插值、统计和信号处理的功能
import scipy.stats as st
plt.figure(1) # 创建一个新的图形窗口
plt.title('Johnson SU') # 设置图形的标题为 'Johnson SU'
# 函数绘制概率密度分布图
sns.distplot(y, kde=False, fit=st.johnsonsu)
# y: 这是要绘制分布的数据。
# kde=False: 禁用核密度估计曲线,只绘制直方图。
# fit=st.johnsonsu: 在这里,st.johnsonsu 表示使用 Johnson SU 分布进行拟合,下图中的曲线就是Johnson SU 分布拟合的
补充:无界约翰逊分布(Unbounded Johnson Distribution)是一种统计概率分布,它是有界约翰逊分布的一种扩展,用于建模不受限制的随机变量。这种分布的特点是能够适应多种分布形状,包括对称、右偏和左偏的形状。约翰逊分布是通过对原始随机变量进行适当的变换,使其服从正态分布。无界约翰逊分布是指这种变换没有对随机变量的范围(上下界)进行限制,因此是“无界”的
正态分布拟合:
plt.figure(1)
plt.title('Normal')
sns.distplot(y, kde=False, fit=st.norm)
可以看出正态分布拟合的效果不怎么好
对数正态分布拟合:
plt.figure(1)
plt.title('Normal')
sns.distplot(y, kde=False, fit=st.lognorm)
效果也还行
分析:价格不服从正态分布,所以在进行回归之前,它必须进行转换。虽然对数变换做得很好,但最佳拟合是无界约翰逊分布
6.2 查看预测值的具体频数
plt.hist(train_data['price'], orientation = 'vertical',histtype = 'bar', color ='orange')
plt.show()
# orientation='vertical': 这个参数指定了直方图的方向。在这里,设置为 'vertical' 表示垂直方向的直方图。
# histtype='bar': 这个参数指定了直方图的类型。在这里,设置为 'bar' 表示使用条形(bar)来表示直方图。
# color ='orange': 这个参数指定了直方图的颜色。在这里,设置为 'red' 表示直方图的颜色为红色。
# plt.show(): 这个函数用于显示图形。在 Matplotlib 中,你需要调用 plt.show() 才能实际显示图形。
- 查看频数, 大于20000得值极少,其实这里也可以把这些当作特殊得值(异常值)直接用填充或者删掉,再前面进行
- log变换 z之后的分布较均匀,可以进行log变换进行预测,这也是预测问题常用的trick
plt.hist(np.log(train_data['price']), orientation = 'vertical',histtype = 'bar', color ='orange')
plt.show()
6.3 查看skewness(偏度) and kurtosis(峰度)
■ 偏度(skewness),是描述数据分布形态的统计量,其描述的是某总体取值分布的对称性,简单来说就是数据的不对称程度,正态分布的偏度为0
■ 峰度又称峰态系数。偏度是描述某变量所有取值分布形态陡缓程度的统计量,简单来说就是数据分布顶的尖锐程度。峰度为0表示该总体数据分布与正态分布的陡缓程度相同;峰度 >0表示该总体数据分布与正态分布相比较为陡峭,为尖顶峰
train_data.skew()
train_data.kurt()
偏度可视化:
sns.distplot(train_data.skew(),color='blue',axlabel="skewness")
峰度可视化:
sns.distplot(train_data.kurt(),color='green',axlabel="kurtosis")
7. 特征分析
特征分为类别特征和数字特征,一般分开进行分析
这里需要根据实际意义人为来区分:
y_train=train_data['price'] # 把价格标签摘出来
# 数字特征
numeric_features = ['power', 'kilometer', 'v_0', 'v_1', 'v_2', 'v_3', 'v_4', 'v_5', 'v_6', 'v_7', 'v_8', 'v_9', 'v_10', 'v_11', 'v_12', 'v_13','v_14' ]
# 分类特征
categorical_features = ['name', 'model', 'brand', 'bodyType', 'fuelType', 'gearbox', 'notRepairedDamage', 'regionCode',]
7.1 统计类别特征的值和数量
nunique()可以计算每个列的类别数量
for cat in categorical_features:
print("{}的特征分布如下:".format(cat))
print("{}特征一共有{}个值,每个值的数量如下:".format(cat,train_data[cat].nunique()))
print(train_data[cat].value_counts())
7.2 类别特征箱形图可视化
■ 箱型图:箱形图(也称盒图,箱线图等),因为形状长得像一个箱子而得名。它是用于显示一组数据分散情况的统计图,可以通过这种图直观的探索数据特征。
■ 箱线图判断异常值的标准以四分位数和四分位距为基础,四分位数具有一定的耐抗性,多达25%的数据可以变得任意远而不会很大地扰动四分位数,所以异常值不会影响箱形图的数据形状。箱线图识别异常值的结果比较客观,能够准确稳定地描绘出数据的离散分布情况,同时也利于数据的清洗。
■ 补充:
①箱线图是针对连续型变量的,解读时候重点关注平均水平、波动程度和异常值。
②当箱子被压得很扁,或者有很多异常的时候,试着做对数变换。
③当只有一个连续型变量时,并不适合画箱线图,直方图是更常见的选择。
④箱线图最有效的使用途径是作比较,配合一个或者多个定性数据,画分组箱线图
# 因为 name和 regionCode的类别太稀疏了,这里我们把不稀疏的几类画一下
categorical_features = ['model',
'brand',
'bodyType',
'fuelType',
'gearbox',
'notRepairedDamage']
for c in categorical_features:
# 将每个分类特征的数据类型转换为 Pandas 中的 'category' 类型
train_data[c] = train_data[c].astype('category')
if train_data[c].isnull().any():
# 如果包含缺失值,则新增一个类别'MISSING'作为缺失值
train_data[c] = train_data[c].cat.add_categories(['MISSING'])
# 用'MISSING'填充缺失值
train_data[c] = train_data[c].fillna('MISSING')
# 自定义的箱线图绘制函数
def boxplot(x, y, **kwargs):
sns.boxplot(x=x, y=y)
x=plt.xticks(rotation=90) # 将 x 轴标签旋转90度,以便更好地显示分类特征的名称
f = pd.melt(train_data, id_vars=['price'], value_vars=categorical_features)
g = sns.FacetGrid(f, col="variable", col_wrap=2, sharex=False, sharey=False)
g = g.map(boxplot, "value", "price")
还可以使用小提琴图、柱状图等多种图表来可视化,这里不再一一赘述。
7.3数字特征分析
- 追加标签
numeric_features.append('price')
numeric_data=train_data[numeric_features] # 带标签的数字特征数据集
- 相关性分析
correlation=numeric_data.corr() # 计算各列之间的相关性
print(correlation['price'].sort_values(ascending=False),'\n') # 打印预测值price与各个特征之间的相关性,并降序排序
- 利用热图可视化相关性
plt.title('Correlation of Numeric Features with Price')
sns.heatmap(correlation,square = True)
7.4 每个数字特征得分布可视化
f = pd.melt(train_data, value_vars=numeric_features)
g = sns.FacetGrid(f, col="variable", col_wrap=2, sharex=False, sharey=False)
g = g.map(sns.distplot, "value")
- melt 函数的目的是将数据框从宽格式(wide format)转换为长格式(long format),转换后的数据框 f 包含三列,分别是 “variable”(变量,即原数据框的列名)、“value”(值,即原数据框中的数值)和 “ID”(如果有的话,用于标识原数据的行)
- 注:宽格式一般包含多个列,长格式就是把多个列都放到一列显示
- FacetGrid 函数创建一个多面板图。f 是转换后的数据框,col=“variable” 表示按照 “variable” 列的不同取值(即原数据框的列名)分割多个面板,col_wrap=2 表示每行最多显示两个面板,sharex=False 和 sharey=False 分别表示每个面板的 x 轴和 y 轴是独立的,即它们的刻度不共享。
- 第三行使用 map 方法将 Seaborn 的 distplot 函数应用到每个面板上。distplot 用于绘制单变量的直方图,并可选地包含核密度估计曲线。这里,“value” 列中的数值将作为参数传递给 distplot,从而在每个面板上绘制对应变量的分布图
7.5 数字特征相互之间的关系可视化
sns.set() # 用于设置默认的图形样式和颜色主题
columns = ['price', 'v_12', 'v_8' , 'v_0', 'power', 'v_5', 'v_2', 'v_6', 'v_1', 'v_14']
# pairplot 函数,用于绘制一组变量之间的散点图矩阵
# size=2: 指定每个子图的大小为2。
# kind='scatter': 指定绘制散点图。
# diag_kind='kde': 对角线上的图形使用核密度图(Kernel Density Estimate)来表示单变量分布
sns.pairplot(Train_data[columns],size = 2 ,kind ='scatter',diag_kind='kde')
plt.show()
7.6 多变量之间的关系可视化
# 创建了一个包含多个子图的图形,按照5行2列的网格排列,figsize 参数指定了整个图形的大小
fig, ((ax1, ax2), (ax3, ax4), (ax5, ax6), (ax7, ax8), (ax9, ax10)) = plt.subplots(nrows=5, ncols=2, figsize=(24, 20))
# 这一行创建了一个新的数据框 v_12_scatter_plot,其中包含目标变量 'price' 和特征 'v_12'。pd.concat 函数用于在水平方向上连接两个数据框
v_12_scatter_plot = pd.concat([y_train,train_data['v_12']],axis = 1)
# 在第一个子图 ax1 上绘制了 'v_12' 与 'price' 的散点图和线性回归拟合线
sns.regplot(x='v_12',y = 'price', data = v_12_scatter_plot,scatter= True, fit_reg=True, ax=ax1)
# x='v_12', y='price': 指定 x 轴和 y 轴的变量。
# data=v_12_scatter_plot: 指定绘图数据。
# scatter=True: 表示显示散点图。
# fit_reg=True: 表示显示线性回归拟合线。
# ax=ax1: 指定要在哪个子图上进行绘制,这里是第一个子图 ax1
v_8_scatter_plot = pd.concat([Y_train,Train_data['v_8']],axis = 1)
sns.regplot(x='v_8',y = 'price',data = v_8_scatter_plot,scatter= True, fit_reg=True, ax=ax2)
这里仅展示两个:
8. 用ydata_profiling生成数据报告
import pandas as pd
from ydata_profiling import ProfileReport
train_path="..."
train_data=pd.read_csv(train_path,sep=' ')
pfr = ProfileReport(train_data)
pfr.to_file("./example.html")
9. 总结
数据探索有利于我们发现数据的一些特性,数据之间的关联性,对于后续的特征构建是很有帮助的。
-
对于数据的初步分析(直接查看数据,或.sum(), .mean(),.descirbe()等统计函数)可以从:样本数量,训练集数量,是否有时间特征,是否是时许问题,特征所表示的含义(非匿名特征),特征类型(字符类似,int,float,time),特征的缺失情况(注意缺失的在数据中的表现形式,有些是空的有些是”NAN”符号等),特征的均值方差情况。
-
分析记录某些特征值缺失占比30%以上样本的缺失处理,有助于后续的模型验证和调节,分析特征应该是填充(填充方式是什么,均值填充,0填充,众数填充等),还是舍去,还是先做样本分类用不同的特征模型去预测。
-
对于异常值做专门的分析,分析特征异常的label是否为异常值(或者偏离均值较远或者事特殊符号),异常值是否应该剔除,还是用正常值填充,是记录异常,还是机器本身异常等。
-
对于Label做专门的分析,分析标签的分布情况等。
-
进步分析可以通过对特征作图,特征和label联合做图(统计图,离散图),直观了解特征的分布情况,通过这一步也可以发现数据之中的一些异常值等,通过箱型图分析一些特征值的偏离情况,对于特征和特征联合作图,对于特征和label联合作图,分析其中的一些关联性。
-
分析的时候,训练集,测试集,标签我们都要做相应的处理和分析,上述只展示了训练集,望周知