Relief算法python实现

文章目录
一、算法流程图
二、代码步骤
1.第一步:定义类和init方法
2.第二步:数据处理
3.第三步:通过计算距离,找出猜错近邻和猜对近邻
4.第四步:计算特征权重
5.第五步:根据权重过滤式选取特征
6.第六步:选取最终特征
7.第七步:定义主函数
三、为什么要写relief算法?以及解决什么问题?

算法流程

relief算法流程图.png

第一步:定义类和init方法

方便函数调用参数,只需要一次向类中导入参数即可,不用重复导入参数

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))#round函数:四舍五入
        self.__t = t
        self.__k = k

第二步:数据处理

将读取到的数据特征值中离散型处理成连续型,比如色泽:青绿,属于离散型,密度:0.679,属于连续型。

    def get_data(self):
        new_data = pd.DataFrame()#建立一个空二维表
        for one in self.__feature[:-1]:#遍历循环每个特征
            col = self.__data[one]#读取全部样本中其中一个特征
            # 判断读取到的特征是否全为数值类,如果字符串中全为数字  ,则不作改变写进新的二维表new_data里面,否则处理成数值类型写进二维表
            if (str(list(col)[0]).split(".")[0]).isdigit() or str(list(col)[0]).isdigit()\
            or (str(list(col)[0]).split('-')[-1]).split(".")[-1].isdigit():#isdigit函数:如果是字符串包含数字返回ture,否则返回false
                new_data[one] = self.__data[one]
            else:
                keys = list(set(list(col)))#set函数:删除重复值,得到一个特征的类别,如色泽:青绿、浅白、乌黑
                values = list(range(len(keys)))#遍历循环len(keys),色泽特征为例,三个特征类别,则得到三个数0、1、2
                new = dict(zip(keys, values))#dict函数就是创建一个字典,zip函数矩阵中的元素对应打包成一个元组列表
                new_data[one] = self.__data[one].map(new)#map函数:将new: {'青绿': 0, '浅白': 1, '乌黑': 2}在col列表里做一个映射
        new_data[self.__feature[-1]] = self.__data[self.__feature[-1]]#瓜的类别属性不做改变
        return new_data

get_data(self)函数:经过处理数据,用0、1、2这样的数字去取代特征的中文类别,拿色泽为例,色泽=['青绿','浅白','乌黑'] 替换为 色泽=['0' , '1' , '2'],完成所有替换后返回一个连续型数据类型的数据集。

第三步:通过计算距离,找出猜错近邻和猜对近邻

    def get_neighbors(self, row):
        df = self.get_data()
        #row是一行一行(一个一个)样本
        row_type = row[df.columns[-1]]#矩阵最后一列:瓜的类别,如好瓜、坏瓜、模糊
        #下面进行分类,与读取到的样本类型相同的分为  “同类”,不相同的分为 “异类”,储存在两个数据集中
        right_df = df[df[df.columns[-1]] == row_type].drop(columns=[df.columns[-1]])#筛选出数据集类别与读取样本同类的样本,删除数据集最后一列
        #将删除后的数据集储存在right_df中,原数据集df保持不变。
        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))#lambda函数:定义一个隐函数,  mat函数:转换为矩阵,方便线性代数的操作
        #eulidsim函数是自己定义的一个计算距离的函数
        right_sim = right_df.apply(f, axis=1)#apply函数:就是将right_df里面的每个变量,axis=1按行的顺序计算,
        #apply函数解释链接: https://www.cnblogs.com/xiaodongsuibi/p/8927688.html
        right_sim_two = right_sim.drop(right_sim.idxmin())#idxmin()函数:获取某行最小的序列号,里面删除了读取样本本身的那个数据
        #之所以在同类中删除距离最小的那个数据,是因为在原数据集中,包括了本身的那个数据
        wrong_sim = wrong_df.apply(f, axis=1)
        return right_sim_two.idxmin(), wrong_sim.idxmin()

