欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

数据挖掘学习小组:Task2.数据分析

程序员文章站 2024-02-12 12:48:10
...

Task2.数据分析

EDA(探索性数据分析)

探索性数据分析(Exploratory Data Analysis,简称EDA),是指对已有的数据(特别是调查或观察得来的原始数据)在尽量少的先验假定下进行探索,通过作图、制表、方程拟合、计算特征量等手段探索数据的结构和规律的一种数据分析方法。特别是当我们对面对大数据时代到来的时候,各种杂乱的“脏数据”,往往不知所措,不知道从哪里开始了解目前拿到手上的数据的时候,探索性数据分析就非常有效。
流程

  1. 载入各种数据科学以及可视化库:
    • 数据科学库 pandas、numpy、scipy;
    • 可视化库 matplotlib、seabon;
    • 其他;
  2. 载入数据
    • 载入训练集和测试集;
    • 简略观察数据(head()+shape);
  3. 数据总览:
    • 通过describe()来熟悉数据的相关统计量
    • 通过info()来熟悉数据类型
  4. 判断数据缺失和异常
    • 查看每列的存在nan情况
    • 异常值检测
  5. 了解预测值的分布
    • 总体分布概况(*约翰逊分布等)
    • 查看skewness and kurtosis
    • 查看预测值的具体频数
  6. 特征分为类别特征和数字特征,并对类别特征查看unique分布
  7. 数字特征分析
    • 相关性分析
    • 查看几个特征得 偏度和峰值
    • 每个数字特征得分布可视化
    • 数字特征相互之间的关系可视化
    • 多变量互相回归关系可视化
  8. 类型特征分析
    • unique分布
    • 类别特征箱形图可视化
    • 类别特征的小提琴图可视化
    • 类别特征的柱形图可视化类别
    • 特征的每个类别频数可视化(count_plot)
  9. 用pandas_profiling生成数据报告

Numpy

  • 之前没有系统学习过numpy,pandas等工具的使用,借此机会重新学习了一下。
  1. 几个常用的生成方法:

    a = np.array(1,2,3,4)    # WRONG
    a = np.array([1,2,3,4])  # RIGHT
    np.zeros( (3,4) )
    np.ones( (2,3,4), dtype=np.int16)   # dtype can also be specified
    np.empty( (2,3) )      # uninitialized, output may vary
    
  2. 当arange与浮点参数一起使用时,由于有限的浮点精度,通常不可能预测所获得的元素的数量。出于这个原因,通常最好使用linspace函数来接收我们想要的元素数量的函数,而不是步长(step):

    >>> from numpy import pi
    >>> np.linspace( 0, 2, 9 )                 # 9 numbers from 0 to 2
    array([ 0.  ,  0.25,  0.5 ,  0.75,  1.  ,  1.25,  1.5 ,  1.75,  2.])
    >>> x = np.linspace( 0, 2*pi, 100 )        # useful to evaluate function at lots of points
    >>> f = np.sin(x)
    
  3. 当您打印数组时,NumPy以与嵌套列表类似的方式显示它,但具有以下布局:

  • 最后一个轴从左到右打印,
  • 倒数第二个从上到下打印,
  • 其余部分也从上到下打印,每个切片用空行分隔。
  1. 然后将一维数组打印为行,将二维数据打印为矩阵,将三维数据打印为矩数组表。

    >>> a = np.arange(6)                         # 1d array
    >>> print(a)
    [0 1 2 3 4 5]
    >>>
    >>> b = np.arange(12).reshape(4,3)           # 2d array
    >>> print(b)
    [[ 0  1  2]
     [ 3  4  5]
     [ 6  7  8]
     [ 9 10 11]]
    >>>
    >>> c = np.arange(24).reshape(2,3,4)         # 3d array
    >>> print(c)
    [[[ 0  1  2  3]
      [ 4  5  6  7]
      [ 8  9 10 11]]
     [[12 13 14 15]
      [16 17 18 19]
      [20 21 22 23]]]
    
  2. 与许多矩阵语言不同,乘积运算符*在NumPy数组中按元素进行运算。矩阵乘积可以使用@运算符(在python> = 3.5中)或dot函数或方法执行

  3. 某些操作(例如+=*=)会更直接更改被操作的矩阵数组而不会创建新矩阵数组

  4. 当使用不同类型的数组进行操作时,结果数组的类型对应于更一般或更精确的数组(称为向上转换的行为)

  5. 许多一元操作,例如计算数组中所有元素的总和,都是作为ndarray类的方法实现的

    >>> a = np.random.random((2,3))
    >>> a
    array([[ 0.18626021,  0.34556073,  0.39676747],
          [ 0.53881673,  0.41919451,  0.6852195 ]])
    >>> a.sum()
    2.5718191614547998
    >>> a.min()
    0.1862602113776709
    >>> a.max()
    0.6852195003967595
    

