机器学习纳米学位
非监督学习
项目 3: 创建用户分类
欢迎来到机器学习工程师纳米学位的第三个项目!在这个notebook文件中,有些模板代码已经提供给你,但你还需要实现更多的功能来完成这个项目。除非有明确要求,你无须修改任何已给出的代码。以‘练习’开始的标题表示接下来的代码部分中有你必须要实现的功能。每一部分都会有详细的指导,需要实现的部分也会在注释中以‘TODO’标出。请仔细阅读所有的提示!
除了实现代码外,你还必须回答一些与项目和你的实现有关的问题。每一个需要你回答的问题都会以‘问题 X’为标题。请仔细阅读每个问题,并且在问题后的‘回答’文字框中写出完整的答案。我们将根据你对问题的回答和撰写代码所实现的功能来对你提交的项目进行评分。
提示:**Code 和 Markdown 区域可通过 **Shift + Enter 快捷键运行。此外,Markdown可以通过双击进入编辑模式。
开始
在这个项目中,你将分析一个数据集的内在结构,这个数据集包含很多客户真对不同类型产品的年度采购额(用金额表示)。这个项目的任务之一是如何最好地描述一个批发商不同种类顾客之间的差异。这样做将能够使得批发商能够更好的组织他们的物流服务以满足每个客户的需求。
这个项目的数据集能够在UCI机器学习信息库中找到.因为这个项目的目的,分析将不会包括’Channel’和’Region’这两个特征——重点集中在6个记录的客户购买的产品类别上。
运行下面的的代码单元以载入整个客户数据集和一些这个项目需要的Python库。如果你的数据集载入成功,你将看到后面输出数据集的大小。
# 检查你的Python版本
from sys import version_info
if version_info.major != 2 and version_info.minor != 7:
raise Exception('请使用Python 2.7来完成此项目')
# 引入这个项目需要的库
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)
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’。想一下这里每一个类型代表你会购买什么样的产品。
# 显示数据集的一个描述
display(data.describe())
Fresh | Milk | Grocery | Frozen | Detergents_Paper | Delicatessen | |
---|---|---|---|---|---|---|
count | 440.000000 | 440.000000 | 440.000000 | 440.000000 | 440.000000 | 440.000000 |
mean | 12000.297727 | 5796.265909 | 7951.277273 | 3071.931818 | 2881.493182 | 1524.870455 |
std | 12647.328865 | 7380.377175 | 9503.162829 | 4854.673333 | 4767.854448 | 2820.105937 |
min | 3.000000 | 55.000000 | 3.000000 | 25.000000 | 3.000000 | 3.000000 |
25% | 3127.750000 | 1533.000000 | 2153.000000 | 742.250000 | 256.750000 | 408.250000 |
50% | 8504.000000 | 3627.000000 | 4755.500000 | 1526.000000 | 816.500000 | 965.500000 |
75% | 16933.750000 | 7190.250000 | 10655.750000 | 3554.250000 | 3922.000000 | 1820.250000 |
max | 112151.000000 | 73498.000000 | 92780.000000 | 60869.000000 | 40827.000000 | 47943.000000 |
练习: 选择样本
为了对客户有一个更好的了解,并且了解代表他们的数据将会在这个分析过程中如何变换。最好是选择几个样本数据点,并且更为详细地分析它们。在下面的代码单元中,选择三个索引加入到索引列表indices
中,这三个索引代表你要追踪的客户。我们建议你不断尝试,直到找到三个明显不同的客户。
# TODO:从数据集中选择三个你希望抽样的数据点的索引
indices = [1,100,200]
# 为选择的样本建立一个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:
Fresh | Milk | Grocery | Frozen | Detergents_Paper | Delicatessen | |
---|---|---|---|---|---|---|
0 | 7057 | 9810 | 9568 | 1762 | 3293 | 1776 |
1 | 11594 | 7779 | 12144 | 3252 | 8035 | 3029 |
2 | 3067 | 13240 | 23127 | 3941 | 9959 | 731 |
问题 1
在你看来你选择的这三个样本点分别代表什么类型的企业(客户)?对每一个你选择的样本客户,通过它在每一种产品类型上的花费与数据集的统计描述进行比较,给出你做上述判断的理由。
提示: 企业的类型包括超市、咖啡馆、零售商以及其他。注意不要使用具体企业的名字,比如说在描述一个餐饮业客户时,你不能使用麦当劳。
回答:
- 0号样本代表零售商,Fresh在50%,Milk在75%,Grocery在75%,Frozen在50%,Detergents_Paper在75%,Delicatessen在75%,所有基本都在平均数左右,代表每样东西都有一些,但是都不算特别多,而且没有某列数据特别多,所以是零售商的可能性较大
- 1号样本代表超市,Fresh在75%以上,Milk在75%,Grocery在75%,Frozen在75%,Detergents_Paper在75%以上,Delicatessen在75%以上,大部分数据都超过平均值,没有哪样东西特别多,所以是一个大型超市的可能性比较大
- 2号样本代表咖啡馆,Milk、Grocery、Frozen和Detergents_Paper的量超过各自的均值很多,Fresh和Delicatessen的量明显低于各自的均值,所以可以看出来该样本不属于什么都在经营的零售商和超市,更有可能是使用牛奶等物品较多的咖啡馆
练习: 特征相关性
一个有趣的想法是,考虑这六个类别中的一个(或者多个)产品类别,是否对于理解客户的购买行为具有实际的相关性。也就是说,当用户购买了一定数量的某一类产品,我们是否能够确定他们必然会成比例地购买另一种类的产品。有一个简单的方法可以检测相关性:我们用移除了某一个特征之后的数据集来构建一个监督学习(回归)模型,然后用这个模型去预测那个被移除的特征,再对这个预测结果进行评分,看看预测结果如何。
在下面的代码单元中,你需要实现以下的功能:
- 使用
DataFrame.drop
函数移除数据集中你选择的不需要的特征,并将移除后的结果赋值给new_data
。 - 使用
sklearn.model_selection.train_test_split
将数据集分割成训练集和测试集。- 使用移除的特征作为你的目标标签。设置
test_size
为0.25
并设置一个random_state
。
- 使用移除的特征作为你的目标标签。设置
- 导入一个DecisionTreeRegressor(决策树回归器),设置一个
random_state
,然后用训练集训练它。 - 使用回归器的
score
函数输出模型在测试集上的预测得分。
# TODO:为DataFrame创建一个副本,用'drop'函数丢弃一个特征
selectFeature = 'Grocery'
new_data = data.drop([selectFeature],axis=1)
labels = data[selectFeature]
# TODO:使用给定的特征作为目标,将数据分割成训练集和测试集
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(new_data, labels, test_size=0.25, random_state=0)
# TODO:创建一个DecisionTreeRegressor(决策树回归器)并在训练集上训练它
from sklearn.tree import DecisionTreeRegressor
regressor = DecisionTreeRegressor(random_state=0)
regressor.fit(X_train,y_train)
y_pred = regressor.predict(X_test)
# TODO:输出在测试集上的预测得分
from sklearn.metrics import r2_score
score = r2_score(y_test,y_pred)
print score
0.602801978878
问题 2
你尝试预测哪一个特征?预测的得分是多少?这个特征对于区分用户的消费习惯来说必要吗?
提示: 决定系数(coefficient of determination), R^2
,结果在0到1之间,1表示完美拟合,一个负的R^2
表示模型不能够拟合数据。
回答:
- Grocery
- 0.602801978878
- 得分较高,说明这个特征对于区分用户的消费习惯来说必要性较低,因为它可以通过其他数据拟合出来的可能性较高
可视化特征分布
为了能够对这个数据集有一个更好的理解,我们可以对数据集中的每一个产品特征构建一个散布矩阵(scatter matrix)。如果你发现你在上面尝试预测的特征对于区分一个特定的用户来说是必须的,那么这个特征和其它的特征可能不会在下面的散射矩阵中显示任何关系。相反的,如果你认为这个特征对于识别一个特定的客户是没有作用的,那么通过散布矩阵可以看出在这个数据特征和其它特征中有关联性。运行下面的代码以创建一个散布矩阵。
# 对于数据中的每一对特征构造一个散布矩阵
pd.plotting.scatter_matrix(data, alpha = 0.3, figsize = (14,8), diagonal = 'kde');
![这里写图片描述](https://img-blog.csdn.net/20171214175301180?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZ3JhcGU4NzU0OTk3NjU=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
from scipy.stats import normaltest
head = ['Fresh','Milk','Grocery','Frozen','Detergents_Paper','Delicatessen']
for item in head:
print item
print normaltest(data[item])
print normaltest(np.log(data[item] + 1))
Fresh
NormaltestResult(statistic=274.34162662040899, pvalue=2.6759173855882678e-60)
NormaltestResult(statistic=148.84539396194026, pvalue=4.7712631818181207e-33)
Milk
NormaltestResult(statistic=410.65364260210919, pvalue=6.7250345862368488e-90)
NormaltestResult(statistic=4.5934474079370444, pvalue=0.10058786005776135)
Grocery
NormaltestResult(statistic=374.02330569249045, pvalue=6.0516125835768024e-82)
NormaltestResult(statistic=66.149385698876415, pvalue=4.3235790604778842e-15)
Frozen
NormaltestResult(statistic=546.90399640685735, pvalue=1.7430350282461951e-119)
NormaltestResult(statistic=10.196318717413183, pvalue=0.0061079788231069403)
Detergents_Paper
NormaltestResult(statistic=371.81993263618244, pvalue=1.8210735948002004e-81)
NormaltestResult(statistic=6.1496525917521669, pvalue=0.046197652407495196)
Delicatessen
NormaltestResult(statistic=793.29833041667666, pvalue=5.4633157170835384e-173)
NormaltestResult(statistic=94.42429506607354, pvalue=3.1334649963545683e-21)
问题 3
这里是否存在一些特征他们彼此之间存在一定程度相关性?如果有请列出。这个结果是验证了还是否认了你尝试预测的那个特征的相关性?这些特征的数据是怎么分布的?
提示: 这些数据是正态分布(normally distributed)的吗?大多数的数据点分布在哪?
回答:
- 存在特征彼此之间有相关性
- 比如:grocery和Detergents_paper、grocery和Milk
- 验证了我尝试预测的特征相关性
- 不是正态分布,大多数的数据点分布在数值较小的地方
数据预处理
在这个部分,你将通过在数据上做一个合适的缩放,并检测异常点(你可以选择性移除)将数据预处理成一个更好的代表客户的形式。预处理数据是保证你在分析中能够得到显著且有意义的结果的重要环节。
练习: 特征缩放
如果数据不是正态分布的,尤其是数据的平均数和中位数相差很大的时候(表示数据非常歪斜)。这时候通常用一个非线性的缩放是很合适的,(英文原文) — 尤其是对于金融数据。一种实现这个缩放的方法是使用Box-Cox 变换,这个方法能够计算出能够最佳减小数据倾斜的指数变换方法。一个比较简单的并且在大多数情况下都适用的方法是使用自然对数。
在下面的代码单元中,你将需要实现以下功能:
- 使用
np.log
函数在数据data
上做一个对数缩放,然后将它的副本(不改变原始data的值)赋值给log_data
。 - 使用
np.log
函数在样本数据samples
上做一个对数缩放,然后将它的副本赋值给log_samples
。
# TODO:使用自然对数缩放数据
log_data = np.log(data + 1)
# TODO:使用自然对数缩放样本数据
log_samples = np.log(samples + 1)
# 为每一对新产生的特征制作一个散射矩阵
pd.plotting.scatter_matrix(log_data, alpha = 0.3, figsize = (14,8), diagonal = 'kde');
![这里写图片描述](https://img-blog.csdn.net/20171214174323002?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZ3JhcGU4NzU0OTk3NjU=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
观察
在使用了一个自然对数的缩放之后,数据的各个特征会显得更加的正态分布。对于任意的你以前发现有相关关系的特征对,观察他们的相关关系是否还是存在的(并且尝试观察,他们的相关关系相比原来是变强了还是变弱了)。
运行下面的代码以观察样本数据在进行了自然对数转换之后如何改变了。
# 展示经过对数变换后的样本数据
display(log_samples)
Fresh | Milk | Grocery | Frozen | Detergents_Paper | Delicatessen | |
---|---|---|---|---|---|---|
0 | 8.861917 | 9.191259 | 9.166284 | 7.474772 | 8.099858 | 7.482682 |
1 | 9.358329 | 8.959312 | 9.404673 | 8.087333 | 8.991687 | 8.016318 |
2 | 8.028781 | 9.491073 | 10.048799 | 8.279443 | 9.206332 | 6.595781 |
练习: 异常值检测
对于任何的分析,在数据预处理的过程中检测数据中的异常值都是非常重要的一步。异常值的出现会使得把这些值考虑进去后结果出现倾斜。这里有很多关于怎样定义什么是数据集中的异常值的经验法则。这里我们将使用Tukey的定义异常值的方法:一个异常阶(outlier step)被定义成1.5倍的四分位距(interquartile range,IQR)。一个数据点如果某个特征包含在该特征的IQR之外的特征,那么该数据点被认定为异常点。
在下面的代码单元中,你需要完成下面的功能:
- 将指定特征的25th分位点的值分配给
Q1
。使用np.percentile
来完成这个功能。 - 将指定特征的75th分位点的值分配给
Q3
。同样的,使用np.percentile
来完成这个功能。 - 将指定特征的异常阶的计算结果赋值给
step
. - 选择性地通过将索引添加到
outliers
列表中,以移除异常值。
注意: 如果你选择移除异常值,请保证你选择的样本点不在这些移除的点当中!
一旦你完成了这些功能,数据集将存储在good_data
中。
outliers = []
# 对于每一个特征,找到值异常高或者是异常低的数据点
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 = (Q3 - Q1) * 1.5
# 显示异常点
print "Data points considered outliers for the feature '{}':".format(feature)
outdatas = log_data[~((log_data[feature] >= Q1 - step) & (log_data[feature] <= Q3 + step))]
outliers.extend(outdatas.index.tolist())
display(log_data[~((log_data[feature] >= Q1 - step) & (log_data[feature] <= Q3 + step))])
# 可选:选择你希望移除的数据点的索引
# outliers = []
outliers = [val for val in list(set(outliers)) if outliers.count(val)>=2]
print outliers
# 如果选择了的话,移除异常点
good_data = log_data.drop(log_data.index[outliers]).reset_index(drop = True)
Data points considered outliers for the feature ‘Fresh’:
Fresh | Milk | Grocery | Frozen | Detergents_Paper | Delicatessen | |
---|---|---|---|---|---|---|
65 | 4.454347 | 9.950371 | 10.732672 | 3.610918 | 10.095429 | 7.261225 |
66 | 2.302585 | 7.336286 | 8.911665 | 5.170484 | 8.151622 | 3.332205 |
81 | 5.393628 | 9.163354 | 9.575261 | 5.648974 | 8.964312 | 5.056246 |
95 | 1.386294 | 7.979681 | 8.740817 | 6.089045 | 5.411646 | 6.565265 |
96 | 3.178054 | 7.869784 | 9.001962 | 4.983607 | 8.262301 | 5.384495 |
128 | 4.948760 | 9.087947 | 8.249052 | 4.962845 | 6.968850 | 1.386294 |
171 | 5.303305 | 10.160569 | 9.894295 | 6.480045 | 9.079548 | 8.740497 |
193 | 5.198497 | 8.156510 | 9.918031 | 6.866933 | 8.633909 | 6.502790 |
218 | 2.944439 | 8.923325 | 9.629445 | 7.159292 | 8.475954 | 8.759826 |
304 | 5.087596 | 8.917445 | 10.117550 | 6.426488 | 9.374498 | 7.787797 |
305 | 5.497168 | 9.468079 | 9.088512 | 6.684612 | 8.271293 | 5.356586 |
338 | 1.386294 | 5.811141 | 8.856803 | 9.655154 | 2.772589 | 6.311735 |
353 | 4.770685 | 8.742734 | 9.961945 | 5.433722 | 9.069122 | 7.013915 |
355 | 5.252273 | 6.590301 | 7.607381 | 5.505332 | 5.220356 | 4.852030 |
357 | 3.637586 | 7.151485 | 10.011130 | 4.927254 | 8.817001 | 4.709530 |
412 | 4.584967 | 8.190354 | 9.425532 | 4.595120 | 7.996654 | 4.143135 |
Data points considered outliers for the feature 'Milk':
Fresh | Milk | Grocery | Frozen | Detergents_Paper | Delicatessen | |
---|---|---|---|---|---|---|
86 | 10.040027 | 11.205027 | 10.377078 | 6.895683 | 9.907031 | 6.806829 |
98 | 6.222576 | 4.727388 | 6.658011 | 6.797940 | 4.043051 | 4.890349 |
154 | 6.434547 | 4.025352 | 4.927254 | 4.330733 | 2.079442 | 2.197225 |
356 | 10.029547 | 4.905275 | 5.389072 | 8.057694 | 2.302585 | 6.308098 |
Data points considered outliers for the feature 'Grocery':
Fresh | Milk | Grocery | Frozen | Detergents_Paper | Delicatessen | |
---|---|---|---|---|---|---|
75 | 9.923241 | 7.037028 | 1.386294 | 8.391176 | 1.386294 | 6.883463 |
154 | 6.434547 | 4.025352 | 4.927254 | 4.330733 | 2.079442 | 2.197225 |
Data points considered outliers for the feature 'Frozen':
Fresh | Milk | Grocery | Frozen | Detergents_Paper | Delicatessen | |
---|---|---|---|---|---|---|
38 | 8.432071 | 9.663325 | 9.723763 | 3.526361 | 8.847504 | 6.073045 |
57 | 8.597482 | 9.203718 | 9.257987 | 3.663562 | 8.932345 | 7.156956 |
65 | 4.454347 | 9.950371 | 10.732672 | 3.610918 | 10.095429 | 7.261225 |
145 | 10.000614 | 9.034200 | 10.457171 | 3.761200 | 9.440817 | 8.396381 |
175 | 7.759614 | 8.967759 | 9.382191 | 3.970292 | 8.342125 | 7.437206 |
264 | 6.979145 | 9.177817 | 9.645105 | 4.127134 | 8.696343 | 7.143618 |
325 | 10.395681 | 9.728241 | 9.519808 | 11.016496 | 7.149132 | 8.632306 |
420 | 8.402231 | 8.569216 | 9.490091 | 3.258097 | 8.827468 | 7.239933 |
429 | 9.060447 | 7.467942 | 8.183397 | 3.871201 | 4.442651 | 7.824846 |
439 | 7.933080 | 7.437795 | 7.828436 | 4.189655 | 6.169611 | 3.970292 |
Data points considered outliers for the feature 'Detergents_Paper':
Fresh | Milk | Grocery | Frozen | Detergents_Paper | Delicatessen | |
---|---|---|---|---|---|---|
75 | 9.923241 | 7.037028 | 1.386294 | 8.391176 | 1.386294 | 6.883463 |
161 | 9.428270 | 6.293419 | 5.648974 | 6.996681 | 1.386294 | 7.711549 |
Data points considered outliers for the feature 'Delicatessen':
Fresh | Milk | Grocery | Frozen | Detergents_Paper | Delicatessen | |
---|---|---|---|---|---|---|
66 | 2.302585 | 7.336286 | 8.911665 | 5.170484 | 8.151622 | 3.332205 |
109 | 7.249215 | 9.724959 | 10.274603 | 6.513230 | 6.729824 | 1.386294 |
128 | 4.948760 | 9.087947 | 8.249052 | 4.962845 | 6.968850 | 1.386294 |
137 | 8.035279 | 8.997271 | 9.021961 | 6.495266 | 6.582025 | 3.610918 |
142 | 10.519673 | 8.875287 | 9.018453 | 8.005033 | 3.044522 | 1.386294 |
154 | 6.434547 | 4.025352 | 4.927254 | 4.330733 | 2.079442 | 2.197225 |
183 | 10.514557 | 10.690831 | 9.912001 | 10.506026 | 5.480639 | 10.777789 |
184 | 5.793014 | 6.823286 | 8.457655 | 4.317488 | 5.814131 | 2.484907 |
187 | 7.799343 | 8.987572 | 9.192176 | 8.743532 | 8.149024 | 1.386294 |
203 | 6.369901 | 6.530878 | 7.703910 | 6.152733 | 6.861711 | 2.944439 |
233 | 6.872128 | 8.514189 | 8.106816 | 6.843750 | 6.016157 | 2.079442 |
285 | 10.602989 | 6.463029 | 8.188967 | 6.949856 | 6.079933 | 2.944439 |
289 | 10.663990 | 5.659482 | 6.156979 | 7.236339 | 3.496508 | 3.135494 |
343 | 7.432484 | 8.848653 | 10.177970 | 7.284135 | 9.646658 | 3.637586 |
[128, 154, 65, 66, 75]
### 问题 4
*请列出所有在多于一个特征下被看作是异常的数据点。这些点应该被从数据集中移除吗?为什么?把你认为需要移除的数据点全部加入到到`outliers`变量中。*
**回答:**
- 65、66、128、75、154这几个数据多于一个特征下被看作了异常点,应该被移除
- 因为异常点的存在会导致训练的误差增大,所以需要去除
- 我将上面五个点移除了
## 特征转换
在这个部分中你将使用主成分分析(PCA)来分析批发商客户数据的内在结构。由于使用PCA在一个数据集上会计算出最大化方差的维度,我们将找出哪一个特征组合能够最好的描绘客户。
### 练习: 主成分分析(PCA)
既然数据被缩放到一个更加正态分布的范围中并且我们也移除了需要移除的异常点,我们现在就能够在`good_data`上使用PCA算法以发现数据的哪一个维度能够最大化特征的方差。除了找到这些维度,PCA也将报告每一个维度的*解释方差比(explained variance ratio)*–这个数据有多少方差能够用这个单独的维度来解释。注意PCA的一个组成部分(维度)能够被看做这个空间中的一个新的“特征”,但是它是原来数据中的特征构成的。
在下面的代码单元中,你将要实现下面的功能:
- 导入`sklearn.decomposition.PCA`并且将`good_data`用PCA并且使用6个维度进行拟合后的结果保存到`pca`中。
- 使用`pca.transform`将`log_samples`进行转换,并将结果存储到`pca_samples`中。
# 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)
![这里写图片描述](https://img-blog.csdn.net/20171214174620046?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZ3JhcGU4NzU0OTk3NjU=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
### 问题 5
*数据的第一个和第二个主成分* **总共** *表示了多少的方差?* 前四个主成分呢?使用上面提供的可视化图像,讨论从用户花费的角度来看前四个**主要成分**的消费行为最能代表哪种类型的客户并给出你做出判断的理由。
**提示:** 某一特定维度上的正向增长对应**正权**特征的**增长**和**负权**特征的**减少**。增长和减少的速率和每个特征的权重相关。[参考资料(英文)](https://onlinecourses.science.psu.edu/stat505/node/54)。
**回答:**
- 总共表示了0.7085的方差
- 前四个表示了0.9311的方差
- 表示零售商和咖啡店,从第一个维度来看,可以分成两部分,第一部分Milk、Grocery和DP他们是负权特征,彼此之间是相关性最强,第二个维度上,Fresh、Frozen和Delicatessen彼此之间相关性最强,第三个维度体现了Frozen和Delicatessen的相关性,第四个维度体现了Fresh和Delicatessen的相关性,由此可见6个特征被主要分成了2组,其中Milk、Grocery、DP代表的是咖啡店,Fresh、Frozen、Delicatessen代表的是零售商
### 观察
运行下面的代码,查看经过对数转换的样本数据在进行一个6个维度的主成分分析(PCA)之后会如何改变。观察样本数据的前四个维度的数值。考虑这和你初始对样本点的解释是否一致。
# 展示经过PCA转换的sample log-data
display(pd.DataFrame(np.round(pca_samples, 4), columns = pca_results.index.values))
Dimension 1 | Dimension 2 | Dimension 3 | Dimension 4 | Dimension 5 | Dimension 6 | |
---|---|---|---|---|---|---|
0 | -1.7837 | -0.8112 | 0.2298 | -0.0154 | 0.1148 | -0.2111 |
1 | -2.3494 | -1.7385 | 0.2247 | 0.2711 | -0.6018 | -0.0192 |
2 | -2.9861 | -0.3758 | 0.3013 | 1.5540 | 0.1934 | 0.1232 |
练习:降维
当使用主成分分析的时候,一个主要的目的是减少数据的维度,这实际上降低了问题的复杂度。当然降维也是需要一定代价的:更少的维度能够表示的数据中的总方差更少。因为这个,累计解释方差比(cumulative explained variance ratio)对于我们确定这个问题需要多少维度非常重要。另外,如果大部分的方差都能够通过两个或者是三个维度进行表示的话,降维之后的数据能够被可视化。
在下面的代码单元中,你将实现下面的功能:
- 将
good_data
用两个维度的PCA进行拟合,并将结果存储到pca
中去。 - 使用
pca.transform
将good_data
进行转换,并将结果存储在reduced_data
中。 - 使用
pca.transform
将log_samples
进行转换,并将结果存储在pca_samples
中。
# TODO:通过在good data上进行PCA,将其转换成两个维度
from sklearn.decomposition import 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转换相比较时,前两维的数值是保持不变的。
# 展示经过两个维度的PCA转换之后的样本log-data
display(pd.DataFrame(np.round(pca_samples, 4), columns = ['Dimension 1', 'Dimension 2']))
Dimension 1 | Dimension 2 | |
---|---|---|
0 | -1.7837 | -0.8112 |
1 | -2.3494 | -1.7385 |
2 | -2.9861 | -0.3758 |
# Create a biplot
vs.biplot(good_data, reduced_data, pca)
### 观察
一旦我们有了原始特征的投影(红色箭头),就能更加容易的理解散点图每个数据点的相对位置。
在这个双标图中,哪些初始特征与第一个主成分有强关联?哪些初始特征与第二个主成分相关联?你观察到的是否与之前得到的 pca_results 图相符?
## 聚类
在这个部分,你讲选择使用K-Means聚类算法或者是高斯混合模型聚类算法以发现数据中隐藏的客户分类。然后,你将从簇中恢复一些特定的关键数据点,通过将它们转换回原始的维度和规模,从而理解他们的含义。
### 问题 6
*使用K-Means聚类算法的优点是什么?使用高斯混合模型聚类算法的优点是什么?基于你现在对客户数据的观察结果,你选用了这两个算法中的哪一个,为什么?*
**回答:**
- 使用K-Means,算法简单,容易理解。计算量不大,收敛快。可以很方便的进行分布式计算。
- 使用高斯混合模型聚类,优点是投影后样本点不是得到一个确定的分类标记,而是得到每个类的概率。针对服从正态分布、偏态分布的数据有比较好的效果。
- 选择了高斯混合模型聚类,因为处理过的数据基本服从正态分布,所以可能会有较好的效果
### 练习: 创建聚类
针对不同情况,有些问题你需要的聚类数目可能是已知的。但是在聚类数目不作为一个**先验**知道的情况下,我们并不能够保证某个聚类的数目对这个数据是最优的,因为我们对于数据的结构(如果存在的话)是不清楚的。但是,我们可以通过计算每一个簇中点的**轮廓系数**来衡量聚类的质量。数据点的[轮廓系数](http://scikit-learn.org/stable/modules/generated/sklearn.metrics.silhouette_score.html)衡量了它与分配给他的簇的相似度,这个值范围在-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`并输出结果。
# TODO:在降维后的数据上使用你选择的聚类算法
from sklearn.mixture import GaussianMixture
from sklearn.metrics import silhouette_score
for i in xrange(2,8):
clusterer = GaussianMixture(n_components=i,random_state=0)
clusterer.fit(reduced_data)
# TODO:预测每一个点的簇
preds = clusterer.predict(reduced_data)
# TODO:找到聚类中心
centers = clusterer.means_
# TODO:预测在每一个转换后的样本点的类
sample_preds = clusterer.predict(pca_samples)
# TODO:计算选择的类别的平均轮廓系数(mean silhouette coefficient)
score = silhouette_score(reduced_data,preds)
print score
clusterer = GaussianMixture(n_components=2,random_state=0)
clusterer.fit(reduced_data)
# TODO:预测每一个点的簇
preds = clusterer.predict(reduced_data)
# TODO:找到聚类中心
centers = clusterer.means_
# TODO:预测在每一个转换后的样本点的类
sample_preds = clusterer.predict(pca_samples)
# TODO:计算选择的类别的平均轮廓系数(mean silhouette coefficient)
score = silhouette_score(reduced_data,preds)
0.422922850609
0.370495744444
0.330340504197
0.309626504416
0.330147227158
0.283632396906
### 问题 7
*汇报你尝试的不同的聚类数对应的轮廓系数。在这些当中哪一个聚类的数目能够得到最佳的轮廓系数?*
**回答:**
- 尝试了2-7的聚类数,结果分别是
- 0.422922850609
- 0.370495744444
- 0.330340504197
- 0.309626504416
- 0.330147227158
- 0.283632396906
- 2聚类得到了最佳的轮廓系数
聚类可视化
一旦你选好了通过上面的评价函数得到的算法的最佳聚类数目,你就能够通过使用下面的代码块可视化来得到的结果。作为实验,你可以试着调整你的聚类算法的聚类的数量来看一下不同的可视化结果。但是你提供的最终的可视化图像必须和你选择的最优聚类数目一致。
# 从已有的实现中展示聚类的结果
vs.cluster_results(reduced_data, preds, centers, pca_samples)
![这里写图片描述](https://img-blog.csdn.net/20171214174934362?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZ3JhcGU4NzU0OTk3NjU=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
练习: 数据恢复
上面的可视化图像中提供的每一个聚类都有一个中心点。这些中心(或者叫平均点)并不是数据中真实存在的点,但是是所有预测在这个簇中的数据点的平均。对于创建客户分类的问题,一个簇的中心对应于那个分类的平均用户。因为这个数据现在进行了降维并缩放到一定的范围,我们可以通过施加一个反向的转换恢复这个点所代表的用户的花费。
在下面的代码单元中,你将实现下列的功能:
- 使用
pca.inverse_transform
将centers
反向转换,并将结果存储在log_centers
中。 - 使用
np.log
的反函数np.exp
反向转换log_centers
并将结果存储到true_centers
中。
# 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)
import seaborn as sns
import matplotlib.pyplot as plt
# add the true centers as rows to our original data
newdata = data.append(true_centers)
# show the percentiles of the centers
ctr_pcts = 100. * newdata.rank(axis=0, pct=True).loc[['Segment 0', 'Segment 1']].round(decimals=3)
print ctr_pcts
# visualize percentiles with heatmap
sns.heatmap(ctr_pcts, annot=True, cmap='Greens', fmt='.1f', linewidth=.1, square=True, cbar=False)
plt.xticks(rotation=45, ha='center')
plt.yticks(rotation=0)
plt.title('Percentile ranks of\nsegment centers');
Fresh | Milk | Grocery | Frozen | Detergents_Paper | Delicatessen | |
---|---|---|---|---|---|---|
Segment 0 | 8937.0 | 2100.0 | 2747.0 | 2076.0 | 353.0 | 731.0 |
Segment 1 | 3604.0 | 7878.0 | 12261.0 | 878.0 | 4695.0 | 976.0 |
Fresh Milk Grocery Frozen Detergents_Paper Delicatessen
Segment 0 52.5 34.0 34.4 58.4 32.2 41.3
Segment 1 28.1 79.0 80.5 31.6 80.3 50.7
问题 8
考虑上面的代表性数据点在每一个产品类型的花费总数,你认为这些客户分类代表了哪类客户?为什么?需要参考在项目最开始得到的统计值来给出理由。
提示: 一个被分到'Cluster X'
的客户最好被用 'Segment X'
中的特征集来标识的企业类型表示。
回答:
- Cluster 0更像是零售商,因为他们的中心点Segment 0的各项特征值都是50%左右,低于平均值,不存在某个特征特别高,而量又没有特别大,符合零售商什么都卖,但是入货不算多的特点
- Cluster 1更像是咖啡店,并且他们的中心点Segment 1的值牛奶和、杂物和清洁纸的量超过了75%而且超过了平均数,而其他的数量较少,咖啡店牛奶和纸巾的消耗必然很多,所以更有可能是咖啡店
问题 9
对于每一个样本点 问题 8 中的哪一个分类能够最好的表示它?你之前对样本的预测和现在的结果相符吗?
运行下面的代码单元以找到每一个样本点被预测到哪一个簇中去。
# 显示预测结果
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 1
Sample point 1 predicted to be in Cluster 1
Sample point 2 predicted to be in Cluster 1
回答:
- 主要是咖啡店,因为他们都属于Cluster 1,并且他们的中心点Segment 1的值牛奶和、杂物和清洁纸的量超过了75%而且超过了平均数,而其他的数量较少,咖啡店牛奶和纸巾的消耗必然很多,所以更有可能是咖啡店
- 不相符,,,年轻不懂事
结论
在最后一部分中,你要学习如何使用已经被分类的数据。首先,你要考虑不同组的客户客户分类,针对不同的派送策略受到的影响会有什么不同。其次,你要考虑到,每一个客户都被打上了标签(客户属于哪一个分类)可以给客户数据提供一个多一个特征。最后,你会把客户分类与一个数据中的隐藏变量做比较,看一下这个分类是否辨识了特定的关系。
问题 10
在对他们的服务或者是产品做细微的改变的时候,公司经常会使用A/B tests以确定这些改变会对客户产生积极作用还是消极作用。这个批发商希望考虑将他的派送服务从每周5天变为每周3天,但是他只会对他客户当中对此有积极反馈的客户采用。这个批发商应该如何利用客户分类来知道哪些客户对它的这个派送策略的改变有积极的反馈,如果有的话?你需要给出在这个情形下A/B 测试具体的实现方法,以及最终得出结论的依据是什么?
提示: 我们能假设这个改变对所有的客户影响都一致吗?我们怎样才能够确定它对于哪个类型的客户影响最大?
回答:
- 从每个分类中各找出相等的一小部分客户,然后给他们派送的服务从每周5天变成3天,经过两周的实验,观察哪个分类的用户反馈更积极
- 如果其中某些分类的客户基本没有退订服务,那么说明这些客户改成3天没有问题
- 如果某些分类的客户退订服务很多,那么说明这些客户还应该每周派送5天
- 可以先假设都一致,根据退订的比例来确定对那个类型的客户影响最大
问题 11
通过聚类技术,我们能够将原有的没有标记的数据集中的附加结构分析出来。因为每一个客户都有一个最佳的划分(取决于你选择使用的聚类算法),我们可以把用户分类作为数据的一个工程特征。假设批发商最近迎来十位新顾客,并且他已经为每位顾客每个产品类别年度采购额进行了预估。进行了这些估算之后,批发商该如何运用它的预估和非监督学习的结果来对这十个新的客户进行更好的预测?
提示:在下面的代码单元中,我们提供了一个已经做好聚类的数据(聚类结果为数据中的cluster属性),我们将在这个数据集上做一个小实验。尝试运行下面的代码看看我们尝试预测‘Region’的时候,如果存在聚类特征’cluster’与不存在相比对最终的得分会有什么影响?这对你有什么启发?
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'
。另外,你将发现样本点在图中被圈了出来,用以显示他们的标签。
# 根据‘Channel‘数据显示聚类的结果
vs.channel_results(reduced_data, outliers, pca_samples)
问题 12
你选择的聚类算法和聚类点的数目,与内在的旅馆/餐馆/咖啡店和零售商的分布相比,有足够好吗?根据这个分布有没有哪个簇能够刚好划分成’零售商’或者是’旅馆/饭店/咖啡馆’?你觉得这个分类和前面你对于用户分类的定义是一致的吗?
回答:
- 聚类点的数目是两个,效果差不多
- 有的,就是在Dimension 1的值为-0.4左右的纵轴基本可以将二者分开
- 不一致,,,不知道为什么左侧的会是零售商,零售商会买很多牛奶、杂货和餐巾纸么?
注意: 当你写完了所有的代码,并且回答了所有的问题。你就可以把你的 iPython Notebook 导出成 HTML 文件。你可以在菜单栏,这样导出File -> Download as -> HTML (.html)把这个 HTML 和这个 iPython notebook 一起做为你的作业提交。
来源:CSDN
作者:牧码人小鹏
链接:https://blog.csdn.net/grape875499765/article/details/78805483