天池二手车价格预测比赛(一)——数据分析步骤
数据分析的整个流程
- 一、数据读取及浏览
- 1.数据读取
- a.设置展示的行数和列数
- b.读取数据
- 2.数据概览
- a.观察部分数据
- b.查看数据类型和缺失值情况
- c.查看数据的取值是否合理(异常值)以及数据的大致分布
- 二、EDA-探索性数据分析
- 1.数据缺失和异常分析
- a.查看每个特征的缺失情况
- b.异常值检测
- c.无用特征的识别和删除
- 2.查看预测目标的分布
- a.统计每个取值及其数量
- b. 用各种不同的分布去拟合数据
- c.查看偏度和峰度:skewness and kurtosis
- d.查看预测目标的分布——频数分布直方图
- 3.数值型特征分析
- a.数值型特征间的相关性分析——热力图
- b.查看几个数值型特征的偏度和峰度——格式化排版输出
- c.每个数值型特征的分布可视化——数据转换 pd.melt 和 sns.FacetGrid 绘图
- d. 数值型特征之间的关系可视化——散点图和kde图
- e.每个数值型特征和预测目标price的回归关系——拟合回归图
- 4.类别特征分析
- a.类别特征的nunique数量
- b.类别特征的箱线图
- c.类别特征的小提琴图
- d.类别特征的柱形图
- e.类别特征的频数分布图
- 5.用pandas_profiling生成一个较为全面的数据分析报告
一、数据读取及浏览
1.数据读取
a.设置展示的行数和列数
pd.set_option('max_columns', None)
pd.set_option('max_rows', 100)
b.读取数据
# 部分默认参数
pd.read_csv(sep=',', index_col=None, encoding=None)
2.数据概览
a.观察部分数据
# append方法
Train_data.head(3).append(Train_data.tail(3)).append(Train_data.sample(3))
Test_data.head(3).append(Test_data.tail(3)).append(Test_data.sample(3))
b.查看数据类型和缺失值情况
Train_data.info()
Test_data.info()
c.查看数据的取值是否合理(异常值)以及数据的大致分布
# 只针对数值型数据(int 和 float),不包括object;缺失数据是不计数、不统计的
Train_data.describe()
Test_data.describe()
二、EDA-探索性数据分析
养成看数据集的head()以及shape的习惯,防止某一步出错造成的连串错误!!!
训练集和测试集都看!!!
1.数据缺失和异常分析
a.查看每个特征的缺失情况
# isnull对df操作,sum的默认axis=0,然后降序排列
missing_train = Train_data.isnull().sum().sort_values(ascending=False)
# Series的条件过滤,只展示有缺失值的特征
missing_train[missing_train > 0]
# 没必要的,缺失值的pandas作图,条形图
missing_train[missing_train > 0].plot.bar()
缺失值分析的目的在于:判断缺失值(nan)的个数,如果很小一般选择填充,如果使用lgb等树模型可以直接空缺,让树自己去优化,但如果nan存在的过多、可以考虑删掉;一般的百分比界限是??
新的缺失值分析库,分析缺失值
import missingno as msno # 随机取些数据,可视化看下缺省值,白线越多说明缺失越严重,最右边的折线不知道是什么含义,百度也没找到
msno.matrix(Train_data.sample(250))
# 随机取些数据,计算每个特征的非缺失数量
msno.bar(Train_data.sample(1000))
缺失值里面,如果有的缺失值不是nan,而是特殊的符号,如’-’,那么isnull()函数是无法检测出来的!!!
b.异常值检测
本文中除了notRepairedDamage 特征为object类型外,其他都为数字,对该特征进行查看
统计特征的每个取值及其数量
Train_data['notRepairedDamage'].value_counts().sort_values(ascending=False)
异常值的替换:‘ - ’也为空缺值,对其进行替换,训练集和测试集都处理
Train_data['notRepairedDamage'].replace('-', np.nan, inplace=True)
Test_data['notRepairedDamage'].replace('-', np.nan, inplace=True)
c.无用特征的识别和删除
只需要看训练数据的,但是测试数据对应的也要删除!!
#以下两个类别特征严重倾斜,一般不会对预测有什么帮助,继续挖掘的意义不大,故先删掉
Train_data["seller"].value_counts() # 两个特征值的数量比是149999:1
Train_data["offerType"].value_counts() # 只有一个特征值del Train_data["seller"]
del Train_data["offerType"]
del Test_data["seller"]
del Test_data["offerType"]
2.查看预测目标的分布
a.统计每个取值及其数量
Train_data['price'].value_counts().sort_values(ascending=False)
b. 用各种不同的分布去拟合数据
需要累积分布经验
import scipy.stats as sty = Train_data['price']
# 无界约翰逊分布
plt.figure(1)
plt.title('Johnson SU')
sns.distplot(y, kde=False, fit=st.johnsonsu)
# 正态分布
plt.figure(2)
plt.title('Normal')
sns.distplot(y, kde=False, fit=st.norm)
# 取对数的正态分布
plt.figure(3)
plt.title('Log Normal')
sns.distplot(y, kde=False, fit=st.lognorm)
预测价格不服从正态分布,所以在进行回归之前,它必须进行特征转换。虽然对数变换做得很好,但最佳拟合是无界约翰逊分布
c.查看偏度和峰度:skewness and kurtosis
# Series的 .skew() .kurt()方法,返回单个值
sns.distplot(Train_data['price']);
print("Skewness: %f" % Train_data['price'].skew())
print("Kurtosis: %f" % Train_data['price'].kurt())# 查看整个df的偏度和峰度,返回Series,并绘制出分布图
Train_data.skew()
sns.distplot(Train_data.skew(),color='blue',axlabel ='Skewness')
知道偏度和峰度后有何操作??
d.查看预测目标的分布——频数分布直方图
# 分布不均匀,尝试进行log变换
plt.hist(Train_data['price'], orientation = 'vertical',histtype = 'bar', color ='red')
plt.show()
频数大于20000的值极少,其实这里也可以把这些当作特殊得值(异常值)直接用填充或者删掉。
plt.hist(np.log(Train_data['price']), orientation = 'vertical', histtype = 'bar', color ='red')
plt.show()
log变换后,预测价格的分布较均匀,可以进行log变换进行预测,这也是预测问题常用的trick,有无界约翰逊变换吗???
将预测目标单独取出
Y_train = Train_data['price']
3.数值型特征分析
将类别特征和数值型特征分开
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']
a.数值型特征间的相关性分析——热力图
类别特征也可以计算,但是不适合
数值型特征包括预测目标
price_numeric = Train_data[numeric_features]
correlation = price_numeric.corr()
correlation['price'].sort_values(ascending = False)# 只查看大于0.2的特征,bool索引
# correlation['price'][correlation['price'] > 0.2].sort_values(ascending = False)
绘制热力图
f, ax = plt.subplots(figsize = (15, 15))
plt.title('Correlation of Numeric Features with Price', y=1, size=16)
sns.heatmap(correlation, square=True, vmax=0.8, annot=True, fmt='.3f')
b.查看几个数值型特征的偏度和峰度——格式化排版输出
格式化输出字符串
# 只占字符 (:10)
# 居中对齐 (:^)
# 靠左对齐 (:<)
# 靠右对齐 (:>)
for col in numeric_features:# 格式占12个字符,加个 ^ 后居中对齐print('{:^12}'.format(col), # 格式占5个字符,且保留2位小数,加个 < 后左对齐'Skewness: {:<10.2f}'.format(Train_data[col].skew()),# 格式占6个字符,且保留2位小数,加个 > 后右对齐'Kurtosis: {:>7.2f}'.format(Train_data[col].kurt()) )
c.每个数值型特征的分布可视化——数据转换 pd.melt 和 sns.FacetGrid 绘图
数据转换 pd.melt:将特征名称和特征取值分成两列
# value_vars:需要转换的列名,如果剩下的列全部都要转换,就不用写了
f = pd.melt(Train_data, value_vars=numeric_features)
先sns.FacetGrid画出轮廓, 然后用map填充内容(直方图,也可以是其他图形)
# col是需要被统计的变量名称:在这里为variable;col_wrap每列两个
g = sns.FacetGrid(f, col="variable", col_wrap=2, sharex=False, sharey=False)
# 统计特征在哪些变量(value是)的取值,distplot是统计这些取值的分布
g = g.map(sns.distplot, "value")
sns.FacetGrid就是一次性画多个图。对sns.FacetGrid的多变量关系的分析参考:https://www.jianshu.com/p/6a210c2ad3ad
d. 数值型特征之间的关系可视化——散点图和kde图
图画着需要1-2分钟。kind='reg’ 既包括 kind=‘scatter’ 的散点图,也多了个回归线,但是速度超级慢,10min都没好
# 通过set()重置默认的绘图参数
sns.set()
columns = ['price', 'v_12', 'v_8' , 'v_0', 'power', 'v_5', 'v_2', 'v_6', 'v_1', 'v_14']
# 对称线上的图为kde
sns.pairplot(Train_data[columns], size=2, kind='scatter', diag_kind='kde')
plt.show()
可以看出匿名特征相对分布均匀
e.每个数值型特征和预测目标price的回归关系——拟合回归图
我的简化版——比较省代码和内存空间
# 注意如何设置画板的!!
fig, axes= plt.subplots(nrows=5, ncols=2, figsize=(20, 20))
cols = [ 'v_12', 'v_8' , 'v_0', 'power', 'v_5', 'v_2', 'v_6', 'v_1', 'v_14', 'v_13']
for i, ax_pair in enumerate(axes):sns.regplot(x=cols[2*i], y='price', data=Train_data, scatter=True, fit_reg=True, ax=ax_pair[0])sns.regplot(x=cols[2*i + 1], y='price', data=Train_data, scatter=True, fit_reg=True, ax=ax_pair[1])
9个特征,所以最后一个图是空的,不加try的话会出现list index out of range,也可以把price加在最后代替前面的 v_13
fig, axes= plt.subplots(nrows=5, ncols=2, figsize=(24, 20))
features_numeric = ['v_12', 'v_8' , 'v_0', 'power', 'v_5', 'v_2', 'v_6', 'v_1', 'v_14']
for i, ax_pair in enumerate(axes):try:sns.regplot(x=features_numeric[2*i], y='price', data=Train_data, scatter=True, fit_reg=True, ax=ax_pair[0])sns.regplot(x=features_numeric[2*i + 1], y='price', data=Train_data, scatter=True, fit_reg=True, ax=ax_pair[1])# 没有用,图都在最后一起画,所以开头全是空行# print('\n')except:pass
除了enumerate,zip() 也可以压缩多个迭代目标然后进行迭代,尝试过把axes 和 features_numeric压缩在一起,但是axes的长度是5,而后者的长度为9,会被截断
4.类别特征分析
categorical_features = ['name', 'model', 'brand', 'bodyType', 'fuelType', 'gearbox', 'notRepairedDamage', 'regionCode']
a.类别特征的nunique数量
for fea in categorical_features:# unique方法显示所有的特征值(缺失值也会被计数), unique计算不同特征值的数量(缺失值不会被计数)print('{:20} 特征值数量 {:<10}'.format(fea, Train_data[fea].nunique()) )# 缺失值不会被计数# print(Train_data[cat_fea].value_counts(), '\n')
b.类别特征的箱线图
df.isnull().any() 会判断哪些”列”存在缺失值
判断某列是否存在缺失值: all() 要求全部缺失,any() 要求一个就可以,返回True或者False,作用的对象都是元素值True
# 结果是bool Series
Train_data.isnull().any()
因为 name和 regionCode 的类别太稀疏了,这里只绘制不稀疏的几个类别型特征
categorical_features = ['model', 'brand', 'bodyType', 'fuelType', 'gearbox', 'notRepairedDamage']
for c in categorical_features:# 将特征转换为 ‘分类型’ 特征Train_data[c] = Train_data[c].astype('category') # 针对有缺失值的特征if Train_data[c].isnull().any():# category类型数据加入新的 ‘分类型’ 特征值 MISSING,必须先加到 .category容器里Train_data[c] = Train_data[c].cat.add_categories(['MISSING'])Train_data[c] = Train_data[c].fillna('MISSING')
使用Category类型数据的一个好处就是:相比于object类型,可以很好的节省在时间和空间的消耗。
Category类型数据插入新的值(包括缺失值填充),必须将这个值添加到.categories的容器中,然后再添加值;否则报错: fill value must be in categories,如:
# 报错: fill value must be in categories
# Train_Data = Train_data.copy()
# Train_Data['gearbox'] = Train_Data['gearbox'].astype('category')
# Train_Data['gearbox'] = Train_Data['gearbox'].fillna('MISSING')
绘制箱线图,箱线图的横坐标是每个特征variable对应的不同特征值value,纵坐标是price的取值
# 修改箱线图的参数配置
def boxplot(x, y, **kwargs):sns.boxplot(x=x, y=y)# 逆时针旋转90度x = plt.xticks(rotation=90)# id_vars不进行转换的特征:特征名和特征值不需要分开
f = pd.melt(Train_data, id_vars=['price'], value_vars=categorical_features)
# 针对variable的不同特征值(就是每个特征名称),分别作图!!
g = sns.FacetGrid(f, col="variable", col_wrap=2, sharex=False, sharey=False, size=5)
g = g.map(boxplot, "value", "price")
单独箱线图的绘制方法,sns.FacetGrid就是一次性画多个图
sns.boxplot(x='gearbox', y='price', data=Train_data)
c.类别特征的小提琴图
target = 'price'
for cat in categorical_features :sns.violinplot(x=cat, y=target, data=Train_data)plt.show()
d.类别特征的柱形图
直接绘图是不统计缺失值的,但是统计异常值 ‘-’
# 修改条形图的绘图参数
def bar_plot(x, y, **kwargs):sns.barplot(x=x, y=y)# 逆时针旋转45°x = plt.xticks(rotation=45)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, size=5)
g = g.map(bar_plot, "value", "price")
e.类别特征的频数分布图
横坐标是特征值,纵坐标是特征值的数量
def count_plot(x, **kwargs):sns.countplot(x=x)x = plt.xticks(rotation=45)f = pd.melt(Train_data, value_vars=categorical_features)
g = sns.FacetGrid(f, col="variable", col_wrap=2, sharex=False, sharey=False, size=5)
g = g.map(count_plot, "value")
单独的频数分布图绘制
sns.countplot(x='gearbox', data=Train_data)
单独的频数分布图绘制——和其他变量相关联
sns.countplot(x='gearbox', hue='notRepairedDamage', data=Train_data)
5.用pandas_profiling生成一个较为全面的数据分析报告
强烈不推荐,cpu占用100%,而且一直卡在45%进行不下去,分析时间超长
import pandas_profilingfile = pandas_profiling.ProfileReport(Train_data)
file.to_file("./example.html")
参考:
[1]:https://tianchi.aliyun.com/notebook-ai/detail?spm=5176.12586969.1002.3.1cd8593a8bkYih&postId=95457
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