Pandas

  1. Series:

    In [3]: s = pd.Series([1, 3, 5, np.nan, 6, 8])
    In [4]: s
    Out[4]: 
    0    1.0
    1    3.0
    2    5.0
    3    NaN
    4    6.0
    5    8.0
    dtype: float64
    
  2. DataFrame:

    • 用含日期时间索引与标签的 NumPy 数组生成 DataFrame
    In [5]: dates = pd.date_range('20130101', periods=6)
        
    In [6]: dates
    Out[6]: 
    DatetimeIndex(['2013-01-01', '2013-01-02', '2013-01-03', '2013-01-04',
                   '2013-01-05', '2013-01-06'],
                  dtype='datetime64[ns]', freq='D')
    
    In [7]: df = pd.DataFrame(np.random.randn(6, 4), index=dates, columns=list('ABCD'))
        
    In [8]: df
    Out[8]: 
                       A         B         C         D
    2013-01-01  0.469112 -0.282863 -1.509059 -1.135632
    2013-01-02  1.212112 -0.173215  0.119209 -1.044236
    2013-01-03 -0.861849 -2.104569 -0.494929  1.071804
    2013-01-04  0.721555 -0.706771 -1.039575  0.271860
    2013-01-05 -0.424972  0.567020  0.276232 -1.087401
    2013-01-06 -0.673690  0.113648 -1.478427  0.524988	
    
    • 用 Series 字典对象生成 DataFrame
    In [9]: df2 = pd.DataFrame({'A': 1.,
       ...:                     'B': pd.Timestamp('20130102'),
       ...:                     'C': pd.Series(1, index=list(range(4)), dtype='float32'),
       ...:                     'D': np.array([3] * 4, dtype='int32'),
       ...:                     'E': pd.Categorical(["test", "train", "test", "train"]),
       ...:                     'F': 'foo'})
       ...: 
    
    In [10]: df2
    Out[10]: 
         A          B    C  D      E    F
    0  1.0 2013-01-02  1.0  3   test  foo
    1  1.0 2013-01-02  1.0  3  train  foo
    2  1.0 2013-01-02  1.0  3   test  foo
    3  1.0 2013-01-02  1.0  3  train  foo
    
  3. Pandas东西太多了,以后再补充吧,打卡要完不成了。

总览数据概况

  1. 要养成看数据集的head()以及shape的习惯,这会让你每一步更放心,导致接下里的连串的错误, 如果对自己的pandas等操作不放心,建议执行一步看一下,这样会有效的方便你进行理解函数并进行操作

  2. describe种有每列的统计量,个数count、平均值mean、方差std、最小值min、中位数25% 50% 75% 、以及最大值 看这个信息主要是瞬间掌握数据的大概的范围以及每个值的异常值的判断,比如有的时候会发现999 9999 -1 - 等,这些其实都是nan的另外一种表达方式,有的时候需要注意下

  3. info 通过info来了解数据每列的type,有助于了解是否存在除了nan以外的特殊符号异常

    可以发现除了notRepairedDamage 为object类型其他都为数字 这里我们把他的几个不同的值都进行显示就知道了 :Train_data['notRepairedDamage'].value_counts()

  4. 判断数据缺失或者异常:

    主要的目的在于 nan存在的个数是否真的很大,如果很小一般选择填充,如果使用lgb等树模型可以直接空缺,让树自己去优化,但如果nan存在的过多、可以考虑删掉

  5. 对于类别特征严重倾斜的,一般不会对预测有什么帮助,所以可以删掉。

