带最佳拟合线(the line of best fit)的散点图

散点图在机器学习中,能够指导数据预处理和模型使用

  1. 趋势明显有线性关系

    如果是特征与特征间的线性关系,要去共线性
    如果是特征与标签间的线性关系,则使用线性模型

  1. 线性趋势不明显,含噪音或错误

    基于现在的分布,寻找最佳拟合线
    最佳拟合线(或趋势线):最能代表散点图上的数据的直线

    1. 这条线线可以通过一些散点,不通过任何散点或通过所有散点
    2. 拟合线可以找出并不太明显的趋势

03 拟合线的散点图2.png

横坐标:发动机排量(L)
总坐标:公路里程/加仑
图例:汽缸数量

如何绘制最佳拟合线

绘制最佳拟合线的函数

sns.lmplot()
lmplot()是seaborn中最常用的函数之一,可以绘制出数据点的最佳拟合线。

  1. import numpy as np
  2. import pandas as pd
  3. import matplotlib as mpl
  4. import matplotlib.pyplot as plt
  5. import seaborn as sns
  6. %matplotlib inline
  1. X1 = np.linspace(0, 10, 50)
  2. y = 2 * X1 + 5 + np.random.randn(50) * 10 # 加上随机数,模拟现实收集数据时的噪音
  3. plt.scatter(X1, y)
  1. <matplotlib.collections.PathCollection at 0x15fcd9a9b38>

output_6_1.png

  1. data = pd.DataFrame({'X1':X1, 'y':y}) # 将所有的数据放入DataFrame
  2. gridobj = sns.lmplot('X1', 'y', data = data) # 必须通过 data + 字符串 的方式来读取数据

output_7_0.png

事实上,sns.lmplot()功能复杂,参数丰富(39个),
我们可以通过它创造一个在数据集的不同子集上拟合回归模型的便捷界面,对于有众多分类特征的回归类数据非常有效。

  1. # 创建分类型特征
  2. X2 = [0] * 10 + [1] * 40
  3. type(X2)
  1. list
  1. data = pd.DataFrame({'X1':X1, 'X2':X2, 'y':y}) # 将所有的数据放入DataFrame
  2. gridobj = sns.lmplot('X1', 'y'
  3. , data = data
  4. , hue = 'X2' # hue:色调,类似于参数c
  5. , legend = False # 关闭图例,因为固定显示分类特征的名称,读图不便
  6. )
  7. plt.legend(['类别0', '类别1'], fontsize = 16)
  8. # 解决中文乱码问题
  9. plt.rcParams['font.sans-serif']=['SimHei']
  10. plt.rcParams['axes.unicode_minus']=False
  11. # 无法在matpotlib和seaborn中显示中文或者负号的问题:https://www.jianshu.com/p/914b5f4ce6bf

output_10_0.png

重要参数

x, y, data:横坐标,纵坐标,数据
hue:取出数据集的子集,对数据集进行分类。分成多少类,就生成多少条拟合线
legend:是否显示图例

更多参数见:https://seaborn.pydata.org/generated/seaborn.lmplot.html

绘制图像

  1. df = pd.read_csv('https://raw.githubusercontent.com/selva86/datasets/master/mpg_ggplot2.csv')
  1. df.head()
