题目
试编程实现Relief算法,并考察其在西瓜数据集3.0上的运行结果。
分析
Relief是一种过滤式特征选择方法。简单来说就是利用这种方法可以计算出各个特征子集的重要性。我们用 δ j \delta^j δj来度量 j j j属性的重要程度,公式为 δ j = ∑ − d i f f ( x i j , x i , n h j ) 2 + d i f f ( x i j , x i , n m j ) 2 \delta^j = \sum-diff(x_i^j,x^j_{i,nh})^2+diff(x_i^j,x^j_{i,nm})^2 δj=∑−diff(xij,xi,nhj)2+diff(xij,xi,nmj)2。其中 x i j x_i^j xij第i个样本在属性j上的取值。 x i , n h x_{i,nh} xi,nh表示与第i个样本类别相同且距离最近的样本,称为“猜中近邻”; x i , n m x_{i,nm} xi,nm表示与第i个样本类别不同且最近的样本,称为“猜错近邻”。得到这两个点之后,分别计算其与 x i x_i xi的j属性上的距离,然后带入公式。可以这样理解公式,样本在j上的值与“猜中近邻”在j上的值越相近越好,与“猜错近邻”上的值越远越好。
代码
import numpy as np
import matplotlib.pyplot as plt
# 特征字典,后面用到了好多次,干脆当全局变量了
featureDic = {
'色泽': ['浅白', '青绿', '乌黑'],
'根蒂': ['硬挺', '蜷缩', '稍蜷'],
'敲声': ['沉闷', '浊响', '清脆'],
'纹理': ['清晰', '模糊', '稍糊'],
'脐部': ['凹陷', '平坦', '稍凹'],
'触感': ['硬滑', '软粘']}
def getDataSet():
"""
get watermelon data set 3.0 alpha.
:return: 编码好的数据集以及特征的字典。
"""
dataSet = [
['青绿', '蜷缩', '浊响', '清晰', '凹陷', '硬滑', 0.697, 0.460, 1],
['乌黑', '蜷缩', '沉闷', '清晰', '凹陷', '硬滑', 0.774, 0.376, 1],
['乌黑', '蜷缩', '浊响', '清晰', '凹陷', '硬滑', 0.634, 0.264, 1],
['青绿', '蜷缩', '沉闷', '清晰', '凹陷', '硬滑', 0.608, 0.318, 1],
['浅白', '蜷缩', '浊响', '清晰', '凹陷', '硬滑', 0.556, 0.215, 1],
['青绿', '稍蜷', '浊响', '清晰', '稍凹', '软粘', 0.403, 0.237, 1],
['乌黑', '稍蜷', '浊响', '稍糊', '稍凹', '软粘', 0.481, 0.149, 1],
['乌黑', '稍蜷', '浊响', '清晰', '稍凹', '硬滑', 0.437, 0.211, 1],
['乌黑', '稍蜷', '沉闷', '稍糊', '稍凹', '硬滑', 0.666, 0.091, 0],
['青绿', '硬挺', '清脆', '清晰', '平坦', '软粘', 0.243, 0.267, 0],
['浅白', '硬挺', '清脆', '模糊', '平坦', '硬滑', 0.245, 0.057, 0],
['浅白', '蜷缩', '浊响', '模糊', '平坦', '软粘', 0.343, 0.099, 0],
['青绿', '稍蜷', '浊响', '稍糊', '凹陷', '硬滑', 0.639, 0.161, 0],
['浅白', '稍蜷', '沉闷', '稍糊', '凹陷', '硬滑', 0.657, 0.198, 0],
['乌黑', '稍蜷', '浊响', '清晰', '稍凹', '软粘', 0.360, 0.370, 0],
['浅白', '蜷缩', '浊响', '模糊', '平坦', '硬滑', 0.593, 0.042, 0],
['青绿', '蜷缩', '沉闷', '稍糊', '稍凹', '硬滑', 0.719, 0.103, 0]
]
features = ['色泽', '根蒂', '敲声', '纹理', '脐部', '触感', '密度', '含糖量']
# # 每种特征的属性个数
# numList = [] # [3, 3, 3, 3, 3, 2]
# for i in range(len(features) - 2):
# numList.append(len(featureDic[features[i]]))
dataSet = np.array(dataSet)
return dataSet, features
def getDiff(dataSet, i, j, mode=""):
"""
得到数据集dataSet中第i个数据的第j个属性值与其"猜中近邻"第j个属性值的距离。
对于离散型的属性j,若第i个样本的j属性值与其"猜中近邻"的j属性值相同则距离为0,反之为1。
对于连续型的属性j,直接计算差的绝对值。
:param mode: "nh":猜中近邻。 "nm":猜错近邻
:param dataSet: 数据集
:param i: 第i个数据
:param j: 第j个属性
:return: 第i个数据的第j个属性值与其"猜中近邻"第j个属性值的距离
"""
exDataSet = None
if mode == 'nh':
exDataSet = dataSet[dataSet[:, -1] == dataSet[i][-1]]
if mode == 'nm':
exDataSet = dataSet[dataSet[:, -1] != dataSet[i][-1]]
dist = np.inf
if j < 6: # 前六个为离散型数据,后两个是连续型数据。
dist = 1 # 对于离散型数据,初始dist为1,当遇到相同的j属性值时,置零。
for k in range(len(exDataSet)):
if k == i: # 遇到第i个样本跳过。
continue
if exDataSet[k][j] == dataSet[i][j]:
dist = 0
break
else:
for k in range(len(exDataSet)):
if k == i:
continue
sub = abs(float(exDataSet[k][j]) - float(dataSet[i][j]))
if sub < dist:
dist = sub
return dist
def Relief(dataSet):
"""
计算Relief度量。
:param dataSet:
:return:
"""
m, n = dataSet.shape
r = [] # 相关统计量
for j in range(n - 1):
rj = 0
for i in range(m):
diffNh = getDiff(dataSet, i, j, mode='nh')
diffNm = getDiff(dataSet, i, j, mode='nm')
rj += -(diffNh**2) + (diffNm**2)
r.append(rj)
return r
def main():
dataSet, features = getDataSet()
rf = np.array(Relief(dataSet))
index = rf.argsort()
print(rf[index])
print(np.array(features)[index])
if __name__ == '__main__':
main()
输出结果
经过了从小到大的排序:
对应属性越大的度量值表示,其样本与同类的样本离得近,与不同类的样本离得远,也以为着这种属性更好。可以通过这种方法选择更好的属性,即过滤式选择特征。