数据挖掘与scikit-learn入门

数据探索

所谓的数据探索就是对数据有一个初步的认知。比如业务背景是什么,特征是什么,特征的取值范围要不要约定,特征的维度,样本的维度等等。建议将数据利用pandas转换成dataframe,这样使用head()等方法可以很方便的查看数据信息。比如:

#观察数据类型 
data.head()

#观察数据结构 
data.shape() 
data.info()

数据预处理

去除重复值

样本重复:即有样本之间的所有特征都相同。有时候可能时人为失误,但有时候可能是系统录入重复,总而言之我们必须对数据进行去重处理。

def remove_repeat_data(self):
    df = pd.DataFrame(self.data).copy()
    df.drop_duplicates(inplace=True) # 去除重复值
    df.index = range(df.shape[0])   # 去重之后千万不要忘记,恢复索引
    self.data = df
填补缺失值

缺失值的处理方式有很多种,比较常见的有直接删除和均值填充。可以直接删除的情况是:该缺失特征的样本较少。不能直接删除的就需要对特征值进行填充,常见的填充方法有:均值插入,模型预测插值,拉格朗日插值,牛顿插值。目前python的scipy只提供了拉格朗日插值接口,本文仅对均值插入和模型预测插入进行说明。

  • 方法一:利用统计值(如平均值)对缺失值进行填充:
#探索缺失值 
>> data.isnull().sum()

SeriousDlqin2yrs                            0
RevolvingUtilizationOfUnsecuredLines        0
age                                         0
NumberOfTime30-59DaysPastDueNotWorse        0
DebtRatio                                   0
MonthlyIncome                           29221
NumberOfOpenCreditLinesAndLoans             0
NumberOfTimes90DaysLate                     0
NumberRealEstateLoansOrLines                0
NumberOfTime60-89DaysPastDueNotWorse        0
NumberOfDependents                       3828
dtype: int64

查看有缺失值样本的比例,以下两种方法均可

>> data.isnull().sum()/data.shape[0]

SeriousDlqin2yrs                        0.000000
RevolvingUtilizationOfUnsecuredLines    0.000000
age                                     0.000000
NumberOfTime30-59DaysPastDueNotWorse    0.000000
DebtRatio                               0.000000
MonthlyIncome                           0.195601
NumberOfOpenCreditLinesAndLoans         0.000000
NumberOfTimes90DaysLate                 0.000000
NumberRealEstateLoansOrLines            0.000000
NumberOfTime60-89DaysPastDueNotWorse    0.000000
NumberOfDependents                      0.025624
dtype: float64
>> data.isnull().mean()

SeriousDlqin2yrs                        0.000000
RevolvingUtilizationOfUnsecuredLines    0.000000
age                                     0.000000
NumberOfTime30-59DaysPastDueNotWorse    0.000000
DebtRatio                               0.000000
MonthlyIncome                           0.195601
NumberOfOpenCreditLinesAndLoans         0.000000
NumberOfTimes90DaysLate                 0.000000
NumberRealEstateLoansOrLines            0.000000
NumberOfTime60-89DaysPastDueNotWorse    0.000000
NumberOfDependents                      0.025624
dtype: float64

利用平均值对NumberOfDependents特征的缺失值进行填充

data['NumberOfDependents'].fillna(data['NumberOfDependents'].mean(),inplace=True)
data.info()

<class 'pandas.core.frame.DataFrame'>
RangeIndex: 149391 entries, 0 to 149390
Data columns (total 11 columns):
SeriousDlqin2yrs                        149391 non-null int64
RevolvingUtilizationOfUnsecuredLines    149391 non-null float64
age                                     149391 non-null int64
NumberOfTime30-59DaysPastDueNotWorse    149391 non-null int64
DebtRatio                               149391 non-null float64
MonthlyIncome                           120170 non-null float64
NumberOfOpenCreditLinesAndLoans         149391 non-null int64
NumberOfTimes90DaysLate                 149391 non-null int64
NumberRealEstateLoansOrLines            149391 non-null int64
NumberOfTime60-89DaysPastDueNotWorse    149391 non-null int64
NumberOfDependents                      149391 non-null float64
dtypes: float64(4), int64(7)
memory usage: 12.5 MB

方法二:模型预测插值(比如利用随机森林(回归)对缺失值补充)

原理:“既然我可以使用A,B,C去预测Z,那我也可以使用 A,C,Z去预测B的思想来填补缺失值”。对于一个有n个特征的数据来说,其中特征T有缺失值,我们就把特征T当作标签,其他的n-1个特征和原本的标签组成新的特征矩阵。那对于T来说,它没有缺失的部分就是我们的y_train,这部分数据既有标签也有特征,而它缺失的部分,只有特征没有标签,就是我们需要预测的部分。