get_neighbors()函数:随机从数据集中选取一个样本X,识别样本X所属的类别,如好瓜、坏瓜、模糊。在数据集中将与样本X同类的数据分类为同类数据集,不同类的分为异类数据集,得到两个数据集。计算样本X与两个数据集中的所有样本的欧式距离,因为同类样本数据集中有一个是数据样本X,要将这个样本删了,因为返回最近近邻的数据时,样本X与样本X的距离肯定是最小的。然后分别返回两个数据集中与样本X距离最近的数据样本,同类数据集中的最近距离样本即为猜对近邻,异类数据集中的最近距离样本即为猜错近邻。

第四步:计算特征权重

    def get_weight(self, feature, index, NearHit, NearMiss):
        data = self.__data.drop(self.__feature[-1], axis=1)#西瓜数据集
        row = data.iloc[index]#选取到的数据样本,命名为样本X
        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)#猜错近邻特征权重
        else:
            # 离散型特征距离计算方式:
            right = 0 if row[feature] == nearhit[feature] else 1#如果猜对近邻特征与样本X一样。则返回0,不一样返回1
            wrong = 0 if row[feature] == nearmiss[feature] else 1#如果猜错近邻特征与样本X一样。则返回0,不一样返回1
        w = wrong - right#right越小,说明两个同类样本的特征越相似,反之,则同类样本差距越大。
                         #wrong越小,说明两个异类样本的特征越相似,反之,则异类样本差距越大。
        #w越大,说明wrong越大,ringt越小,则表示同类的样本间,该特征的数值差距小,不同类的差距大,说明以该特征区分不同类别,比较容易。
        return w

计算出每个数据样本中每个特征的权重w

第五步:根据权重过滤式选取特征

    def relief(self):
        sample = self.get_data()
        m, n = np.shape(self.__data)  # m为行数,n为列数
        score = []
        sample_index = random.sample(range(0, m), self.__sample_num)#random.sample函数:从指定序列中随机获取指定长度的片断。sample函数不会修改原有序列。
        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'])
        print("f_w:",f_w)
        final_feature_t = f_w[f_w['weight'] > self.__t]#设置阈值,将大于阈值的属性筛选出来
        print ("final_feature_t:",final_feature_t)
        final_feature_k = f_w.sort_values('weight').head(self.__k)#sort_values函数:排序函数,https://blog.csdn.net/qq_24753293/article/details/80692679
        print ("final_feature_k:",final_feature_k)
        return final_feature_t, final_feature_k

将数据集中的平均特征权重转换成一个表格形式,方便我们研究数据,设置一个阈值,将符合我们预期的特征筛选出来并且将平均特征权重做一个排序。到这里为止,我们就对数据集中的特征做了一个筛选,返回出我们需要的特征。

第七步:定义主函数

if __name__ == '__main__':
    data = pd.read_csv('1.csv',encoding="gbk")[['色泽', '根蒂', '敲声', '纹理', '脐部', '触感', '密度', '含糖率', '类别']]
    f = Filter(data, 1, 0.2, 8)
    f.relief()
    f.get_final()

为什么要写relief算法?以及解决什么问题?

为什么写relief算法,因为relief算法简单,可操作性强。
解决什么问题?当我们遇到维数灾难问题的时候,主要是因为属性太多,我们需要将一些影响极小的属性进行一个剔除,所以就引入特征选择,这其实跟降维差不多。
然而relief算法就是特征选择的一个方法,通过对所有样本的一个过滤,过滤后给每个特征赋予一个权重,我们根据一个权重的大小排序来筛选出对类别影响大的特征。
以后的运用:比如我们建立一个模型,去判断不同属性对信用程度的一个影响,例如其中属性包括财富、性格、颜值、年龄等,我们给这些属性一个权重,比如颜值这个属性的权重极小,也就是说你颜值如何跟你守不守信关系极小,所以我们根据计算出来的属性权重值做一个排序,将一些对我们分类的目的影响极小的属性进行一个剔除。

附上代码+数据:https://gitee.com/liu_ji_duan/DuanGe/tree/master/%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0/RELIEF%E7%AE%97%E6%B3%95%E5%AE%9E%E7%8E%B0

你可能感兴趣的:(Relief算法python实现)