特征选择算法之Relief算法python实现

本文介绍了特征选择的重要性和分类,重点讲解了Relief算法的工作原理和Python实现过程。Relief算法是一种过滤式特征选择方法,适用于二分类问题,能够通过评估特征在区分不同类别的能力来确定其权重。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

版权声明:本文为博主原创文章,转载时请附上原文链接:https://blog.youkuaiyun.com/qq_40871363/article/details/86511843

一、特征选择算法简介

对一个学习任务,给定它的数据属性集,属性即为“特征”。其中有些属性对学习任务有用称之为“相关特征”,有些属性无用则称之为“无关特征”。无关特征的存在不仅会在一定程度上加大数据集的冗余程度,还会在学习任务过程中造成不小的影响。因此,从给定的特征集合中选择出相关特征子集非常必要,而这个过程,即为“特征选择”。特征选择有两个显著的好处,一是可以起到降维的作用,二是可以减小学习任务的训练难度。
如何选择出相关特征子集?穷举显然是不合适的。我们希望可以给它一个初始集合,称之为“候选集合”,然后评价其的好坏,基于评价结果生成下一个候选集合,如此循环,直至无法找到更好的候选集合为止。当然这种方法容易让结果陷入局部最优。那么采用这种方法,关键在于两点:如何根据评价结果产生下一个候选特征子集?如何评价一个候选特征子集的好坏?下面将根据此作简单介绍。

(一)子集搜索与评价

第一个关键点是“子集搜索”问题,第二个关键点是“子集评价”问题。关于子集搜索,常见的方法有“前向搜索”、“后向搜索”、“双向搜索”等。关于子集评价,方法之一是计算信息熵增益,以此作为评价标准。将特征子集搜索与子集评价结合起来,即可进行特征选择。

(二)特征选择分类

常见的特征选择大致可分为三类:过滤式、包裹式和嵌入式。其中,Relief算法针对二分类进行特征选择,ReliefF算法针对多分类进行特征选择,ReliefF算法详细参见博文:https://blog.youkuaiyun.com/qq_40871363/article/details/86594578

1. 过滤式特征选择

2. 包裹式特征选择

3. 嵌入式特征选择

本文主要对过滤式特征选择算法进行python实现。

二、Relief算法简介

待补充

(一)原理

待补充

(二)伪算法

待补充

三、Relief算法python实现

(一)代码

代码片

// An highlighted block
# _*_ coding:utf8 _*_

"""
# 说明:特征选择方法一:过滤式特征选择(Relief算法)
# 思想:先用特征选择过程对初始特征进行"过滤",然后再用过滤后的特征训练模型
# 时间:2019-1-14
# 问题:
"""

import pandas as pd
import numpy as np
import numpy.linalg as la
import random


# 异常类
class FilterError:
    pass