综上:特征T不缺失的值对应的其他n-1个特征+本来的标签为:x_train,特征T不缺失的值为:y_train,特征T缺失的值对应的其他n-1个特征+本来的标签为:x_test,特征T缺失的值为:我们需要预测的y_test。这种做法,对于某一个特征大量缺失,其他特征却很完整的情况,非常适用。

def fill_by_rf(self, x, y, col_name):
    """
    x:输入的特征矩阵,且不包含标签维度
    y:标签矩阵
    col_name:缺失特征的columns名词
    """
    
    df = x.copy()
    #构建我们的新特征矩阵和新标签
    tag_col = df.loc[:, col_name]
    df = pd.concat([df.loc[:, df.columns != col_name], pd.DataFrame(y)], axis=1)
    # 找出我们的训练集和测试集
    y_train = tag_col[tag_col.notnull()]
    x_train = df.iloc[y_train.index, :]
    y_test = tag_col[tag_col.isnull()]
    x_test = df.iloc[y_test.index, :]
    rf = RandomForestRegressor(n_estimators=100)
    rf = rf.fit(x_train, y_train)
    y_pred = rf.predict(x_test)
    return y_pred
    
x = data.iloc[:, 1:]
y = data['SeriousDlqin2yrs']
y_pred = fill_by_rf(x, y, 'MonthlyIncome')
y_pred

array([0.11, 0.33, 0.16, ..., 0.19, 0.09, 0.  ])

对上述的2个功能进行一个小小的封装:

def fill_by_model(self, x, y, col_name):
    df = x.copy()
    tag_col = df.loc[:, col_name]
    df = pd.concat([df.loc[:, df.columns != col_name], pd.DataFrame(y)], axis=1)
    # 找出我们的训练集和测试集
    y_train = tag_col[tag_col.notnull()]
    x_train = df.iloc[y_train.index, :]
    y_test = tag_col[tag_col.isnull()]
    x_test = df.iloc[y_test.index, :]
    # model = RandomForestRegressor(n_estimators=100)
    model = LinearRegression()
    model.fit(x_train, y_train)
    y_pred = model.predict(x_test)
    return y_pred

def fill_miss_value(self):
    df = pd.DataFrame(self.data).copy()
    miss_percent = df.isnull().sum() / df.shape[0]
    miss_percent.sort_values(inplace=True)
    for index, i in enumerate(miss_percent):
        col_name = miss_percent.index[index]
        if float(i) <= Config.miss_rate_value and float(i) != 0.0:
            df[col_name].fillna(df[col_name].mean(), inplace=True)
        elif float(i) > Config.miss_rate_value:
            x = df.loc[:, df.columns != Config.lable_colname]
            y = df[Config.lable_colname]
            y_pred = self.fill_by_model(x, y, col_name)
            df.loc[df[col_name].isnull(), col_name] = y_pred
        else:
            pass
    df.index = range(df.shape[0])
    self.data = df
处理异常值

方法:箱线图、3σ、聚类、孤立森林
举例:在银行数据中,我们希望排除的“异常值”不是一些超高或超低的数字,而是一些不符合常理的数据:比如,收入不能为负数,但是一个超高水平的收入却是合理的,可以存在的。所以在银行业中,我们往往就使用普通的描述性统计来观察数据的异常与否与数据的分布情况。注意,这种方法只能在特征量有限的情况下进行,如果有几百个特征又无法成功降维或特征选择不管用,那还是用比较好。

