項目 3: 創建用戶分類

歡迎來到機器學習工程師納米學位的第三個項目!在這個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

你選擇的聚類算法和聚類點的數目,與內在的旅館/餐館/咖啡店和零售商的分佈相比,有足夠好嗎?根據這個分佈有沒有哪個簇能夠剛好劃分成'零售商'或者是'旅館/飯店/咖啡館'?你覺得這個分類和前面你對於用戶分類的定義是一致的嗎?

回答:基本一致,我認爲沒有哪個簇能夠剛好劃分這兩類,因爲彼此的間隔並不清晰。這個分類和前面的定義基本一致。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章