class Filter:
    def __init__(self, data_df, sample_rate, t, k):
        """
        #
        :param data_df: 数据框(字段为特征,行为样本)
        :param sample_rate: 抽样比例
        :param t: 统计量分量阈值
        :param k: 选取的特征的个数
        """
        self.__data = data_df
        self.__feature = data_df.columns
        self.__sample_num = int(round(len(data_df) * sample_rate))
        self.__t = t
        self.__k = k

    # 数据处理(将离散型数据处理成连续型数据,比如字符到数值)
    def get_data(self):
        new_data = pd.DataFrame()
        for one in self.__feature[:-1]:
            col = self.__data[one]
            if (str(list(col)[0]).split(".")[0]).isdigit() or str(list(col)[0]).isdigit() or (str(list(col)[0]).split('-')[-1]).split(".")[-1].isdigit():
                new_data[one] = self.__data[one]
                # print '%s 是数值型' % one
            else:
                # print '%s 是离散型' % one
                keys = list(set(list(col)))
                values = list(xrange(len(keys)))
                new = dict(zip(keys, values))
                new_data[one] = self.__data[one].map(new)
        new_data[self.__feature[-1]] = self.__data[self.__feature[-1]]
        return new_data

    # 返回一个样本的猜中近邻和猜错近邻
    def get_neighbors(self, row):
        df = self.get_data()
        row_type = row[df.columns[-1]]
        right_df = df[df[df.columns[-1]] == row_type].drop(columns=[df.columns[-1]])
        wrong_df = df[df[df.columns[-1]] != row_type].drop(columns=[df.columns[-1]])
        aim = row.drop(df.columns[-1])
        f = lambda x: eulidSim(np.mat(x), np.mat(aim))
        right_sim = right_df.apply(f, axis=1)
        right_sim_two = right_sim.drop(right_sim.idxmin())
        # print right_sim_two
        # print right_sim.values.argmax()   # np.argmax(wrong_sim)
        wrong_sim = wrong_df.apply(f, axis=1)
        # print wrong_sim
        # print wrong_sim.values.argmax()
        # print right_sim_two.idxmin(), wrong_sim.idxmin()
        return right_sim_two.idxmin(), wrong_sim.idxmin()

    # 计算特征权重
    def get_weight(self, feature, index, NearHit, NearMiss):
        data = self.__data.drop(self.__feature[-1], axis=1)
        row = data.iloc[index]
        nearhit = data.iloc[NearHit]
        nearmiss = data.iloc[NearMiss]
        if (str(row[feature]).split(".")[0]).isdigit() or str(row[feature]).isdigit() or (str(row[feature]).split('-')[-1]).split(".")[-1].isdigit():
            max_feature = data[feature].max()
            min_feature = data[feature].min()
            right = pow(round(abs(row[feature] - nearhit[feature]) / (max_feature - min_feature), 2), 2)
            wrong = pow(round(abs(row[feature] - nearmiss[feature]) / (max_feature - min_feature), 2), 2)
            # w = wrong - right
        else:
            right = 0 if row[feature] == nearhit[feature] else 1
            wrong = 0 if row[feature] == nearmiss[feature] else 1
            # w = wrong - right
        w = wrong - right
        # print w
        return w

    # 过滤式特征选择
    def relief(self):
        sample = self.get_data()
        # print sample
        m, n = np.shape(self.__data)  # m为行数,n为列数
        score = []
        sample_index = random.sample(range(0, m), self.__sample_num)
        print '采样样本索引为 %s ' % sample_index
        num = 1
        for i in sample_index:    # 采样次数
            one_score = dict()
            row = sample.iloc[i]
            NearHit, NearMiss = self.get_neighbors(row)
            print '第 %s 次采样,样本index为 %s,其NearHit行索引为 %s ,NearMiss行索引为 %s' % (num, i, NearHit, NearMiss)
            for f in self.__feature[0:-1]:
                w = self.get_weight(f, i, NearHit, NearMiss)
                one_score[f] = w
                print '特征 %s 的权重为 %s.' % (f, w)
            score.append(one_score)
            num += 1
        f_w = pd.DataFrame(score)
        print '采样各样本特征权重如下:'
        print f_w
        print '平均特征权重如下:'
        print f_w.mean()
        return f_w.mean()

    # 返回最终选取的特征
    def get_final(self):
        f_w = pd.DataFrame(self.relief(), columns=['weight'])
        final_feature_t = f_w[f_w['weight'] > self.__t]
        print final_feature_t
        final_feature_k = f_w.sort_values('weight').head(self.__k)
        print final_feature_k
        return final_feature_t, final_feature_k


# 几种距离求解
def eulidSim(vecA, vecB):
    return la.norm(vecA - vecB)


def cosSim(vecA, vecB):
    """
    :param vecA: 行向量
    :param vecB: 行向量
    :return: 返回余弦相似度(范围在0-1之间)
    """
    num = float(vecA * vecB.T)
    denom = la.norm(vecA) * la.norm(vecB)
    cosSim = 0.5 + 0.5 * (num / denom)
    return cosSim


def pearsSim(vecA, vecB):
    if len(vecA) < 3:
        return 1.0
    else:
        return 0.5 + 0.5 * np.corrcoef(vecA, vecB, rowvar=0)[0][1]


if __name__ == '__main__':
    data = pd.read_csv('西瓜数据集3.csv')[['色泽', '根蒂', '敲击', '纹理', '脐部', '触感', '密度', '含糖率', '类别']]
    print data
    f = Filter(data, 1, 0.8, 6)
    f.relief()
    # f.get_final()


评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值