项目 3: 创建用户分类

白昼怎懂夜的黑 提交于 2019-12-06 15:05:54

欢迎来到机器学习工程师纳米学位的第三个项目!在这个notebook文件中,有些模板代码已经提供给你,但你还需要实现更多的功能来完成这个项目。除非有明确要求,你无须修改任何已给出的代码。以'练习'开始的标题表示接下来的代码部分中有你必须要实现的功能。每一部分都会有详细的指导,需要实现的部分也会在注释中以'TODO'标出。请仔细阅读所有的提示!

除了实现代码外,你还必须回答一些与项目和你的实现有关的问题。每一个需要你回答的问题都会以'问题 X'为标题。请仔细阅读每个问题,并且在问题后的'回答'文字框中写出完整的答案。我们将根据你对问题的回答和撰写代码所实现的功能来对你提交的项目进行评分。

提示:Code 和 Markdown 区域可通过 Shift + Enter 快捷键运行。此外,Markdown可以通过双击进入编辑模式。

开始

在这个项目中,你将分析一个数据集的内在结构,这个数据集包含很多客户真对不同类型产品的年度采购额(用金额表示)。这个项目的任务之一是如何最好地描述一个批发商不同种类顾客之间的差异。这样做将能够使得批发商能够更好的组织他们的物流服务以满足每个客户的需求。

这个项目的数据集能够在UCI机器学习信息库中找到.因为这个项目的目的,分析将不会包括'Channel'和'Region'这两个特征——重点集中在6个记录的客户购买的产品类别上。

运行下面的的代码单元以载入整个客户数据集和一些这个项目需要的Python库。如果你的数据集载入成功,你将看到后面输出数据集的大小。

In [41]:
# 检查你的Python版本
from sys import version_info
if version_info.major != 2 and version_info.minor != 7:
    raise Exception('请使用Python 2.7来完成此项目')
In [42]:
# 引入这个项目需要的库
import numpy as np
import pandas as pd
import visuals as vs
from IPython.display import display # 使得我们可以对DataFrame使用display()函数

# 设置以内联的形式显示matplotlib绘制的图片(在notebook中显示更美观)
%matplotlib inline

# 载入整个客户数据集
try:
    data = pd.read_csv("customers.csv")
    data.drop(['Region', 'Channel'], axis = 1, inplace = True)
    #这里的inplace参数代表
    print "Wholesale customers dataset has {} samples with {} features each.".format(*data.shape)
except:
    print "Dataset could not be loaded. Is the dataset missing?"
Wholesale customers dataset has 440 samples with 6 features each.

分析数据

在这部分,你将开始分析数据,通过可视化和代码来理解每一个特征和其他特征的联系。你会看到关于数据集的统计描述,考虑每一个属性的相关性,然后从数据集中选择若干个样本数据点,你将在整个项目中一直跟踪研究这几个数据点。

运行下面的代码单元给出数据集的一个统计描述。注意这个数据集包含了6个重要的产品类型:'Fresh''Milk''Grocery''Frozen''Detergents_Paper''Delicatessen'。想一下这里每一个类型代表你会购买什么样的产品。

In [43]:
# 显示数据集的一个描述
display(data.describe())
 FreshMilkGroceryFrozenDetergents_PaperDelicatessen
count440.000000440.000000440.000000440.000000440.000000440.000000
mean12000.2977275796.2659097951.2772733071.9318182881.4931821524.870455
std12647.3288657380.3771759503.1628294854.6733334767.8544482820.105937
min3.00000055.0000003.00000025.0000003.0000003.000000
25%3127.7500001533.0000002153.000000742.250000256.750000408.250000
50%8504.0000003627.0000004755.5000001526.000000816.500000965.500000
75%16933.7500007190.25000010655.7500003554.2500003922.0000001820.250000
max112151.00000073498.00000092780.00000060869.00000040827.00000047943.000000

练习: 选择样本

为了对客户有一个更好的了解,并且了解代表他们的数据将会在这个分析过程中如何变换。最好是选择几个样本数据点,并且更为详细地分析它们。在下面的代码单元中,选择三个索引加入到索引列表indices中,这三个索引代表你要追踪的客户。我们建议你不断尝试,直到找到三个明显不同的客户。

In [44]:
# TODO:从数据集中选择三个你希望抽样的数据点的索引
indices = [1,14,168]