了解预测值的分布

  • 用几种常见的分布进行拟合

    import scipy.stats as st
    y = 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)
    
  • 查看偏度(skewness)和锋度(kurtosis)

    sns.distplot(Train_data['price']);
    print("Skewness: %f" % Train_data['price'].skew())
    print("Kurtosis: %f" % Train_data['price'].kurt())
    
  • 查看预测值的具体频数

    plt.hist(Train_data['price'], orientation = 'vertical',histtype = 'bar', color ='red')
    plt.show()
    
  • log变换 z之后的分布较均匀,可以进行log变换进行预测,这也是预测问题常用的trick

    plt.hist(np.log(Train_data['price']), orientation = 'vertical',histtype = 'bar', color ='red') 
    plt.show()
    

特征分为类别特征和数字特征,并对类别特征查看unique分布

  • 关于Label Encoding 与 One-Hot Encoding的区别

    在机器学习中,通常需要对类别变量单独做处理,这是因为模型的输入项基本都需要是数值型变量,而因为类别变量本身不带数值属性,所以需要进行一层转换。常用的方法一般有两种:label encoding和one hot encoding,这两种方法在不同的模型和数据集上有不同意义。

    注:这里只谈分类模型。

    1、两类模型

    虽然大多数模型要求输入项是数值型变量,但其对数值的处理方式是完全不同的。有些模型的损失函数对数值大小是敏感的,即变量间的数值大小本身是有比较意义的,如逻辑回归,SVM等,我们暂将其称为A类模型;有些模型本身对数值变化不敏感,数值存在的意义更多的是为了排序,即0.1,0.2,0.3与1,2,3是没有区别的,这部分模型绝大部分是树模型,暂将其称为B类模型。

    2、两类类别变量

    类别变量再细分可以分成有序变量和无序变量,典型的有序变量就是学历,如博士研究生,硕士研究生,本科生等在业务含义上本身是有高低之分的。而无序变量在业务含义上是无序的,如品牌等。

    3、label encoding与one hot encoding

    label encoding是将类别变量中每一类别赋一数值,从而转换成数值型。比如有一列 [dog,cat,dog,mouse,cat],我们把其转换为[1,2,1,3,2]。这里就产生了一个奇怪的现象:dog和mouse的平均值是cat,所以label encoding最直观的缺点就是赋值难以解释,适用场景更窄。

    one hot encoding的优点就是它的值只有0/1,不同的类型存储在垂直的空间。缺点就是,当类别的数量很多时,特征空间会变得非常大。

    4、两类encoding的使用场景

    A类模型

    如果使用的是A类模型,所有的类别变量必须做one hot encoding,因为label encoding的赋值是没有数值含义的。

    但是对于类别很多的变量,做one hot encoding会使得生成的变量过于稀疏,所以这里有一些经验上的处理方式。优先考虑的是有没业务上类别合并的方法,如城市变量,可以依发展程度分为一线城市,二线城市等等;另外一种方法是只one hot出现次数最多的前n个类别,其他类别放在其他类的变量中;也可以利用y值(训练集)中positive rate做合并,不过容易出现过拟合的现象。

    B类模型

    如果使用的是B类模型,并且是有序变量,则优先使用label encoding,且赋值要与顺序一致。

    如果是无序变量,则两种方法在很多情况下差别不大,但是在实际使用中label encoding的效果一般要比one hot encoding要好。这是因为在树模型中,label encoding至少可以完成one hot encoding同样的效果,而多出来的那部分信息则是label encoding后的数值本身是有排序作用的,它可以起到类别变量合并的效果,这种效果在类别较多的变量中更明显。

    举个例子,在树模型中,有四个类别,dog,cat,mouse,horse,如果dog类的占比很少,但它的positive rate与cat是类似的,若在one hot encoding中,前三个类别都入模型了。如果我们做label encoding时,随机将dog,cat,mouse,horse分别赋值为[1,3,2,4],那么可以预见这个变量会分裂两次,达到上面one hot encoding的效果。但是如果我们将dog,cat,mouse赋值为[1,2,3,4],可能这个变量只会分裂1次,它将dog和cat分在一起,此时达到的效果可能与one hot encoding相当。

    但是在实际使用中,由于one hot encoding增加了变量的维度,在树模型中意味着更深的分裂,如果刚好树的深度限制在一数值时,dog这个类别可能不能入模型,其实意味着丢失了dog这一信息,而上面第二种label encoding没有这种问题,也意味着拟合的效果会更好,但是因为利用到了y值的信息,也容易发生过拟合。

    在Applied Predictive Modeling p372-p377讨论了一方面的问题,有兴趣的可以去翻阅,但是其中的Grouped Category与label encoding还是不完全一样的。

    参考:[关于Label Encoding 与 One-Hot Encoding的区别]: https://zhuanlan.zhihu.com/p/36804348

  • 特征区分

    # 这个区别方式适用于没有直接label coding的数据
    # 数字特征
    # numeric_features = Train_data.select_dtypes(include=[np.number])
    # numeric_features.columns
    # # 类型特征
    # categorical_features = Train_data.select_dtypes(include=[np.object])
    # categorical_features.columns
    
    # 这里不适用,需要人为根据实际含义来区分
    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',]
    
  • 特征nunique分布

    # 特征nunique分布
    for cat_fea in categorical_features:
        print(cat_fea + "的特征分布如下:")
        print("{}特征有个{}不同的值".format(cat_fea, Train_data[cat_fea].nunique()))
        print(Train_data[cat_fea].value_counts())
    
  • 相关性分析

    ## 1) 相关性分析
    price_numeric = Train_data[numeric_features]
    correlation = price_numeric.corr()
    print(correlation['price'].sort_values(ascending = False),'\n')
    
  • 查看几个特征的偏度和峰度

    ## 2) 查看几个特征的偏度和峰度
    for col in numeric_features:
        print('{:15}'.format(col), 
              'Skewness: {:05.2f}'.format(Train_data[col].skew()) , 
              '   ' ,
              'Kurtosis: {:06.2f}'.format(Train_data[col].kurt())  
             )
    

    关于偏度和锋度

    • **偏度:**偏度是数据的不对称程度。无论偏度值是 0、正数还是负数,都显示有关数据分布形状的信息。(对称或非偏斜分布,正偏斜或右偏斜分布,负偏斜或左偏斜分布)
    • **峰度:**峰度表示分布的尾部与正态分布的区别。使用峰度可帮助您初步了解有关数据分布的一般特征。(正峰度和负峰度,基线:峰度值0 即正态分布)
  • 每个数字特征的分布可视化

    ## 3) 每个数字特征得分布可视化
    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")
    
  • 数字特征相互之间的关系可视化

    ## 4) 数字特征相互之间的关系可视化
    sns.set()
    columns = ['price', 'v_12', 'v_8' , 'v_0', 'power', 'v_5',  'v_2', 'v_6', 'v_1', 'v_14']
    sns.pairplot(Train_data[columns],size = 2 ,kind ='scatter',diag_kind='kde')
    plt.show()
    
  • 多变量互相回归关系可视化

    fig, ((ax1, ax2), (ax3, ax4), (ax5, ax6), (ax7, ax8), (ax9, ax10)) = plt.subplots(nrows=5, ncols=2, figsize=(24, 20))
    # ['v_12', 'v_8' , 'v_0', 'power', 'v_5',  'v_2', 'v_6', 'v_1', 'v_14']
    v_12_scatter_plot = pd.concat([Y_train,Train_data['v_12']],axis = 1)
    sns.regplot(x='v_12',y = 'price', data = v_12_scatter_plot,scatter= True, fit_reg=True, ax=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)
    
    v_0_scatter_plot = pd.concat([Y_train,Train_data['v_0']],axis = 1)
    sns.regplot(x='v_0',y = 'price',data = v_0_scatter_plot,scatter= True, fit_reg=True, ax=ax3)
    
    power_scatter_plot = pd.concat([Y_train,Train_data['power']],axis = 1)
    sns.regplot(x='power',y = 'price',data = power_scatter_plot,scatter= True, fit_reg=True, ax=ax4)
    
    v_5_scatter_plot = pd.concat([Y_train,Train_data['v_5']],axis = 1)
    sns.regplot(x='v_5',y = 'price',data = v_5_scatter_plot,scatter= True, fit_reg=True, ax=ax5)
    
    v_2_scatter_plot = pd.concat([Y_train,Train_data['v_2']],axis = 1)
    sns.regplot(x='v_2',y = 'price',data = v_2_scatter_plot,scatter= True, fit_reg=True, ax=ax6)
    
    v_6_scatter_plot = pd.concat([Y_train,Train_data['v_6']],axis = 1)
    sns.regplot(x='v_6',y = 'price',data = v_6_scatter_plot,scatter= True, fit_reg=True, ax=ax7)
    
    v_1_scatter_plot = pd.concat([Y_train,Train_data['v_1']],axis = 1)
    sns.regplot(x='v_1',y = 'price',data = v_1_scatter_plot,scatter= True, fit_reg=True, ax=ax8)
    
    v_14_scatter_plot = pd.concat([Y_train,Train_data['v_14']],axis = 1)
    sns.regplot(x='v_14',y = 'price',data = v_14_scatter_plot,scatter= True, fit_reg=True, ax=ax9)
    
    v_13_scatter_plot = pd.concat([Y_train,Train_data['v_13']],axis = 1)
    sns.regplot(x='v_13',y = 'price',data = v_13_scatter_plot,scatter= True, fit_reg=True, ax=ax10)
    
    

