大数据大佬级别数据预处理方式

    作者:野猫谈Python更新于: 2020-09-06 16:16:05

    Python数据分析真的不难学,实战来了:大佬级别数据预处理方式。从技术上看,大数据云计算的关系就像一枚硬币的正反面一样密不可分。大数据必然无法用单台的计算机进行处理,必须采用分布式架构。它的特色在于对海量数据进行分布式数据挖掘。但它必须依托云计算的分布式处理、分布式数据库和云存储、虚拟化技术。

     这次我们专门挑了一份烂大街的数据集Titanic,写了一点关于数据预处理部分,但是代码风格却是大(zhuang)佬(bi)级别。很明显,我不是大佬,不过是有幸被培训过。

    大数据大佬级别数据预处理方式_大数据_数据库_虚拟化_课课家

    说到预处理,一般就是需要:

    • 数字型缺失值处理
    • 类别型缺失值处理
    • 数字型标准化
    • 类别型特征变成dummy变量
    • Pipeline 思想

    在做数据处理以及机器学习的过程中,最后你会发现每个项目似乎都存在“套路”。所有的项目处理过程都会存在一个“套路”:

    • 预处理
    • 建模
    • 训练
    • 预测

    对于预处理,其实也是一个套路,不过我们不用pipeline 函数,而是另一个FeatureUnion函数。

    当然一个函数也不能解决所有问题,我们通过实战来看看哪些函数以及编码风格能让我们的代码看起来很有条理并且“大(zhuang)佬(bi)”风格十足。

    导入数据开启实战

    今天我们分析的titanic 数据,数据我已经下载,并且放在项目路径下的data 文件中。

    1. import pandas as pd 
    2. file = 'data/titanic_train.csv' 
    3. raw_df = pd.read_csv(file) 

    接下来就是标准套路:预览info以及预览head。

    1. print(raw_df.info()) 
    2. print(raw_df.head()) 

    我们对数据集的名称进行简单的回顾:

    • RangeIndex: 891 entries, 0 to 890:表示891 个样本
    • columns :共12 列

    按数据类型来划分:

    int64 :

    • PassengerId :乘客ID
    • Survived:是否生存,1 为生存
    • Pclass :乘客级别
    • SibSp :sibling and spouse (兄弟姐妹以及配偶个数)Parch :parents and children(父母以及子女个数)

    object:

    • Name: 名字
    • Sex:性别
    • Ticket :船票编号
    • Cabin:船舱号
    • Embarked:登船地点

    float64:

    • Age:年龄
    • Fare 票价
    1. RangeIndex: 891 entries, 0 to 890 
    2. Data columns (total 12 columns): 
    3.  #   Column       Non-Null Count  Dtype   
    4. ---  ------       --------------  -----   
    5.  0   PassengerId  891 non-null    int64   
    6.  1   Survived     891 non-null    int64   
    7.  2   Pclass       891 non-null    int64   
    8.  3   Name         891 non-null    object  
    9.  4   Sex          891 non-null    object  
    10.  5   Age          714 non-null    float64 
    11.  6   SibSp        891 non-null    int64   
    12.  7   Parch        891 non-null    int64   
    13.  8   Ticket       891 non-null    object  
    14.  9   Fare         891 non-null    float64 
    15.  10  Cabin        204 non-null    object  
    16.  11  Embarked     889 non-null    object  
    17. dtypes: float64(2), int64(5), object(5) 
    18. memory usage: 83.7+ KB 

    一般的机器学习都不会预处理缺失值以及类别型数据,因此我们至少要对这两种情形做预处理。

    首先我们查看缺失值,其实上文中的info已经有这样的信息。这里我们更显式的展示缺失信息。

    1. # get null count for each columns 
    2. nulls_per_column = raw_df.isnull().sum() 
    3. print(nulls_per_column) 

    结果如下:

    1. PassengerId       0 
    2. Survived             0 
    3. Pclass                 0 
    4. Name                 0 
    5. Sex                     0 
    6. Age                177 
    7. SibSp                  0 
    8. Parch                  0 
    9. Ticket                 0 
    10. Fare                    0 
    11. Cabin             687 
    12. Embarked          2 
    13. dtype: int64 

    可以看到Age 有缺失,Age是float64 类型数据,Cabin 有缺失,Cabin 为object 类型,Embarked 有缺失,Embarked 也是object 类型。

    主角登场(策略与函数)

    上述我们可以看到缺失的列有哪些,对于有些情况,比如快速清理数据,我们仅仅会制定如下策略:

    对于float类型,我们一般就是用均值或者中位数来代替 对于object 类型,如果ordinal 类型,也就是严格类别之分,比如(男,女),比如(高,中,低)等,一般就用众数来替代 对于object 类型,如果nominal类型,也就是没有等级/严格类别关系,比如ID,我们就用常值来替代。本文中用到的是sklearn的preprocessing 模块,pipeline模块,以及一个第三方“新秀”sklearn_pandas 库。

    这里我们简单的介绍这个函数的用途。

    1. StandardScaler: 用于对数字类型做标准化处理 
    2. LabelBinarizer: 顾名思义,将类型类型,先label 化(变成数字),再Binarize (变成二进制)。相当于onehot 编码,不过LabelBinarizer只是针对一列进行处理 
    3. FeatureUnion:用于将不同特征预处理过程(函数)重新合并,但是需要注意的是它的输入不是数据而是transformer,也就是预处理的方法。 
    4. SimpleImputer:sklearn 自带了类似于fillna的预处理函数 
    5. CategoricalImputer: 来自于sklearn_pandas 的补充,因为sklearn 中并没有针对类别类型数据的预处理。 
    6. DataFrameMapper: 相当于构建针对dataframe的不同的列构建不同的transformer。 
    7. from sklearn.preprocessing import StandardScaler 
    8. from sklearn.preprocessing import LabelBinarizer 
    9. from sklearn.pipeline import FeatureUnion 
    10. from sklearn_pandas import CategoricalImputer 
    11. from sklearn_pandas import DataFrameMapper 
    12. from sklearn.impute import SimpleImputer 

    按照我们策略,我们需要将列分为数字型和类别型。思路就是看一列数据是否为object类型。

    1. # split categorical columns and numerical columns 
    2. categorical_mask = (raw_df.dtypes == object) 
    3. categorical_cols = raw_df.columns[categorical_mask].tolist() 
    4. numeric_cols = raw_df.columns[~categorical_mask].tolist() 
    5. numeric_cols.remove('Survived'
    6. print(f'categorical_cols are {categorical_cols}' ) 
    7. print(f'numeric_cols are {numeric_cols}' ) 

    print:

    1. categorical_cols are ['Name''Sex''Ticket''Cabin''Embarked'
    2. numeric_cols are ['PassengerId''Pclass''Age''SibSp''Parch''Fare'

    数值型数据预处理

    对数值型数据进行预处理,这里我们采用DataFrameMapper来创建这个transformer 对象,对所有的numeric_cols 进行填写中值。

    1. numeric_fillna_mapper=DataFrameMapper([([col], SimpleImputer(strategy="median")) for col in numeric_cols], 
    2.                                             input_df=True
    3.                                             df_out=True 
    4.                                            ) 

    我们可以测试代码,看一下变换后的数据是什么样。这里需要调用fit_transform 方法。

    1. transformed = numeric_fillna_mapper.fit_transform(raw_df) 
    2. print(transformed.info()) 

    结果如下,可以看到变换后的数据只包含我们处理的列,并且可以看到non-null 个数已经为891,表明没有缺失。

    1. #   Column       Non-Null Count  Dtype   
    2. --  ------       --------------  -----   
    3. 0   PassengerId  891 non-null    float64 
    4. 1   Pclass       891 non-null    float64 
    5. 2   Age          891 non-null    float64 
    6. 3   SibSp        891 non-null    float64 
    7. 4   Parch        891 non-null    float64 
    8. 5   Fare         891 non-null    float64 

    如果我们需要对数值型特征,先进行缺失值填充,然后再进行标准化。这样我们只需要将上面的函数重新修改,增加一个transformer list。这个transformer list包含SimpleImputer 和StandardScaler 两步。

    1. # fill nan with mean 
    2. and then standardize cols 
    3. numeric_fillna_standardize_mapper=DataFrameMapper([([col], [SimpleImputer(strategy="median"), 
    4.                                                 StandardScaler()]) for col in numeric_cols], 
    5.                                             input_df=True
    6.                                             df_out=True 
    7.                                            ) 
    8. fillna_standardized = numeric_fillna_standardize_mapper.fit_transform(raw_df) 
    9.  
    10. print(fillna_standardized.head()) 

    预览变换后的结果:

    1.    PassengerId       Pclass          Age        SibSp        Parch          Fare 
    2. 0    -1.730108  0.827377 -0.565736  0.432793 -0.473674 -0.502445 
    3. 1    -1.726220 -1.566107  0.663861  0.432793 -0.473674  0.786845 
    4. 2    -1.722332  0.827377 -0.258337 -0.474545 -0.473674 -0.488854 
    5. 3    -1.718444 -1.566107  0.433312  0.432793 -0.473674  0.420730 
    6. 4    -1.714556  0.827377  0.433312 -0.474545 -0.473674 -0.486337 

    这样我们就完成了数值型数据的预处理。类似的我们可以针对类别型数据进行预处理。

    类别型数据预处理

    本例中,Cabin 有缺失,Embarked 有缺失,因为这两者都是有有限类别个数的,我们可以用出现最高频次的数据进行填充,假如是Name 缺失呢?一般Name都没有重名的,而且即便有个别重名,用最高频次的数据进行填充也没有意义。所以我们会选择用常数值填充,比如“unknown”等。

    作为一个模板,这里我们的处理方法要涵盖两种情况。

    ['Name','Cabin','Ticket'] 其实都类似于ID,几乎没有重复的,我们用常值替代,然后用LabelBinarizer变成dummy 变量 其他列,我们用最高频次的类别填充,然后用LabelBinarizer变成dummy 变量。

    1. # Apply categorical imputer 
    2.  
    3. constant_cols = ['Name','Cabin','Ticket'
    4. frequency_cols = [_ for _  in categorical_cols if _ not in constant_cols] 
    5.  
    6. categorical_fillna_freq_mapper = DataFrameMapper( 
    7.                                                 [(col, [CategoricalImputer(),LabelBinarizer()]) for col in frequency_cols], 
    8.                                                 input_df=True
    9.                                                 df_out=True 
    10.                                                ) 
    11.  
    12. categorical_fillna_constant_mapper = DataFrameMapper( 
    13.                                                 [(col, [CategoricalImputer(strategy='constant',fill_value='unknown'),LabelBinarizer()]) for col in constant_cols], 
    14.                                                 input_df=True
    15.                                                 df_out=True 
    16.                                                ) 

    我们同样进行测试代码:

    1. transformed = categorical_fillna_freq_mapper.fit_transform(raw_df) 
    2. print(transformed.info()) 
    3. transformed = categorical_fillna_constant_mapper.fit_transform(raw_df) 
    4. print(transformed.shape) 

    结果如下:

    1. Data columns (total 4 columns): 
    2.  #   Column      Non-Null Count  Dtype 
    3. ---  ------      --------------  ----- 
    4.  0   Sex         891 non-null    int32 
    5.  1   Embarked_C  891 non-null    int32 
    6.  2   Embarked_Q  891 non-null    int32 
    7.  3   Embarked_S  891 non-null    int32 
    8. dtypes: int32(4) 

    以及:

    1. (891, 1720) 

    featureunion 所有的预处理过程

    前面我们已经测试了每一种的预处理的方式(transfomer 或者称为mapper),可以看到结果中只包含处理的部分列对应的结果。

    实际中,我们可以用FeatureUnion,直接将所有需要处理的方式(transfomer 或者称为mapper)变成一个pipeline,一目了然。

    然后调用fit_transform 对原始数据进行变换,这样我们的预处理看起来更有条理。

    1. feature_union_1 = FeatureUnion([("numeric_fillna_standerdize", numeric_fillna_standardize_mapper), 
    2.                               ("cat_freq", categorical_fillna_freq_mapper), 
    3.                                 ("cat_constant", categorical_fillna_constant_mapper)]) 
    4.  
    5. df_1 = feature_union_1.fit_transform(raw_df) 
    6.  
    7. print(df_1.shape) 
    8. print(raw_df.shape) 

    总结

    本文介绍了“大佬”级别的数据预处理方式,并且是在实战中进行演示。

    通过本文可以学到:

    • 数值型预处理,通过DataFrameMapper 直接对数值类型的列进行多次变换
    • 类别型预处理,通过DataFrameMapper 直接对类别型的列进行多次变换
    • 类别型变换方法可以至少采用两种方式
    • LabelBinarizer,SimpleImputer,CategoricalImputer,LabelBinarizer等函数对数据 进行变换
    • FeatureUnion 来将预处理过程管道化(pipeline) 通过这样的方式处理数据,会一目了然。
      大数据技术的战略意义不在于掌握庞大的数据信息,而在于对这些含有意义的数据进行专业化处理。换而言之,如果把大数据比作一种产业,那么这种产业实现盈利的关键,在于提高对数据的“加工能力”,通过“加工”实现数据的“增值”。

课课家教育

未登录