# 为选择的样本建立一个DataFrame
samples = pd.DataFrame(data.loc[indices], columns = data.keys()).reset_index(drop = True)
print "Chosen samples of wholesale customers dataset:"
display(samples)
Chosen samples of wholesale customers dataset:
 FreshMilkGroceryFrozenDetergents_PaperDelicatessen
0705798109568176232931776
12465394651209129450582168
25809735803139379429

问题 1

在你看来你选择的这三个样本点分别代表什么类型的企业(客户)?对每一个你选择的样本客户,通过它在每一种产品类型上的花费与数据集的统计描述进行比较,给出你做上述判断的理由。

提示: 企业的类型包括超市、咖啡馆、零售商以及其他。注意不要使用具体企业的名字,比如说在描述一个餐饮业客户时,你不能使用麦当劳。

回答:第一个样本大概率是咖啡馆,因为milk和Grocery的购买额高于均值,但明显不偏好fresh,这与我们的常识一致; 第二个样本大概率是餐厅,因为生鲜和食杂的额度大大高于均值; 第三个样本大概率是生鲜和冻品类的零售客户,因为各项购买额均小于均值,但生鲜和冻品有所偏好。

练习: 特征相关性

一个有趣的想法是,考虑这六个类别中的一个(或者多个)产品类别,是否对于理解客户的购买行为具有实际的相关性。也就是说,当用户购买了一定数量的某一类产品,我们是否能够确定他们必然会成比例地购买另一种类的产品。有一个简单的方法可以检测相关性:我们用移除了某一个特征之后的数据集来构建一个监督学习(回归)模型,然后用这个模型去预测那个被移除的特征,再对这个预测结果进行评分,看看预测结果如何。

在下面的代码单元中,你需要实现以下的功能:

  • 使用DataFrame.drop函数移除数据集中你选择的不需要的特征,并将移除后的结果赋值给new_data
  • 使用sklearn.model_selection.train_test_split将数据集分割成训练集和测试集。
    • 使用移除的特征作为你的目标标签。设置test_size0.25并设置一个random_state
  • 导入一个DecisionTreeRegressor(决策树回归器),设置一个random_state,然后用训练集训练它。
  • 使用回归器的score函数输出模型在测试集上的预测得分。
In [45]:
# TODO:为DataFrame创建一个副本,用'drop'函数丢弃一个特征
print data.keys()
y_data = data['Detergents_Paper']
new_data = data.drop('Detergents_Paper', axis = 1)
display (new_data.describe())

# TODO:使用给定的特征作为目标,将数据分割成训练集和测试集
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(new_data, y_data, test_size=0.25, random_state=40)

# TODO:创建一个DecisionTreeRegressor(决策树回归器)并在训练集上训练它
from sklearn.tree import DecisionTreeRegressor
regressor =  DecisionTreeRegressor(random_state=0)
regressor.fit(X_train,y_train)

# TODO:输出在测试集上的预测得分
score = regressor.score(X_test, y_test)
print score
Index([u'Fresh', u'Milk', u'Grocery', u'Frozen', u'Detergents_Paper',
       u'Delicatessen'],
      dtype='object')
 FreshMilkGroceryFrozenDelicatessen
count440.000000440.000000440.000000440.000000440.000000
mean12000.2977275796.2659097951.2772733071.9318181524.870455
std12647.3288657380.3771759503.1628294854.6733332820.105937
min3.00000055.0000003.00000025.0000003.000000
25%3127.7500001533.0000002153.000000742.250000408.250000
50%8504.0000003627.0000004755.5000001526.000000965.500000
75%16933.7500007190.25000010655.7500003554.2500001820.250000
max112151.00000073498.00000092780.00000060869.00000047943.000000
0.787994241832

问题 2

你尝试预测哪一个特征?预测的得分是多少?这个特征对于区分用户的消费习惯来说必要吗?
提示: 决定系数(coefficient of determination), R^2,结果在0到1之间,1表示完美拟合,一个负的R^2表示模型不能够拟合数据。

回答:0.788.当选择'Detergents_Paper'特征作为标签时,得到这个分数。曾尝试过Fresh,但决定系数为负值。明显Detergents_Paper'这个特征对于区分用户的消费习惯来说不是必要的,该特征与其它特征的有一定相关性,决定系数较高,该变量可以被其他变量来预测,说明这个变量可以通过其他变量通过一些变化得到。

可视化特征分布