类别特征分析

  • ## 1) unique分布
    for fea in categorical_features:
        print(Train_data[fea].nunique())
    
  • ## 2) 类别特征箱形图可视化
    
    # 因为 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():
            Train_data[c] = Train_data[c].cat.add_categories(['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)
    
    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(boxplot, "value", "price")
    
  • ## 3) 类别特征的小提琴图可视化
    catg_list = categorical_features
    target = 'price'
    for catg in catg_list :
        sns.violinplot(x=catg, y=target, data=Train_data)
        plt.show()
    
  • ## 4) 类别特征的柱形图可视化
    def bar_plot(x, y, **kwargs):
        sns.barplot(x=x, y=y)
        x=plt.xticks(rotation=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, size=5)
    g = g.map(bar_plot, "value", "price")
    
  • ##  5) 类别特征的每个类别频数可视化(count_plot)
    def count_plot(x,  **kwargs):
        sns.countplot(x=x)
        x=plt.xticks(rotation=90)
    
    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")
    
    

用pandas_profiling生成数据报告

import pandas_profiling
pfr = pandas_profiling.ProfileReport(Train_data)
pfr.to_file("./example.html")

总结

所给出的EDA步骤为广为普遍的步骤,在实际的不管是工程还是比赛过程中,这只是最开始的一步,也是最基本的一步。

接下来一般要结合模型的效果以及特征工程等来分析数据的实际建模情况,根据自己的一些理解,查阅文献,对实际问题做出判断和深入的理解。

最后不断进行EDA与数据处理和挖掘,来到达更好的数据结构和分布以及较为强势相关的特征


数据探索在机器学习中我们一般称为EDA(Exploratory Data Analysis):

是指对已有的数据(特别是调查或观察得来的原始数据)在尽量少的先验假定下进行探索,通过作图、制表、方程拟合、计算特征量等手段探索数据的结构和规律的一种数据分析方法。

数据探索有利于我们发现数据的一些特性,数据之间的关联性,对于后续的特征构建是很有帮助的。

  1. 对于数据的初步分析(直接查看数据,或.sum(), .mean(),.descirbe()等统计函数)可以从:样本数量,训练集数量,是否有时间特征,是否是时许问题,特征所表示的含义(非匿名特征),特征类型(字符类似,int,float,time),特征的缺失情况(注意缺失的在数据中的表现形式,有些是空的有些是”NAN”符号等),特征的均值方差情况。
  2. 分析记录某些特征值缺失占比30%以上样本的缺失处理,有助于后续的模型验证和调节,分析特征应该是填充(填充方式是什么,均值填充,0填充,众数填充等),还是舍去,还是先做样本分类用不同的特征模型去预测。
  3. 对于异常值做专门的分析,分析特征异常的label是否为异常值(或者偏离均值较远或者事特殊符号),异常值是否应该剔除,还是用正常值填充,是记录异常,还是机器本身异常等。
  4. 对于Label做专门的分析,分析标签的分布情况等。
  5. 进步分析可以通过对特征作图,特征和label联合做图(统计图,离散图),直观了解特征的分布情况,通过这一步也可以发现数据之中的一些异常值等,通过箱型图分析一些特征值的偏离情况,对于特征和特征联合作图,对于特征和label联合作图,分析其中的一些关联性。