美文网首页程序员
高维数据的LOF算法实现

高维数据的LOF算法实现

作者: 对月流珠_0c9f | 来源:发表于2018-12-03 15:45 被阅读68次

    在之前代码中实现的LOF算法完成的是二维数据的离群点剔除,那么往往我们的数据都是高维的,在这种情况下可以有怎样的处理方式呢?
    源自:https://zhuanlan.zhihu.com/p/37753692
    ps:不得不说,这位大佬对问题逐步深入的思考很令人佩服,思路清洗明了,值得研究。

    数据维度的增大会导致的几个问题:
    1、增加了计算难度,不能仅仅通过距离度量的方式来进行判断
    2、不同参数对问题的影响因子不同,可能会放大一些因素的影响
    3、k的选取增加了新的难度

    可以考虑的处理方法
    1、采用马氏距离作为距离方式
    2、采用随机森林的决策思想,考虑在多个维度投影上采用LOF并将结果结合起来,以提高高维数据的检测质量。

    初期工作:
    1、对数据进行简单的清洗,去除一些意义较差的参数
    2、数据的规约处理(就是将同一类的特征整合为同一个特征数据进行考虑)
    3、数据维度必须大于1,如果只有1维可以进行增维处理

    data = list(zip(data, np.zeros_like(data)))
    

    或者

    data = pd.DataFrame(data)
    

    改进方式:
    1、投票表决模式
    2、LOF异常分数加权模式
    3、混合模式

    一、投票表决模式

    投票表决模式认为每一个维度的数据都是同等重要,单独为每个维度数据设置 LOF 阈值并进行比对,样本的 LOF 值超过阈值则异常票数积 1 分,最终超过票数阈值的样本认为是离群样本。

    • localoutlierfactor(data, predict, k)
      输入:训练样本,测试样本,k值
      输出:每一个测试样本 LOF 值及对应的第k距离
    • plot_lof(result,method)
      输入:LOF 值、阈值
      输出:以索引为横坐标的 LOF 值分布图
    • ensemble_lof(data, predict=None, k=5, groups=[], method=1, vote_method = 'auto')
      输入:训练样本,测试样本,k值,组合特征索引,离群阈值,票数阈值
      组合特征索引(列位置 - 1):如第一列数据与第二列数据作为同类型数据,则传入groups = [[0, 1]]
      离群阈值:每一个特征的离群阈值,缺少位数则以 1 替代
      票数阈值:正常点离群因子得票数上限
      输出:离群点、正常点分类情况
    • 没有输入测试样本时,默认测试样本=训练样本


    由于在这个问题里面参数一共就只有两个维度,所以无法体现出来这种方式的优点。

    def localoutlierfactor(data, predict, k, group_str):
        from sklearn.neighbors import LocalOutlierFactor
        clf = LocalOutlierFactor(n_neighbors=k + 1, algorithm='auto', contamination=0.1, n_jobs=-1)
        clf.fit(data)
        # 记录 LOF 离群因子,做相反数处理
        predict['local outlier factor %s' % group_str] = -clf._decision_function(predict.iloc[:, eval(group_str)])
        return predict
    
    def plot_lof(result, method, group_str):
        import matplotlib.pyplot as plt
        plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
        plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号
        plt.figure('local outlier factor %s' % group_str)
        try:
            plt.scatter(result[result > method].index,
                        result[result > method], c='red', s=50,
                        marker='.', alpha=None,
                        label='离群点')
        except Exception:
            pass
        try:
            plt.scatter(result[result <= method].index,
                        result[result <= method], c='black', s=50,
                        marker='.', alpha=None, label='正常点')
        except Exception:
            pass
        plt.hlines(method, -2, 2 + max(result.index), linestyles='--')
        plt.xlim(-2, 2 + max(result.index))
        plt.title('LOF局部离群点检测', fontsize=13)
        plt.ylabel('局部离群因子', fontsize=15)
        plt.legend()
        plt.show()
    
    def ensemble_lof(data, predict=None, k=5, groups=[], method=1, vote_method = 'auto'):
        import pandas as pd
        import numpy as np
        # 判断是否传入测试数据,若没有传入则测试数据赋值为训练数据
        try:
            if predict == None:
                predict = data.copy()
        except Exception:
            pass
        data = pd.DataFrame(data); 
        predict = pd.DataFrame(predict)
        # 数据标签分组,默认独立自成一组
        for i in range(data.shape[1]):
            if i not in pd.DataFrame(groups).values:
                groups += [[i]]
        # 扩充阈值列表
        if type(method) != list:
            method = [method]
            method += [1] * (len(groups) - 1)
            print(method)
        else:
            method += [1] * (len(groups) - len(method))
            print(method)
        vote = np.zeros(len(predict))
        # 计算LOF离群因子并根据阈值进行票数统计
        for i in range(len(groups)):
            predict = localoutlierfactor(pd.DataFrame(data).iloc[:, groups[i]], predict, k, str(groups[i]))
            plot_lof(predict.iloc[:, -1], method[i], str(groups[i]))
            vote += predict.iloc[:, -1] > method[i]
        # 根据票数阈值划分离群点与正常点
        predict['vote'] = vote
        if vote_method == 'auto':
            vote_method = len(groups)/2
        outliers = predict[vote > vote_method].sort_values(by='vote')
        inliers = predict[vote <= vote_method].sort_values(by='vote')
        return outliers, inliers
    
    import numpy as np
    import pandas as pd
    
    posi = pd.read_excel(r'已结束项目任务数据.xls')
    lon = np.array(posi["任务gps经度"][:])  # 经度
    lat = np.array(posi["任务gps 纬度"][:])  # 纬度
    A = list(zip(lat, lon))  # 按照纬度-经度匹配
    
    posi = pd.read_excel(r'会员信息数据.xlsx')
    lon = np.array(posi["会员位置(GPS)经度"][:])  # 经度
    lat = np.array(posi["会员位置(GPS)纬度"][:])  # 纬度
    B = list(zip(lat, lon))  # 按照纬度-经度匹配
    
    # 获取会员对任务密度,取第5邻域,阈值分别为 1.5,2,得票数超过 1 的认为是异常点 
    outliers4, inliers4 = ensemble_lof(A, B, k=5, method=[1.5,2], vote_method = 1)
    
    # 绘图程序
    plt.figure('投票集成 LOF 模式')
    plt.scatter(np.array(B)[:, 0], np.array(B)[:, 1], s=10, c='b', alpha=0.5)
    plt.scatter(np.array(A)[:, 0], np.array(A)[:, 1], s=10, c='green', alpha=0.3)
    plt.scatter(outliers4[0], outliers4[1], s=10 + 1000, c='r', alpha=0.2)
    plt.title('k = 5, method = [1.5, 2]')
    

    后续在石油项目中我会应用这个方法进行尝试

    二、LOF异常分数加权模式

    异常分数加权模式则是对各维度数据的 LOF 值进行加权,获取最终的 LOF 得分作为整体数据的 LOF 得分。也就是说在LOF异常分数加权模式里面考虑了不同参数的影响程度。

    在这个方法中,权重的确定是至关重要的,很大程度上决定了模型的可行性和拟合程度。因此,模型确认的重点放在了权重的确认上。

    *在这里采用的方法是 熵权法


    与上一个方法相比,差距在对ensemble_lof函数中进行了调整。

    在这里加了一个权重而已2333
    废话少说,上代码:
    def localoutlierfactor(data, predict, k, group_str):
        from sklearn.neighbors import LocalOutlierFactor
        clf = LocalOutlierFactor(n_neighbors=k + 1, algorithm='auto', contamination=0.1, n_jobs=-1)
        clf.fit(data)
        # 记录 LOF 离群因子,做相反数处理
        predict['local outlier factor %s' % group_str] = -clf._decision_function(predict.iloc[:, eval(group_str)])
        return predict
    
    def plot_lof(result, method):
        import matplotlib.pyplot as plt
        plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
        plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号
        plt.scatter(result[result > method].index,
                    result[result > method], c='red', s=50,
                    marker='.', alpha=None,
                    label='离群点')
        plt.scatter(result[result <= method].index,
                    result[result <= method], c='black', s=50,
                    marker='.', alpha=None, label='正常点')
        plt.hlines(method, -2, 2 + max(result.index), linestyles='--')
        plt.xlim(-2, 2 + max(result.index))
        plt.title('LOF局部离群点检测', fontsize=13)
        plt.ylabel('局部离群因子', fontsize=15)
        plt.legend()
        plt.show()
    
    def ensemble_lof(data, predict=None, k=5, groups=[], method='auto', weight=1):
        import pandas as pd
        # 判断是否传入测试数据,若没有传入则测试数据赋值为训练数据
        try:
            if predict == None:
                predict = data
        except Exception:
            pass
        data = pd.DataFrame(data);
        predict = pd.DataFrame(predict)
        # 数据标签分组,默认独立自成一组
        for i in range(data.shape[1]):
            if i not in pd.DataFrame(groups).values:
                groups += [[i]]
        # 扩充权值列表
        if type(weight) != list:
            weight = [weight]
            weight += [1] * (len(groups) - 1)
        else:
            weight += [1] * (len(groups) - len(weight))
        predict['local outlier factor'] = 0
        # 计算LOF离群因子并根据特征权重计算加权LOF得分
        for i in range(len(groups)):
            predict = localoutlierfactor(pd.DataFrame(data).iloc[:, groups[i]], predict, k, str(groups[i]))
            predict['local outlier factor'] += predict.iloc[:, -1] * weight[i]
        if method == 'auto':
            method = sum(weight)
        plot_lof(predict['local outlier factor'], method)
        # 根据离群阈值划分离群点与正常点
        outliers = predict[predict['local outlier factor'] > method].sort_values(by='local outlier factor')
        inliers = predict[predict['local outlier factor'] <= method].sort_values(by='local outlier factor')
        return outliers, inliers
    
    
    import numpy as np
    import pandas as pd
    
    posi = pd.read_excel(r'E:\\CNOOC_drilling\\11month\\离群点异常点\\已结束项目任务数据.xls')
    lon = np.array(posi["任务gps经度"][:])  # 经度
    lat = np.array(posi["任务gps 纬度"][:])  # 纬度
    A = list(zip(lat, lon))  # 按照纬度-经度匹配
    
    posi = pd.read_excel(r'E:\\CNOOC_drilling\\11month\\离群点异常点\\会员信息数据.xlsx')
    lon = np.array(posi["会员位置(GPS)经度"][:])  # 经度
    lat = np.array(posi["会员位置(GPS)纬度"][:])  # 纬度
    B = list(zip(lat, lon))  # 按照纬度-经度匹配
    
    # 获取会员对任务密度,取第5邻域,阈值为 100,权重分别为5,1 
    outliers5, inliers5 = ensemble_lof(A, B, k=5, method=100,weight = [5,1])
    
    # 绘图程序
    plt.figure('LOF 异常分数加权模式')
    plt.scatter(np.array(B)[:, 0], np.array(B)[:, 1], s=10, c='b', alpha=0.5)
    plt.scatter(np.array(A)[:, 0], np.array(A)[:, 1], s=10, c='green', alpha=0.3)
    plt.scatter(outliers5[0], outliers5[1], s=10 + outliers5['local outlier factor'], c='r', alpha=0.2)
    plt.title('k = 5, method = 100')
    


    效果有所改善。

    三、混合模式

    将前两种方法进行综合使用。
    (不过我觉得LOF异常分数加权模式已经能够实现这个功能了。)

    相关文章

      网友评论

        本文标题:高维数据的LOF算法实现

        本文链接:https://www.haomeiwen.com/subject/ustgqqtx.html