为了能够对这个数据集有一个更好的理解,我们可以对数据集中的每一个产品特征构建一个散布矩阵(scatter matrix)。如果你发现你在上面尝试预测的特征对于区分一个特定的用户来说是必须的,那么这个特征和其它的特征可能不会在下面的散射矩阵中显示任何关系。相反的,如果你认为这个特征对于识别一个特定的客户是没有作用的,那么通过散布矩阵可以看出在这个数据特征和其它特征中有关联性。运行下面的代码以创建一个散布矩阵。

In [46]:
# 对于数据中的每一对特征构造一个散布矩阵
pd.plotting.scatter_matrix(data, alpha = 0.3, figsize = (14,8), diagonal = 'kde');
In [47]:
import seaborn as sns
sns.heatmap(data.corr(), annot=True)
Out[47]:
<matplotlib.axes._subplots.AxesSubplot at 0x27229780>

问题 3

这里是否存在一些特征他们彼此之间存在一定程度相关性?如果有请列出。这个结果是验证了还是否认了你尝试预测的那个特征的相关性?这些特征的数据是怎么分布的?

提示: 这些数据是正态分布(normally distributed)的吗?大多数的数据点分布在哪?

回答:我认为存在一定特征相关性。如'Detergents_Paper'和milk,'Detergents_Paper'和Grocery,Grocery和Milk具有明显相关性。 不是正态分布的,在上图矩阵的对角线上,可以呈现出正偏态分布特征。

数据预处理

在这个部分,你将通过在数据上做一个合适的缩放,并检测异常点(你可以选择性移除)将数据预处理成一个更好的代表客户的形式。预处理数据是保证你在分析中能够得到显著且有意义的结果的重要环节。

练习: 特征缩放

如果数据不是正态分布的,尤其是数据的平均数和中位数相差很大的时候(表示数据非常歪斜)。这时候通常用一个非线性的缩放是很合适的(英文原文) — 尤其是对于金融数据。一种实现这个缩放的方法是使用Box-Cox 变换,这个方法能够计算出能够最佳减小数据倾斜的指数变换方法。一个比较简单的并且在大多数情况下都适用的方法是使用自然对数。

在下面的代码单元中,你将需要实现以下功能:

  • 使用np.log函数在数据 data 上做一个对数缩放,然后将它的副本(不改变原始data的值)赋值给log_data
  • 使用np.log函数在样本数据 samples 上做一个对数缩放,然后将它的副本赋值给log_samples
In [48]:
# TODO:使用自然对数缩放数据
log_data = np.log(data)

# TODO:使用自然对数缩放样本数据
log_samples = np.log(samples)

# 为每一对新产生的特征制作一个散射矩阵
pd.plotting.scatter_matrix(log_data, alpha = 0.3, figsize = (14,8), diagonal = 'kde');

观察

在使用了一个自然对数的缩放之后,数据的各个特征会显得更加的正态分布。对于任意的你以前发现有相关关系的特征对,观察他们的相关关系是否还是存在的(并且尝试观察,他们的相关关系相比原来是变强了还是变弱了)。

运行下面的代码以观察样本数据在进行了自然对数转换之后如何改变了。

In [49]:
# 展示经过对数变换后的样本数据
display(log_samples)
 FreshMilkGroceryFrozenDetergents_PaperDelicatessen
08.8617759.1911589.1661797.4742058.0995547.482119
110.1126549.1553569.4002175.6835808.5287267.681560
28.6671646.5998706.6883557.2392154.3694486.061457

练习: 异常值检测

对于任何的分析,在数据预处理的过程中检测数据中的异常值都是非常重要的一步。异常值的出现会使得把这些值考虑进去后结果出现倾斜。这里有很多关于怎样定义什么是数据集中的异常值的经验法则。这里我们将使用Tukey的定义异常值的方法:一个异常阶(outlier step)被定义成1.5倍的四分位距(interquartile range,IQR)。一个数据点如果某个特征包含在该特征的IQR之外的特征,那么该数据点被认定为异常点。

在下面的代码单元中,你需要完成下面的功能:

  • 将指定特征的25th分位点的值分配给Q1。使用np.percentile来完成这个功能。
  • 将指定特征的75th分位点的值分配给Q3。同样的,使用np.percentile来完成这个功能。
  • 将指定特征的异常阶的计算结果赋值给step.
  • 选择性地通过将索引添加到outliers列表中,以移除异常值。

注意: 如果你选择移除异常值,请保证你选择的样本点不在这些移除的点当中! 一旦你完成了这些功能,数据集将存储在good_data中。

In [50]:
# 对于每一个特征,找到值异常高或者是异常低的数据点

