亲和性分析
定义:根据样本个体之间的相似度,确定关系的亲疏。
应用场景
投放广告、推荐商品、寻找有亲缘关系的人
实例:商品推荐
向上销售:向已经购买商品的顾客推荐另一种商品。
规则:如果一个人买了商品X,那么很有可能购买商品Y
判断规则的优劣:支持度(support)和置信度(confidence)
支持度:指数据集中规则应验的次数,也就是给定规则应验的比例,有时候还要进行规范化,即除以规则有效前提下的数量
置信度:指规则的准确率,即符合给定条件(也就是规则中的“如果”)的所有规则里,跟当前规则结论一致的比例有多大。
首先看一下本实例的部分数据集:
面包 | 牛奶 | 奶酪 | 苹果 | 香蕉 |
---|---|---|---|---|
0 | 0 | 1 | 1 | 1 |
1 | 1 | 0 | 1 | 0 |
1 | 0 | 1 | 1 | 0 |
0 | 0 | 1 | 1 | 1 |
0 | 1 | 0 | 0 | 1 |
… | … | … | … | … |
实现简单的排序规则
找出“如果顾客购买了商品X,那么他们可能愿意购买商品Y”这样的规则【一条规则由前提条件和结论组成】
找出这些规则后,再按照上文提到的支持度和置信度来判断规则的优劣,然后进行排序,挑选好的规则进行使用。
下面找出所有的规则,并计算每条规则的support和confidence1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37import numpy as np
file = '/Chapter 1/affinity_dataset.txt'
data = np.loadtxt(file)
from collections import defaultdict
#当字典中的key不存在时,返回默认值,这里是int,也就是0
valid_rules = defaultdict(int) # 有效规则,例如:如果顾客买了商品X,也会买商品Y
invalid_rules = defaultdict(int) # 无效规则,例如:如果顾客买了商品X,也会买商品X或value[Y]==0
num_occurances = defaultdict(int) # 条件相同的规则
features = ['bread','milk','cheese','apple','banana']
num_features = len(features)
for eachdata in data:
for premise in range(num_features):
if eachdata[premise] == 0:
continue #只计算"前提条件"有真实交易的规则
num_occurances[premise] += 1
for conclusion in range(num_features):
if premise == conclusion:
continue
if eachdata[conclusion] == 1:
valid_rules[(premise,conclusion)] += 1
else:
invalid_rules[(premise,conclusion)] += 1
confidence = defaultdict(float)
for premise,conclusion in valid_rules.keys():
confidence[(premise,conclusion)] = valid_rules[(premise,conclusion)]/num_occurances[premise]
for premise,conclusion in confidence:
premise_name = features[premise]
conclusion_name = features[conclusion]
print('如果买%s,也会买%s' % (premise_name,conclusion_name))
print('--support:',valid_rules[(premise,conclusion)])
print('--confidence:',confidence[(premise,conclusion)])
print('')
找到所有规则后,我们要挑选出最佳的规则,这只要根据支持度或置信度进行排序即可:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31'''根据支持度排序'''
from operator import itemgetter
support = valid_rules
sorted_support = sorted(support.items(),key = itemgetter(1),reverse = True)
# 输出支持度最高的五条规则
for i in range(5):
print("Rule ",i+1)
premise,conclusion = sorted_support[i][0]
print('如果买%s,也会买%s' % (features[premise],features[conclusion]))
print('--support:',valid_rules[(premise,conclusion)])
print('--confidence:', confidence[(premise, conclusion)])
Rule 1
# 如果买cheese,也会买banana
# --support: 27
# --confidence: 0.6585365853658537
# Rule 2
# 如果买banana,也会买cheese
# --support: 27
# --confidence: 0.4576271186440678
# Rule 3
# 如果买cheese,也会买apple
# --support: 25
# --confidence: 0.6097560975609756
# Rule 4
# 如果买apple,也会买cheese
# --support: 25
# --confidence: 0.6944444444444444
# Rule 5
# 如果买apple,也会买banana
# --support: 21
# --confidence: 0.5833333333333334
同理,根据置信度confidence排序1
2
3
4
5
6
7
8from operator import itemgetter
sorted_confidence = sorted(confidence.items(),key = itemgetter(1),reverse = True)
for i in range(5):
print("Rule ",i+1)
premise,conclusion = sorted_confidence[i][0]
print('如果买%s,也会买%s' % (features[premise],features[conclusion]))
print('--support:',valid_rules[(premise,conclusion)])
print('--confidence:', confidence[(premise, conclusion)])
根据结果,我们可以看到顾客同时购买banbana和cheese的support和confidence都很高,应用到实际场景中,这两样商品没必要捆绑促销,因为就算不促销,购买的人也很多。
分类
亲和性分析关注的是不同变量的相关性,分类关注的是类别目标这个变量。
分类的目标是,根据已知类别的数据集,经过训练得到分类模型,然后对未知类别的数据进行分类。
实例:Iris植物分类
这是一个经典数据集,一共有150条植物数据,每条数据都给出了四个特征:sepal length;sepal width;petal length;petal width。该数据集一共有三个类别:Iris Setosa;Iris Versicolour;Iris Virginica。我们的目标就是根据植物的特征推测它的种类。
这里用的是OneR算法(one rule),一条规则,也就是只根据训练集中的一个特征实现对数据的分类。注意,这样的准确度不会很高,但是对于某些特定的数据集来说,准确度也是可观的。
这个算法很简单,简单的说就是找到一个特征,根据这个特征进行分类的时候有最高的准确率。具体到实现上,算法要遍历每一个特征的每一个取值,对于每个特征值,统计它在各个类别中出现的次数,找到它出现次数最多的类别,并统计它在其他类别中出现的次数,计算正确分类的准确度,然后对每个特征的准确度进行比较,选取准确度最高的作为分类依据。【值得注意的是,该算法要求特征值都为离散值,所以拿到数据后,对于连续特征值要进行数据离散化。】
下面通过一个例子来具体说明该算法的流程。
身高 | 眼睛大小 | 肤色 | 性别 |
---|---|---|---|
1 | 正常 | 偏白 | 男 |
1 | 较大 | 偏黑 | 男 |
1 | 正常 | 偏黑 | 男 |
1 | 较大 | 偏黑 | 女 |
0 | 正常 | 偏白 | 女 |
0 | 较大 | 偏白 | 女 |
身高作为分类特征:该特征下有0、1两种特征值。特征值为1时,对应的分类为{男,男,男,女},选择划分样本最多的类别作为特征值1的结果,也就是性别为男,但我们明显看到特征值为1时,也有分类结果为女的数据,所以说存在错误率;特征值为0时,划分结果全为女性,可以简单的认为该特征值下的分类结果全为女,该结论可能不正确,但在这里是正确的。
下面计算准确度,按照身高进行划分,0为女,1为男,准确率为$\frac{5}{6}=0.833$
眼睛大小作为分类特征:特征值为正常时,两男一女,我们认为特征值为正常时,分类结果为男性;特征值为较大时,一男两女,我们认为该值的分类结果为女性。准确率为$\frac{4}{6}=0.667$
肤色作为分类特征:特征值为偏白时,一男两女,我们认为特征值为偏白时,分类结果为女性;特征值为偏黑时,两男一女,我们认为该值的分类结果为男性。准确率为$\frac{4}{6}=0.667$
了解了该算法的基本思想后,我们实现该算法并针对本植物数据集进行分类预测。
(1)首先要对数据集的特征值进行离散化处理1
2
3
4
5
6
7
8
9'''导入数据集'''
from sklearn.datasets import load_iris
dataset = load_iris()
data = dataset.data
target = dataset.target
'''数据离散化'''
import numpy as np
attribute_means = data.mean(axis = 0) # 以平均值作为阈值
new_data = np.array(data>attribute_means,dtype = 'int')
(2)计算对某个特征的特征值进行分类和计算错误个数
计算某个特征等于目标特征值时最可能的所属分类,以及错误率
传参:数据集、分类结果、特征下标、特征值
返回:最有可能的分类结果,归类错误的数量1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21from collections import defaultdict
from operator import itemgetter
def feature_value_count(new_data,target,feature_index,value):
class_counts = defaultdict(int)
for eachdata,label in zip(new_data,target):
if eachdata[feature_index] == value:
class_counts[label] += 1
# 将分类的值从大到小排序
sorted_class_counts = sorted(class_counts.items(),key = itemgetter(1),reverse = True)
most_class = sorted_class_counts[0][0] #该分类就是目标分类
# 计算错误分类的数量
sum = 0
for i in range(1,len(sorted_class_counts)):
sum += sorted_class_counts[i][1]
error_counts = sum
return most_class,error_counts
#特征下标为0,特征值为0时,最有可能的所属分类
a = feature_value_count(new_data,target,0,0)
print(a)
#0,30 #最有可能属于0类,这一情况下有30条数据错误归类
(3)计算某个特征划分的正确率(即错误率最低的特征)1
2
3
4
5
6
7
8
9
10
11
12
13
14def min_error(new_data,target,feature_index):
feature_set = set(new_data[:,feature_index]) # 得到该特征的所有特征值取值,set无重复
predictors = {} # 存放该特征下,所有特征值的预测分类
errors = [] #存放错误个数
for each in feature_set:
most_class,error_counts = feature_value_count(new_data,target,feature_index,each) #对于每个特征值,得到最有可能的分类结果以及分类错误的数目
predictors[each] = most_class
errors.append(error_counts)
error_rato = sum(errors)/len(new_data)
return predictors,error_rato
#计算特征下标为0的特征的预测分类以及错误率
a = min_error(new_data,target,0)
print(a)
# ({0: 0, 1: 2}, 0.37333333333333335) #特征值为0时,归类为0,特征值为1时,归类为2,错误率为0.373
(4)“一条规则”,所以遍历所有特征,找出错误率最低的特征作为OneR分类规则1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17def creatModel(new_data,target):
all_predictors = {} # 存放所有特征分类结果
error = {} # 存放每个特征的错误率
for feature_index in range(len(new_data[0])):
predictors,error_rato = min_error(new_data,target,feature_index)
all_predictors[feature_index] = predictors
error[feature_index] = error_rato
sorted_error = sorted(error.items(),key = itemgetter(1),reverse = False)
best_feature_index = sorted_error[0][0]
model = {'分类特征':best_feature_index,'分类结果':all_predictors[best_feature_index]}
accuracy = 1-error[best_feature_index]
return model,accuracy
a = creatModel(new_data,target)
#{'分类特征': 2, '分类结果': {0: 0, 1: 2}}
#准确度:0.667
(5)测试模型1
2
3
4
5
6
7'''利用该模型对新的数据进行预测'''
def OneR_Model(test_data,model):
variable = model['分类特征'] #获取模型的分类特征下标
predictor = model['分类结果'] # 获取模型该特征下的各个特征值对应的分类结果
#new_target = predictor[test_data[variable]]
new_target = np.array([predictor[eachdata[variable]]for eachdata in test_data])
return new_target
机器学习的流程分为模型训练以及测试,绝对不能用测试数据进行模型的训练。
为了方便,我们这里将数据集的数据分为两部分,25%作为测试集,75%作为训练集。1
2
3
4
5
6
7
8
9
10from sklearn.model_selection import train_test_split
# 默认将25%数据作为测试集
data_train,data_test,target_train,target_test = train_test_split(new_data,target,test_size = 0.25,random_state = 14)
model,accuracy = creatModel(data_train,target_train)
print("该模型的准确度为:%.3f" % accuracy)
new_target = OneR_Model(data_test,model)
test_accuracy = np.mean(new_target == target_test)
print('%.3f'% test_accuracy)
#该模型的准确度为:0.670
#测试准确度为:0.658
因为只用了一条规则作为分类依据,这个准确度还算可观。