manufacturer model displ year cyl trans drv cty hwy fl class
0 audi a4 1.8 1999 4 auto(l5) f 18 29 p compact
1 audi a4 1.8 1999 4 manual(m5) f 21 29 p compact
2 audi a4 2.0 2008 4 manual(m6) f 20 31 p compact
3 audi a4 2.0 2008 4 auto(av) f 21 30 p compact
4 audi a4 2.8 1999 6 auto(l5) f 16 26 p compact
  1. df.info()
  1. <class 'pandas.core.frame.DataFrame'>
  2. RangeIndex: 234 entries, 0 to 233
  3. Data columns (total 11 columns):
  4. manufacturer 234 non-null object
  5. model 234 non-null object
  6. displ 234 non-null float64
  7. year 234 non-null int64
  8. cyl 234 non-null int64
  9. trans 234 non-null object
  10. drv 234 non-null object
  11. cty 234 non-null int64
  12. hwy 234 non-null int64
  13. fl 234 non-null object
  14. class 234 non-null object
  15. dtypes: float64(1), int64(4), object(6)
  16. memory usage: 20.2+ KB
  1. df.shape
  1. (234, 11)
  1. df.columns
  1. Index(['manufacturer', 'model', 'displ', 'year', 'cyl', 'trans', 'drv', 'cty',
  2. 'hwy', 'fl', 'class'],
  3. dtype='object')
  1. name = ["汽车制造商","型号名称","发动机排量(L)","制造年份","气缸数量","手动/自动"
  2. ,"驱动类型","城市里程/加仑","公路里程/加仑","汽油种类","车辆种类"]
  3. #驱动类型:四轮,前轮,后轮
  4. #能源种类:汽油,柴油,用电等等
  5. #车辆种类:皮卡,SUV,小型,中型等等
  6. #城市里程/加仑,公路里程/加仑:表示使用没加仑汽油能够跑的英里数,所以这个数值越大代表汽车越节能
  1. [*zip(df.columns.values, np.array(name))]
  1. [('manufacturer', '汽车制造商'),
  2. ('model', '型号名称'),
  3. ('displ', '发动机排量(L)'),
  4. ('year', '制造年份'),
  5. ('cyl', '气缸数量'),
  6. ('trans', '手动/自动'),
  7. ('drv', '驱动类型'),
  8. ('cty', '城市里程/加仑'),
  9. ('hwy', '公路里程/加仑'),
  10. ('fl', '汽油种类'),
  11. ('class', '车辆种类')]
  1. # 准备数据
  2. df_select = df.loc[df.cyl.isin([4, 8]), :]
  3. # isin方法:a.isin(x),表示判断x是否在序列a中存在,若存在则返回True,若不存在则返回False
  4. # 绘制图像
  5. sns.set_style('white') # 设立风格
  6. gridobj = sns.lmplot('displ' # 发动机排量
  7. , 'hwy' # 公路里程/加仑
  8. , data = df_select
  9. , hue = 'cyl' # 分类/子集,汽缸数量
  10. , legend = False
  11. , height = 8 # 图像的高度(纵向,也叫做宽度)
  12. , aspect = 1.6 # 图像的纵横比,因此 aspect * height = 每个图像的长度(横向),单位为英寸
  13. , palette = 'winter' # 色板
  14. , scatter_kws = dict(s = 60, linewidth = .7, edgecolors = 'black') # 散点的其他参数
  15. )
  16. plt.legend(['汽缸数量:4', '汽缸数量:8'], fontsize = 16)
  17. gridobj.set(xlim=(0.5, 7.5), ylim=(0, 50))
  18. plt.xlabel('发动机排量(L)', fontsize = 20)
  19. plt.ylabel('公路里程/加仑', fontsize = 20)
  20. plt.xticks(fontsize = 16)
  21. plt.yticks(fontsize = 16)
  22. plt.title('Scatterplot with line of best fit grouped by number of cylinders', fontsize = 20)
  23. # 解决中文乱码问题
  24. plt.rcParams['font.sans-serif']=['SimHei']
  25. plt.rcParams['axes.unicode_minus']=False

output_20_0.png

  1. df_select.head()
manufacturer model displ year cyl trans drv cty hwy fl class
0 audi a4 1.8 1999 4 auto(l5) f 18 29 p compact
1 audi a4 1.8 1999 4 manual(m5) f 21 29 p compact
2 audi a4 2.0 2008 4 manual(m6) f 20 31 p compact
3 audi a4 2.0 2008 4 auto(av) f 21 30 p compact
7 audi a4 quattro 1.8 1999 4 manual(m5) 4 18 26 p compact
  1. df.cyl.value_counts()
  1. 4 81
  2. 6 79
  3. 8 70
  4. 5 4
  5. Name: cyl, dtype: int64
  1. df_select.cyl.value_counts()
  1. 4 81
  2. 8 70
  3. Name: cyl, dtype: int64

进一步探索

当分类增多时,如一图四线,容易混乱

如何让每个类型的散点显示在不同的图像上?

重要参数