bad_data_index = []
for feature in log_data.keys():
    
    # TODO:计算给定特征的Q1(数据的25th分位点)
    Q1 = np.percentile(log_data[feature],25)
    
    # TODO:计算给定特征的Q3(数据的75th分位点)
    Q3 = np.percentile(log_data[feature],75)
    
    # TODO:使用四分位范围计算异常阶(1.5倍的四分位距)
    step = 1.5*(Q3-Q1)
    
    # 显示异常点
    print "Data points considered outliers for the feature '{}':".format(feature)
    bad_data =log_data[~((log_data[feature] >= Q1 - step) & (log_data[feature] <= Q3 + step))]
    display(bad_data)
    bad_data_index.extend(list(bad_data.index))
    
a = list(set(bad_data_index))
b = list(np.sort(a))

for i in b:
    bad_data_index.remove(i)
print list(bad_data_index)
# 可选:选择你希望移除的数据点的索引
outliers  = [65,66,75,128,154]
bad_samples = pd.DataFrame(data.loc[outliers], columns = data.keys())
display(bad_samples)


# 如果选择了的话,移除异常点
good_data = log_data.drop(log_data.index[outliers]).reset_index(drop = True)
Data points considered outliers for the feature 'Fresh':
 FreshMilkGroceryFrozenDetergents_PaperDelicatessen
654.4426519.95032310.7326513.58351910.0953887.260523
662.1972257.3356348.9115305.1647868.1513333.295837
815.3890729.1632499.5751925.6454478.9641845.049856
951.0986127.9793398.7406576.0867755.4071726.563856
963.1354947.8694029.0018394.9767348.2620435.379897
1284.9416429.0878348.2487914.9558276.9679091.098612
1715.29831710.1605309.8942456.4785109.0794348.740337
1935.1929578.1562239.9179826.8658918.6337316.501290
2182.8903728.9231919.6293807.1585148.4757468.759669
3045.0814048.91731110.1175106.4248699.3744137.787382
3055.4930619.4680019.0883996.6833618.2710375.351858
3381.0986125.8081428.8566619.6550902.7080506.309918
3534.7621748.7425749.9618985.4293469.0690077.013016
3555.2470246.5889267.6068855.5012585.2149364.844187
3573.6109187.15070110.0110864.9199818.8168534.700480
4124.5747118.1900779.4254524.5849677.9963174.127134
Data points considered outliers for the feature 'Milk':
 FreshMilkGroceryFrozenDetergents_PaperDelicatessen
8610.03998311.20501310.3770476.8946709.9069816.805723
986.2205904.7184996.6567276.7968244.0253524.882802
1546.4329404.0073334.9199814.3174881.9459102.079442
35610.0295034.8978405.3844958.0573772.1972256.306275
Data points considered outliers for the feature 'Grocery':
 FreshMilkGroceryFrozenDetergents_PaperDelicatessen
759.9231927.0361481.0986128.3909491.0986126.882437
1546.4329404.0073334.9199814.3174881.9459102.079442
Data points considered outliers for the feature 'Frozen':
 FreshMilkGroceryFrozenDetergents_PaperDelicatessen
388.4318539.6632619.7237033.4965088.8473606.070738
578.5972979.2036189.2578923.6375868.9322137.156177
654.4426519.95032310.7326513.58351910.0953887.260523
14510.0005699.03408010.4571433.7376709.4407388.396155
1757.7591878.9676329.3821063.9512448.3418877.436617
2646.9782149.1777149.6450414.1108748.6961767.142827
32510.3956509.7281819.51973511.0164797.1483468.632128
4208.4020078.5690269.4900153.2188768.8273217.239215
4299.0603317.4673718.1831183.8501484.4308177.824446
4397.9327217.4372067.8280384.1743876.1675163.951244
Data points considered outliers for the feature 'Detergents_Paper':
 FreshMilkGroceryFrozenDetergents_PaperDelicatessen
759.9231927.0361481.0986128.3909491.0986126.882437
1619.4281906.2915695.6454476.9957661.0986127.711101
Data points considered outliers for the feature 'Delicatessen':
 FreshMilkGroceryFrozenDetergents_PaperDelicatessen