# 描述性统计 
data.describe().T

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KyEsnbZY-1580480442870)(evernotecid://199A0999-284B-465C-AF36-D243A9FA840A/appyinxiangcom/18720816/ENResource/p809)]

pandas中的describe函数功能是对数据中每一列数(其实就是特征)进行统计分析:
count:一列的元素个数;
mean:一列数据的平均值;
std:一列数据的均方差;(方差的算术平方根反映了一个数据集的离散程度,越大则数据间的差异越大,数据集中数据的离散程度越高;越小则数据间的大小差异越小,数据集中的数据离散程度越低)
min:一列数据中的最小值;
max:一列数中的最大值;
25%:一列数据中,前25%的数据的平均值;
50%:一列数据中,前50%的数据的平均值;
75%:一列数据中,前75%的数据的平均值;
通过上面的数据可以看出,age和monthlylncome中的最小值均不符合常理,所以应该作为异常值去除。那么常用的去除异常值方式有三种:箱线图、3西格玛法则、聚类。异常值其实也称为离群点,所以在今后的离群点检测也可以借鉴相同的思路。本文也会在这里对每种办法进行举例讲解:

  • 3σ:
    原理:如果数据服从正态分布,在3σ原则下,异常值被定义为一组测定值中与平均值的偏差超过3倍标准差的值。在正态分布的假设下,距离平均值3σ之外的值出现的概率为P(|x-μ|>3σ)≤0.003,属于极个别的小概率事件。如果数据不服从正态分布,也可以用远离平均值的多少倍标准差来描述。
    实现:
def remove_abnormal_value(self):
    df = pd.DataFrame(self.data).copy()
    # 对每个特征进行计算
    for col_name in df:
        abnormal_index = []
        if col_name != Config.lable_colname:
            feature_values = df[col_name]
            mean_ = feature_values.mean()
            std_ = feature_values.std()
            for index, i in enumerate(feature_values):
                score = abs(i - mean_) / std_
                if float(score) > 3.0:
                    abnormal_index.append(index)
            df.drop(index=abnormal_index, axis=0, inplace=True)
        df.index = range(df.shape[0])
    self.data = df
  • 箱型图:
    原理:箱型图提供了识别异常值的一个标准:异常值通常被定义为小于QL-1.5IQR或大于QU+1.5IQR的值。QL称为下四分位数,表示全部观察值中有四分之一的数据取值比它小;QU称为上四分位数,表示全部观察值中有四分之一的数据取值比它大;IQR称为四分位数间距,是上四分位数QU与下四分位数QL之差,其间包含了全部观察值的一半。
    实例:
def remove_abnormal_by_box(self):
    abnorma_info_dict = defaultdict(list)
    df = pd.DataFrame(self.data).copy()
    for col_name in df:
        abnormal_index = []
        if col_name != Config.lable_colname:
            feature_values = np.array(df[col_name])
            q1 = np.percentile(feature_values, 25)
            q3 = np.percentile(feature_values, 75)
            iqr = q3 - q1
            outlier = 1.5 * iqr
            for index, i in enumerate(feature_values):
                if (i > q3 + outlier) or (i < q1 - outlier):
                    abnormal_index.append(index)
            abnorma_info_dict[col_name] = abnormal_index
            df.drop(index=abnormal_index, axis=0, inplace=True)
        df.index = range(df.shape[0])
    print(abnorma_info_dict)
    self.data = df
  • 聚类:
    原理:聚类分析用于发现局部强相关的对象组,而异常检测用来发现不与其他对象强相关的对象。因此,聚类分析非常自然地可以用于离群点检测,本节主要介绍密度聚类的离群点检测方法。
    密度聚类dbscan:这里我对dbscan聚类原理做一个简单的总结,其实dbscan就是对所有的数据划分成三种类型(三种点):核心点,噪声点,边界点。聚类的原则为:1.将边界点划分到与它互为邻居的核心点相同分组中(相同类)。2.若2个核心点互为邻居,则它们属于同一个分组(这也是每个分组的核心点数量>=1的原因)。3.噪声点不属于任何分组。那么现在问题来了,我们如何去定义核心点、边界点、噪声点呢?这一点其实也简单,但是本文就不在这里叙述了,大家可以关注我后续的文章。
    实例:
def remove_abnormal_by_dbscan(self):
    abnormal_index = []
    df = pd.DataFrame(self.data).copy()
    feature_values = np.array(df.loc[:, df.columns != Config.lable_colname])
    label = np.array(df.loc[:, Config.lable_colname])
    min_max_scaler = MinMaxScaler()
    feature_values = min_max_scaler.fit_transform(feature_values, label)
    clusters = DBSCAN(eps=500, min_samples=10, n_jobs=-1).fit_predict(feature_values)
    print(clusters)
    for index, i in enumerate(clusters):
        if int(i) == -1:
            print(i)
            abnormal_index.append(index)
    df.drop(index=abnormal_index, axis=0, inplace=True)
    df.index = range(df.shape[0])
    self.data = df

另外,还有一种方法可以对离群点进行检测。在网络安全领域中针对异常检测也可以有很好的效果,他就是孤立森林。

  • 孤立森林:
    原理:孤立森林是一种无监督学习算法,属于组合决策树家族。这种方法和以上所有方法都不同。之前的所有方法都在试图寻找数据的常规区域,然后将任何在此定义区域之外的点都视为离群点或异常值。
    这种方法的工作方式不同,它明确地隔离异常值, 而不是通过给每个数据点分配一个分数来分析和构造正常的点和区域。它利用了这样一个事实:异常值只是少数,并且它们具有与正常实例非常不同的属性值。而且该算法适用于高维数据集,并且被证明是一种非常有效的异常检测方法!
    实例:
def remove_abnormal_by_iforst(self):
    abnormal_index = []
    df = pd.DataFrame(self.data).copy()
    feature_values = np.array(df.loc[:, df.columns != Config.lable_colname])
    model = IsolationForest(n_estimators=100, max_features=1, contamination=0.2)
    model.fit(feature_values)
    result = model.predict(feature_values)
    for index, i in enumerate(result):
        if int(i) == -1:
            abnormal_index.append(index)
    df.drop(index=abnormal_index, axis=0, inplace=True)
    df.index = range(df.shape[0])
    self.data = df
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值