col:表示按照这个特征中的分类绘制图像,并且一个类别绘制一张图一条拟合线,排成一行
col_wrap:当参数col有效的时候有效,表示每行最多显示col_wrap个图

  1. large = 22; med = 16; small = 12
  2. params = {'axes.titlesize': large, #子图上的标题字体大小
  3. 'legend.fontsize': med, #图例的字体大小
  4. 'figure.figsize': (16, 10), #图像的画布大小
  5. 'axes.labelsize': med, #标签的字体大小
  6. 'xtick.labelsize': med, #x轴上的标尺的字体大小
  7. 'ytick.labelsize': med, #y轴上的标尺的字体大小
  8. 'figure.titlesize': large} #整个画布的标题字体大小
  9. plt.rcParams.update(params) #设定各种各样的默认属性
  10. gridobj = sns.lmplot('displ' # 发动机排量
  11. , 'hwy' # 公路里程/加仑
  12. , data = df
  13. , hue = 'cyl' # 分类/子集,汽缸数量
  14. , legend = True
  15. , height = 8 # 图像的高度(纵向,也叫做宽度)
  16. , aspect = 1.6 # 图像的纵横比,因此 aspect * height = 每个图像的长度(横向),单位为英寸
  17. , palette = 'tab10' # 色板
  18. , scatter_kws = dict(s = 60, linewidth = .7, edgecolors = 'black') # 散点的其他参数
  19. )
  20. gridobj.set(xlim=(0.5, 7.5), ylim=(0, 50))
  21. plt.xlabel('发动机排量(L)', fontsize = 20)
  22. plt.ylabel('公路里程/加仑', fontsize = 20)
  23. plt.xticks(fontsize = 16)
  24. plt.yticks(fontsize = 16)
  25. # 解决中文乱码问题
  26. plt.rcParams['font.sans-serif']=['SimHei']
  27. plt.rcParams['axes.unicode_minus']=False

output_26_0.png

  1. gridobj = sns.lmplot('displ' # 发动机排量
  2. , 'hwy' # 公路里程/加仑
  3. , data = df
  4. , hue = 'cyl' # 分类/子集,汽缸数量
  5. , legend = True
  6. , height = 8 # 图像的高度(纵向,也叫做宽度)
  7. , aspect = 1.6 # 图像的纵横比,因此 aspect * height = 每个图像的长度(横向),单位为英寸
  8. , palette = 'tab10' # 色板
  9. , col = 'cyl' # 表示按照这个特征中的分类绘制图像,并且一个类别绘制一张图一条拟合线,排成一行
  10. , col_wrap = 2 # 当参数col有效时,表示每行最多显示col_wrap个图
  11. , scatter_kws = dict(s = 60, linewidth = .7, edgecolors = 'black') # 散点的其他参数
  12. )
  13. gridobj.set(xlim=(0.5, 7.5), ylim=(0, 50), xlabel = '排量', ylabel = '公路里程/加仑')
  14. # 解决中文乱码问题
  15. plt.rcParams['font.sans-serif']=['SimHei']
  16. plt.rcParams['axes.unicode_minus']=False

output_27_0.png

解读图像

  1. sns.set_style('white') # 设立风格
  2. gridobj = sns.lmplot('displ' # 发动机排量
  3. , 'hwy' # 公路里程/加仑
  4. , data = df_select
  5. , hue = 'cyl' # 分类/子集,汽缸数量
  6. , legend = False
  7. , height = 8 # 图像的高度(纵向,也叫做宽度)
  8. , aspect = 1.6 # 图像的纵横比,因此 aspect * height = 每个图像的长度(横向),单位为英寸
  9. , palette = 'winter' # 色板
  10. , scatter_kws = dict(s = 60, linewidth = .7, edgecolors = 'black') # 散点的其他参数
  11. )
  12. plt.legend(['汽缸数量:4', '汽缸数量:8'], fontsize = 16)
  13. gridobj.set(xlim=(0.5, 7.5), ylim=(0, 50))
  14. plt.xlabel('发动机排量(L)', fontsize = 20)
  15. plt.ylabel('公路里程/加仑', fontsize = 20)
  16. plt.xticks(fontsize = 16)
  17. plt.yticks(fontsize = 16)
  18. plt.title('Scatterplot with line of best fit grouped by number of cylinders', fontsize = 20)
  19. # 解决中文乱码问题
  20. plt.rcParams['font.sans-serif']=['SimHei']
  21. plt.rcParams['axes.unicode_minus']=False

output_29_0.png

  1. 从整体上看:

对于汽缸数量为4的汽车来说,发动机排量越大,公路里程/加仑y越小,即发动机排量越大,汽车越耗油
对于汽缸数量为8的汽车来说,发动机排量越大,公路里程/加仑y越大,即发动机排量越大,汽车越省油

  1. 单独看发动机排量:

汽缸数量为4的汽车,发动机排量都比较小,从而普遍省油
汽缸数量为8的汽车,发动机排量都比较大,可能因为这样更省油

  1. 结论:

汽车生产要遵循的一条规则:省油

  1. 当发现了两个变量间的关系,此时再通过相关的第三变量(维度)介入分析,可以获得层次更丰富的信息
  2. 第三变量(维度)的介入,避免被大量的数据点展现的 趋势 或 无趋势 混淆