662.1972257.3356348.9115305.1647868.1513333.295837
1097.2485049.72489910.2745686.5117456.7286291.098612
1284.9416429.0878348.2487914.9558276.9679091.098612
1378.0349558.9971479.0218406.4937546.5806393.583519
14210.5196468.8751479.0183328.0047002.9957321.098612
1546.4329404.0073334.9199814.3174881.9459102.079442
18310.51452910.6908089.91195210.5059995.47646410.777768
1845.7899606.8221978.4574434.3040655.8111412.397895
1877.7989338.9874479.1920758.7433728.1487351.098612
2036.3681876.5294197.7034596.1506036.8606642.890372
2336.8710918.5139888.1065156.8426836.0137151.945910
28510.6029656.4614688.1886896.9488976.0776422.890372
28910.6639665.6559926.1548587.2356193.4657363.091042
3437.4318928.84850910.1779327.2834489.6465933.610918
[154, 65, 75, 66, 128, 154]
 FreshMilkGroceryFrozenDetergents_PaperDelicatessen
6585209594582836242311423
66915347417175346827
75203981137344073975
1281408847382314210623
154622551377578

问题 4

请列出所有在多于一个特征下被看作是异常的数据点。这些点应该被从数据集中移除吗?为什么?把你认为需要移除的数据点全部加入到到outliers变量中。

回答:65,66,75,128,154.这些点在多个特征中都显示为异常数据,如果仅从单一特征判断异常数据的话,那异常数据量达到了40余个,相对于总数量440的样本来说,占比过大,一旦移除之后,容易造成信息损失,因此判断2个以上特征同时异常才判断为异常数据是合理的,我认为应当被移除。

特征转换

在这个部分中你将使用主成分分析(PCA)来分析批发商客户数据的内在结构。由于使用PCA在一个数据集上会计算出最大化方差的维度,我们将找出哪一个特征组合能够最好的描绘客户。

练习: 主成分分析(PCA)

既然数据被缩放到一个更加正态分布的范围中并且我们也移除了需要移除的异常点,我们现在就能够在good_data上使用PCA算法以发现数据的哪一个维度能够最大化特征的方差。除了找到这些维度,PCA也将报告每一个维度的解释方差比(explained variance ratio)--这个数据有多少方差能够用这个单独的维度来解释。注意PCA的一个组成部分(维度)能够被看做这个空间中的一个新的“特征”,但是它是原来数据中的特征构成的。

在下面的代码单元中,你将要实现下面的功能:

  • 导入sklearn.decomposition.PCA并且将good_data用PCA并且使用6个维度进行拟合后的结果保存到pca中。
  • 使用pca.transformlog_samples进行转换,并将结果存储到pca_samples中。
In [51]:
# TODO:通过在good_data上使用PCA,将其转换成和当前特征数一样多的维度
from sklearn.decomposition import PCA
pca = PCA(n_components=6)
pca.fit(good_data)
# TODO:使用上面的PCA拟合将变换施加在log_samples上
pca_samples = pca.transform(log_samples)

# 生成PCA的结果图
pca_results = vs.pca_results(good_data, pca)

问题 5

数据的第一个和第二个主成分 总共 表示了多少的方差? 前四个主成分呢?使用上面提供的可视化图像,讨论从用户花费的角度来看前四个主要成分的消费行为最能代表哪种类型的客户并给出你做出判断的理由。

提示: 某一特定维度上的正向增长对应正权特征的增长负权特征的减少。增长和减少的速率和每个特征的权重相关。参考资料(英文)

回答: 前两个主成分0.72;前4个主成分表示了0.93的方差。其中后三个主成分有一定的相关性。通过第一主成分,第二三四主成分能明显区别两类用户。 第一个最大可能是咖啡店,因为该类客户特征权重最大的是负权特征是洗涤用品、其次是食杂,说明该类客户同时购买洗涤、食杂和牛奶的可能性较高; 第二个最大可能是食品零售店,以生鲜、冻品、熟食的权重绝对值较大,说明该类客户同时购买此类产品的偏好较高; 第三个主成分在第二个的基础上,最大的两个权重,熟食正相关,生鲜负相关,说明此类客户倾向于购买熟食和冻品,但同时不会购买生鲜的可能性较高。 第四个可能是冻品零售商,因为冻品的正相关较高,同时不会购买熟食的可能性较高。

观察

运行下面的代码,查看经过对数转换的样本数据在进行一个6个维度的主成分分析(PCA)之后会如何改变。观察样本数据的前四个维度的数值。考虑这和你初始对样本点的解释是否一致。

In [52]:
# 展示经过PCA转换的sample log-data
display(pd.DataFrame(np.round(pca_samples, 4), columns = pca_results.index.values))
 Dimension 1Dimension 2Dimension 3Dimension 4Dimension 5Dimension 6
