算法python实现_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算法就是特征选择的一个方法,通过对所有样本的一个过滤,过滤后给每个特征赋予一个权重,我们根据一个权重的大小排序来筛选出对类别影响大的特征。

以后的运用:比如我们建立一个模型,去判断不同属性对信用程度的一个影响,例如其中属性包括财富、性格、颜值、年龄等,我们给这些属性一个权重,比如颜值这个属性的权重极小,也就是说你颜值如何跟你守不守信关系极小,所以我们根据计算出来的属性权重值做一个排序,将一些对我们分类的目的影响极小的属性进行一个剔除。