0-1.7887-0.81230.2315-0.00360.1194-0.2106
1-2.3388-0.9013-1.1515-1.6713-0.0485-0.0739
23.27850.90780.1302-0.3310-0.2375-0.2022

练习:降维

当使用主成分分析的时候,一个主要的目的是减少数据的维度,这实际上降低了问题的复杂度。当然降维也是需要一定代价的:更少的维度能够表示的数据中的总方差更少。因为这个,累计解释方差比(cumulative explained variance ratio)对于我们确定这个问题需要多少维度非常重要。另外,如果大部分的方差都能够通过两个或者是三个维度进行表示的话,降维之后的数据能够被可视化。

在下面的代码单元中,你将实现下面的功能:

  • good_data用两个维度的PCA进行拟合,并将结果存储到pca中去。
  • 使用pca.transformgood_data进行转换,并将结果存储在reduced_data中。
  • 使用pca.transformlog_samples进行转换,并将结果存储在pca_samples中。
In [53]:
# TODO:通过在good data上进行PCA,将其转换成两个维度
pca = PCA(n_components=2)
pca.fit(good_data)
# TODO:使用上面训练的PCA将good data进行转换
reduced_data = pca.transform(good_data)

# TODO:使用上面训练的PCA将log_samples进行转换
pca_samples = pca.transform(log_samples)

# 为降维后的数据创建一个DataFrame
reduced_data = pd.DataFrame(reduced_data, columns = ['Dimension 1', 'Dimension 2'])

观察

运行以下代码观察当仅仅使用两个维度进行PCA转换后,这个对数样本数据将怎样变化。观察这里的结果与一个使用六个维度的PCA转换相比较时,前两维的数值是保持不变的。

In [54]:
# 展示经过两个维度的PCA转换之后的样本log-data
display(pd.DataFrame(np.round(pca_samples, 4), columns = ['Dimension 1', 'Dimension 2']))
 Dimension 1Dimension 2
0-1.7887-0.8123
1-2.3388-0.9013
23.27850.9078

可视化一个双标图(Biplot)

双标图是一个散点图,每个数据点的位置由它所在主成分的分数确定。坐标系是主成分(这里是Dimension 1 和 Dimension 2)。此外,双标图还展示出初始特征在主成分上的投影。一个双标图可以帮助我们理解降维后的数据,发现主成分和初始特征之间的关系。

运行下面的代码来创建一个降维后数据的双标图。

In [55]:
# Create a biplot
vs.biplot(good_data, reduced_data, pca)
Out[55]:
<matplotlib.axes._subplots.AxesSubplot at 0x229b7ac8>

观察

一旦我们有了原始特征的投影(红色箭头),就能更加容易的理解散点图每个数据点的相对位置。

在这个双标图中,哪些初始特征与第一个主成分有强关联?哪些初始特征与第二个主成分相关联?你观察到的是否与之前得到的 pca_results 图相符?

聚类

在这个部分,你讲选择使用K-Means聚类算法或者是高斯混合模型聚类算法以发现数据中隐藏的客户分类。然后,你将从簇中恢复一些特定的关键数据点,通过将它们转换回原始的维度和规模,从而理解他们的含义。

问题 6

使用K-Means聚类算法的优点是什么?使用高斯混合模型聚类算法的优点是什么?基于你现在对客户数据的观察结果,你选用了这两个算法中的哪一个,为什么?

回答:K-Means优点是:计算速度快、时间短,易解释,聚类效果还不错;但缺点主要是需要提前确定K值,对异常值极度敏感。 高斯混合模型聚类算法的优点是聚类输出的信息量更大,理论上可以拟合任何连续的概率密度函数。 我会选高斯混合模型,因为现有客户数据在做了Box-Cox 变换后,在各个维度都呈现了较为明显的正态分布特征。

练习: 创建聚类

针对不同情况,有些问题你需要的聚类数目可能是已知的。但是在聚类数目不作为一个先验知道的情况下,我们并不能够保证某个聚类的数目对这个数据是最优的,因为我们对于数据的结构(如果存在的话)是不清楚的。但是,我们可以通过计算每一个簇中点的轮廓系数来衡量聚类的质量。数据点的轮廓系数衡量了它与分配给他的簇的相似度,这个值范围在-1(不相似)到1(相似)。平均轮廓系数为我们提供了一种简单地度量聚类质量的方法。

在接下来的代码单元中,你将实现下列功能:

  • reduced_data上使用一个聚类算法,并将结果赋值到clusterer,需要设置 random_state 使得结果可以复现。
  • 使用clusterer.predict预测reduced_data中的每一个点的簇,并将结果赋值到preds
  • 使用算法的某个属性值找到聚类中心,并将它们赋值到centers
  • 预测pca_samples中的每一个样本点的类别并将结果赋值到sample_preds
  • 导入sklearn.metrics.silhouette_score包并计算reduced_data相对于preds的轮廓系数。
    • 将轮廓系数赋值给score并输出结果。
In [56]:
# TODO:在降维后的数据上使用你选择的聚类算法
from sklearn.mixture import GaussianMixture


clusterer = GaussianMixture(n_components=2,random_state = 40)
clusterer.fit(reduced_data)
# TODO:预测每一个点的簇
preds = clusterer.predict(reduced_data)

# TODO:找到聚类中心
centers = clusterer.means_ 

# TODO:预测在每一个转换后的样本点的类
sample_preds = clusterer.predict(pca_samples)
from sklearn.metrics import silhouette_score
# TODO:计算选择的类别的平均轮廓系数(mean silhouette coefficient)
score = silhouette_score(reduced_data,preds)
print score
0.421916846463

问题 7

汇报你尝试的不同的聚类数对应的轮廓系数。在这些当中哪一个聚类的数目能够得到最佳的轮廓系数?

回答:聚类数目为2时,轮廓系数达到了0.42最高。当聚类数目为3时,轮廓系数达到了0.32.当聚类数目为6时,轮廓系数为0.31.

聚类可视化

一旦你选好了通过上面的评价函数得到的算法的最佳聚类数目,你就能够通过使用下面的代码块可视化来得到的结果。作为实验,你可以试着调整你的聚类算法的聚类的数量来看一下不同的可视化结果。但是你提供的最终的可视化图像必须和你选择的最优聚类数目一致。

In [57]:
# 从已有的实现中展示聚类的结果
vs.cluster_results(reduced_data, preds, centers, pca_samples)

练习: 数据恢复

上面的可视化图像中提供的每一个聚类都有一个中心点。这些中心(或者叫平均点)并不是数据中真实存在的点,但是是所有预测在这个簇中的数据点的平均。对于创建客户分类的问题,一个簇的中心对应于那个分类的平均用户。因为这个数据现在进行了降维并缩放到一定的范围,我们可以通过施加一个反向的转换恢复这个点所代表的用户的花费。

在下面的代码单元中,你将实现下列的功能:

  • 使用pca.inverse_transformcenters 反向转换,并将结果存储在log_centers中。
  • 使用np.log的反函数np.exp反向转换log_centers并将结果存储到true_centers中。
In [58]:
# TODO:反向转换中心点
log_centers = pca.inverse_transform(centers)

# TODO:对中心点做指数转换
true_centers = np.exp(log_centers)

# 显示真实的中心点
segments = ['Segment {}'.format(i) for i in range(0,len(centers))]
true_centers = pd.DataFrame(np.round(true_centers), columns = data.keys())
true_centers.index = segments
display(true_centers)
 FreshMilkGroceryFrozenDetergents_PaperDelicatessen
Segment 03552.07837.012219.0870.04696.0962.0
Segment 18953.02114.02765.02075.0353.0732.0

问题 8

考虑上面的代表性数据点在每一个产品类型的花费总数,你认为这些客户分类代表了哪类客户?为什么?需要参考在项目最开始得到的统计值来给出理由。

提示: 一个被分到'Cluster X'的客户最好被用 'Segment X'中的特征集来标识的企业类型表示。

回答:我认为0代表餐厅,1代表超市。因为第一个数据点的Grocery和Detergents——paper开支较高,高于总体中值;而第二个数据点的各项开支基本符合一个超市类零售商的消费特征。

问题 9

对于每一个样本点 问题 8 中的哪一个分类能够最好的表示它?你之前对样本的预测和现在的结果相符吗?

运行下面的代码单元以找到每一个样本点被预测到哪一个簇中去。

In [59]:
# 显示预测结果
for i, pred in enumerate(sample_preds):
    print "Sample point", i, "predicted to be in Cluster", pred
Sample point 0 predicted to be in Cluster 0
Sample point 1 predicted to be in Cluster 0
Sample point 2 predicted to be in Cluster 1

回答:cluster0,也就是餐厅。结果不太相符。因为之前的预测更为细致,目前的算法只输出2类。

结论

在最后一部分中,你要学习如何使用已经被分类的数据。首先,你要考虑不同组的客户客户分类,针对不同的派送策略受到的影响会有什么不同。其次,你要考虑到,每一个客户都被打上了标签(客户属于哪一个分类)可以给客户数据提供一个多一个特征。最后,你会把客户分类与一个数据中的隐藏变量做比较,看一下这个分类是否辨识了特定的关系。

问题 10

在对他们的服务或者是产品做细微的改变的时候,公司经常会使用A/B tests以确定这些改变会对客户产生积极作用还是消极作用。这个批发商希望考虑将他的派送服务从每周5天变为每周3天,但是他只会对他客户当中对此有积极反馈的客户采用。这个批发商应该如何利用客户分类来知道哪些客户对它的这个派送策略的改变有积极的反馈,如果有的话?你需要给出在这个情形下A/B 测试具体的实现方法,以及最终得出结论的依据是什么?
提示: 我们能假设这个改变对所有的客户影响都一致吗?我们怎样才能够确定它对于哪个类型的客户影响最大?

回答:对两组客户分别分为A1;A2,B1,B2组;对A1参考组,A2实验组组分别应用每周5天和每周3天的配送服务,分别记录采用不同配送服务时的周均销售额对比,如果采用A2实验组采用新配送服务的销售额较高,说明该类服务对该类客户组有积极作用,则对所在A1A2组采用新配送服务,否则采用原配送服务;同理可对B1,B2组采取同样方法。如果新配送服务对两组都有积极反馈,则看实验组和参考组的销售额上升程度,上升较多的那一组客户为影响最大的客户。

问题 11

通过聚类技术,我们能够将原有的没有标记的数据集中的附加结构分析出来。因为每一个客户都有一个最佳的划分(取决于你选择使用的聚类算法),我们可以把用户分类作为数据的一个工程特征。假设批发商最近迎来十位新顾客,并且他已经为每位顾客每个产品类别年度采购额进行了预估。进行了这些估算之后,批发商该如何运用它的预估和非监督学习的结果来对这十个新的客户进行更好的预测?

提示:在下面的代码单元中,我们提供了一个已经做好聚类的数据(聚类结果为数据中的cluster属性),我们将在这个数据集上做一个小实验。尝试运行下面的代码看看我们尝试预测‘Region’的时候,如果存在聚类特征'cluster'与不存在相比对最终的得分会有什么影响?这对你有什么启发?

In [60]:
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split

# 读取包含聚类结果的数据
cluster_data = pd.read_csv("cluster.csv")
y = cluster_data['Region']
X = cluster_data.drop(['Region'], axis = 1)

# 划分训练集测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=24)

clf = RandomForestClassifier(random_state=24)
clf.fit(X_train, y_train)
print "使用cluster特征的得分", clf.score(X_test, y_test)

# 移除cluster特征
X_train = X_train.copy()
X_train.drop(['cluster'], axis=1, inplace=True)
X_test = X_test.copy()
X_test.drop(['cluster'], axis=1, inplace=True)
clf.fit(X_train, y_train)
print "不使用cluster特征的得分", clf.score(X_test, y_test)
使用cluster特征的得分 0.666666666667
不使用cluster特征的得分 0.64367816092

回答:使用cluster特征得分更高;使用聚类分析得到的结果,加入原有的监督学习的输入特征中,给监督学习增加了特征信息,增加了特征维度,有利于结果精度提高。

可视化内在的分布

在这个项目的开始,我们讨论了从数据集中移除'Channel''Region'特征,这样在分析过程中我们就会着重分析用户产品类别。通过重新引入Channel这个特征到数据集中,并施加和原来数据集同样的PCA变换的时候我们将能够发现数据集产生一个有趣的结构。

运行下面的代码单元以查看哪一个数据点在降维的空间中被标记为'HoReCa' (旅馆/餐馆/咖啡厅)或者'Retail'。另外,你将发现样本点在图中被圈了出来,用以显示他们的标签。

In [61]:
# 根据‘Channel‘数据显示聚类的结果
vs.channel_results(reduced_data, outliers, pca_samples)

问题 12

你选择的聚类算法和聚类点的数目,与内在的旅馆/餐馆/咖啡店和零售商的分布相比,有足够好吗?根据这个分布有没有哪个簇能够刚好划分成'零售商'或者是'旅馆/饭店/咖啡馆'?你觉得这个分类和前面你对于用户分类的定义是一致的吗?

回答:基本一致,我认为没有哪个簇能够刚好划分这两类,因为彼此的间隔并不清晰。这个分类和前面的定义基本一致。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!