數據挖掘習題彙總:線性迴歸、KNN、K-means、決策樹、關聯規則

作業清單(4/20)

【1】 安裝Python 3.X 和 Orange3 軟件,是否完成?

已完成

【2】 完成課堂實驗(給定教師數據,判斷身份的實驗),是否完成?

已完成

data =[ {'NAME':'Mike','RANK':'Assistant Prof', 'YEARS':3, 'TENURED':None},
        {'NAME':'Mary','RANK':'Assistant Prof', 'YEARS':7, 'TENURED':None},
        {'NAME':'Bill','RANK':'Professor', 'YEARS':2, 'TENURED':None},
        {'NAME':'Jim','RANK':'Assistant Prof', 'YEARS':7, 'TENURED':None},
        {'NAME':'Dave','RANK':'Assistant Prof', 'YEARS':6, 'TENURED':None},
        {'NAME':'Anne','RANK':'Assistant Prof', 'YEARS':3, 'TENURED':None}]
for dict in data:
    if dict['RANK']=='Professor' or dict['YEARS']>6:
        dict['TENURED']='yes'
    else:
        dict['TENURED']='no'
    print(dict['NAME'],dict['TENURED'])

【3】 複習Numpy的主要功能(按照課堂PPT完成相關實驗)

  • 求平均數、中位數、衆數,對數組進行排序

    >>> from numpy import mean,median,sort
    >>> from scipy.stats import mode
    >>> a = [1,2,3,4,5,5,6]
    >>> print(mean(a))
    3.7142857142857144
    >>> print(median(a))
    4.0
    >>> print(mode(a)[0])
    [5]
    >>> print(sort(a))
    [1 2 3 4 5 5 6]
    
  • 創建矩陣

>>> from numpy import mean,median,sort
>>> b= np.arange(3,15)
>>> b
array([ 3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14])
>>> b.reshape(3,4)
array([[ 3,  4,  5,  6],
       [ 7,  8,  9, 10],
       [11, 12, 13, 14]])
  • 矩陣排序

  • 矩陣相乘

>>> import numpy as np
>>> b = np.arange(12,24).reshape([4,3])
>>> a = np.arange(0,12).reshape([3,4])
>>> p = np.dot(a,b)
>>> p
array([[114, 120, 126],
       [378, 400, 422],
       [642, 680, 718]])
>>>                           

【4】 完成參考教材第1章 1.9的第1題和第4題(抄題)

Problem:

1.1 What is data mining? In your answer, address the following:

(a) Is it another hype?

(b) Is it a simple transformation or application of technology developed from databases,statistics, machine learning, and pattern recognition?

© We have presented a view that data mining is the result of the evolution of database technology. Do you think that data mining is also the result of the evolution of machine learning research? Can you present such views based on the historical progress of this discipline? Address the same for the fields of statistics and pattern recognition.

(d) Describe the steps involved in data mining when viewed as a process of knowledge discovery.

Answer:

(a) No. 數據挖掘就是從大量的、不完全的、有噪聲的、模糊的、隨機的實際應用數據中,提取隱含在其中的、人們事先不知道的、但又是潛在有用的信息和知識的過程

(b) No

© 數據挖掘起始於20世紀下半葉,是在當時多個學科發展的基礎上發展起來的。隨着數據庫技術的發展應用,數據的積累不斷膨脹,導致簡單的查詢和統計已經無法滿足企業的商業需求,亟需一些革命性的技術去挖掘數據背後的信息。同時,這期間計算機領域的人工智能也取得了巨大進展,進入了機器學習的階段。因此,人們將兩者結合起來,用數據庫管理系統存儲數據,用計算機分析數據,並且嘗試挖掘數據背後的信息。

模式識別方法也越來越多被應用於數據挖掘。

後來,人們逐漸開始發現數據挖掘中有許多工作可以由統計方法來完成,並認爲最好的策略是將統計方法與數據挖掘有機的結合起來.

數據挖掘受到很多學科領域的影響,其中數據庫、機器學習、統計學無疑影響最大。簡言之,對數據挖掘而言,數據庫提供數據管理技術,機器學習和統計學提供數據分析技術。

由於統計學往往醉心於理論的優美而忽視實際的效用,因此,統計學界提供的很多技術通常都要在機器學習界進一步研究,變成有效的機器學習算法之後才能再進入數據挖掘領域。從這個意義上說,統計學主要是通過機器學習來對數據挖掘發揮影響,而機器學習和數據庫則是數據挖掘的兩大支撐技術。*從數據分析的角度來看,絕大多數數據挖掘技術都來自機器學習領域,但機器學習研究往往並不把海量數據作爲處理對象,因此,數據挖掘要對算法進行改造,使得算法性能和空間佔用達到實用的地步。*同時,數據挖掘還有自身獨特的內容,即關聯分析。

Problem:

Present an example where data mining is crucial to the success of a business. What data mining functionalities does this business need (e.g., think of the kinds of patterns that could be mined)? Can such patterns be generated alternatively by data query processing or simple statistical analysis?

Answer:

中國石油天然氣集團有限公司利用“魔鏡”進行數據挖掘。

首先,魔鏡對中石油的供應鏈業務數據進行了分析,可以使其更合理的監控供應時間、庫存、成本、物流、訂單所處環節以及所涉及的金額是多少等等。

其次,魔鏡還對銷售情況進行了分析,方便其瞭解訂單、成交額、成交地區、銷售產品種類、銷售預測及達標率、成本等情況。

再者,魔鏡對中石油的後勤進行了分析,包括交貨數、交貨天數、訂單成交時間、未交訂單等,可以通曉不同地區各時間段的購買情況。

第四,魔鏡對中石油的客戶也進行了分析。魔鏡用雷達覆蓋的面積從購買週期,平均購買量,忠誠度,平均購買銷售額,平均購買創造利潤等多個維度來衡量用戶整體質量,來看客戶的最長、最短和平均購買週期,是穩定的客戶還是波動大的代理商或個人暫時出現的週期等,從而分析出哪個區域的客戶購買力最穩定,對不同用戶進行同時對比,同時展現,綜合的分析客戶的價值,直接找到短板。最後再對這些用戶下一次購買的時間進行預計,報警並及時作出決策。

第五,魔鏡從生產線、產品種類、產品狀態、庫存量、產品質量數量等生產、倉儲情況進行了分析。根據魔鏡可視化圖表,一目就可以瞭解到每種產品在庫存整體佔用情況,每筆單子佔用多少,其中每種產品又有多少;各個過程有多少損耗;還有各產品在各個區域購買的情況等,最終把這些情況轉化成果,知道了哪些品類的產品好賣,又有哪些是滯銷產品。

第六,魔鏡使中石油的銷售額、成本、預算、利潤、結餘等財務得到精準的分析。直觀地瞭解了每一部分的資金流向問題,資金從哪裏來?到哪裏去?還印證了借貸必相等的規則。

最後,通過工作績效、組織架構、員工滿意度等方面的分析。從圖表可以看出中石油的組織架構全局,每一位員工的KPI銷售績效、貢獻程度等情況,還用不同的顏色區分了不同層次的組織和人員的分佈狀態。

這些都是數據查詢處理和簡單的數據分析難以做到的。

作業清單(4/22)

【1】 繼續安裝Python 3.X 和 Orange3 軟件,是否完成?

已完成

【2】 完成常用的概率分佈代碼,如下圖

  • 正態分佈(Nomal Distribution)

在這裏插入圖片描述

import numpy as np
import matplotlib.pyplot as plt
import math
# 均值
u = 0
#標準差 
sig = math.sqrt(0.2)

x = np.linspace(u - 3 * sig, u + 3 * sig, 50)
y_sig = np.exp(-(x - u) ** 2 / (2 * sig ** 2) / (math.sqrt(2 * math.pi) * sig))
plt.plot(x,y_sig,"r-",linewidth=2)
plt.grid(True)
plt.show()

正態分佈概率密度函數曲線

  • 兩點分佈 /伯努利分佈(Two Point Distribution)

在這裏插入圖片描述

import numpy as np
import matplotlib.pyplot as plt
p = 0.7
x = [0,1]
y = [1-p, p]
plt.scatter(x,y) # 散點圖
plt.grid(True)
plt.show()
  • 二項分佈 (Binomial Distribution)

二項分佈,即重複n次的伯努利試驗,用ξ表示隨機試驗的結果。如果事件發生的概率是p,則不發生的概率q=1-p,N次獨立重複試驗中發生K次的概率是
P(ξ=K)= C(n,k) * p^k * (1-p)^(n-k),其中C(n, k) =n!/(k!(n-k)!). 那麼就說這個屬於二項分佈。其中P稱爲成功概率。記作ξ~B(n,p)
期望:Eξ=np;
方差:Dξ=npq, 其中q=1-p

import numpy as np
import matplotlib.pyplot as plt
from scipy.special import comb
p = 0.4
n = 10
x = np.linspace(0,n,n+1)
y = comb(n,x)*p**x*(1-p)**(n-x)
print(x)
print(y)
plt.scatter(x,y)
plt.grid(True)
plt.show()
  • 幾何分佈 (Geometric Distribution)

在這裏插入圖片描述

import numpy as np
import matplotlib.pyplot as plt
p = 0.4
n = 10
x = np.linspace(1,n,n)
y = p*(1-p)**(x-1)
print(x)
print(y)
plt.scatter(x,y)
plt.grid(True)
plt.show()
  • 泊松分佈(Possion Distribution)

在這裏插入圖片描述

import numpy as np
import matplotlib.pyplot as plt

x = np.random.poisson(lam = 5, size = 10000)
pillar = 15
a = plt.hist(x, pillar, color = 'g')
plt.plot(a[1][0:pillar], a[0],'r')
plt.grid()
plt.show()
  • 均勻分佈 (Uniform Distribution)

在這裏插入圖片描述

import numpy as np
import matplotlib.pyplot as plt
a = 3
b = 5
x = np.linspace(a, b, 50)
y = []
for i in range(0,50):
	y.append(1 / (b - a))
plt.plot(x,y,"r-",linewidth=2)
plt.grid(True)
plt.show()
  • 指數分佈 (Exponential Distribution)

在這裏插入圖片描述

import numpy as np
import matplotlib.pyplot as plt

x = np.random.exponential(scale = 100, size = 10000)
pillar = 25
a = plt.hist(x, pillar, color = 'g')
plt.plot(a[1][0:pillar], a[0],'r')
plt.grid()
plt.show()

【3】 完成最大似然估計MLE的Python代碼

用最大似然估計方法,估計正態分佈的平均數,方差

import numpy as np

fig = plt.figure()
mu = 30  # mean of distribution
sigma = 2  # standard deviation of distribution
x = mu + sigma * np.random.randn(10000)

def mle(x):
    u = np.mean(x)
    return u, np.sqrt(np.dot(x - u, (x - u).T) / x.shape[0])

print(mle(x))

【4】 DM Lab2 實驗結果用orange3挖掘(選做)

csv、線性迴歸

【1】 熟悉CSV文件的打開、讀取和寫入數據。

csv文件是一個文本文件,其中,列中的值由逗號分隔

  • 將數據寫入csv文件

利用pandas包將數據寫入DataFrame,再將DataFrame存儲爲csv文件

import pandas as pd

# 多組列表
no = [1, 2, 3, 4, 5, 6, 7]
square_feet = [150, 200, 250, 300, 350, 400, 400]
price = [6450, 7450, 8450, 9450, 11450, 15450, 18450]

# 字典中的key值即爲csv中的列名
data = pd.DataFrame({'No': no, 'square_feet': square_feet, 'price': price})

# 將DataFrame存儲爲csv, index 表示是否顯示行名, default = True
data.to_csv("data/房價.csv", index=False, sep=',')
  • 從csv文件中讀取數據

pandas庫提供read_csv

import pandas as pd
data = pd.read_csv('data/房價.csv')
print(data)

【2】 利用Orange3和Python orange方法完成線性迴歸

import Orange
import matplotlib.pyplot as plt
data = Orange.data.Table("F:\大三下\數據挖掘\DM_Lab\房價")
out_learner = Orange.regression.LinearRegressionLearner()

model = out_learner(data)

在這裏插入圖片描述

【3】 思考最大似然估計MLE和最小二乘之間的關係?

​ 最小二乘法以估計值與觀測值的差的平方和作爲損失函數,極大似然法則是以最大化目標值的似然概率函數爲目標函數,從概率統計的角度處理線性迴歸並在似然概率函數爲高斯函數的假設下同最小二乘建立了的聯繫。

【4】 根據DM Lab3數據散點圖,畫出一元迴歸線。

import numpy
from pandas import read_csv
from sklearn.linear_model import LinearRegression
import matplotlib.pyplot as plt

# 導入數據
data = read_csv('test1.csv')
u =data.corr() # 相關性係數
print(u)

lrModel = LinearRegression()
x = data.活動推廣費.values.reshape(-1,1)
lrModel.fit(x,data.銷售額)
print(lrModel.predict([[75]]))
alpha = lrModel.intercept_
beta = lrModel.coef_
new_r = alpha + beta * numpy.array([75])


plt.scatter(data.活動推廣費,data.銷售額)
plt.plot(data.活動推廣費,lrModel.predict(x))
plt.show()

【5】 根據DM Lab3實驗過程,母親身高167cm,預測孩子身高可能是多少?

import numpy as np
from sklearn.linear_model import LinearRegression
x = [154,157,158,159,160,161,162,163]
y = [155,156,159,162,161,164,165,166]

model = LinearRegression()
x =np.array(x).reshape(-1,1)
print(x)
model.fit(x,y)
print(model.predict([[167]]))

預測孩子身高171.4 cm

作業清單(4/29、5/4)

【1】 根據下列數據集(數據表存爲csv格式)建立線性迴歸模型。
在這裏插入圖片描述

(1) 預測面積爲1000平方英尺的房子價格。

要求: 完成2遍,第1遍可以參考課堂筆記、查閱網絡資料等方式完成;第2遍不參考任何輔助方式,限定15分鐘內獨立編寫代碼,完成此迴歸模型。

(2) 建立多元回顧模型。至少增加2項房子價格的特徵,例如:地段、新舊等因素。

(3) 將(1)和(2)整理成實驗報告。5月6日上課檢查實驗報告情況。

實驗報告

1. 一元迴歸——通過面積預測房價

  • 數據集:csv格式
No,square_feet,price
1,150,6450
2,200,7450
3,250,8450
4,300,9450
5,350,11450
6,400,15450
7,500,18450
  • 從csv文件中讀取數據
import pandas as pd
data = pd.read_csv('data/房價.csv')
  • 建立線性迴歸模型
from sklearn.linear_model import LinearRegression

model = LinearRegression()
x = data['square_feet'].values.reshape(-1, 1)
y = data['price']
model.fit(x, y)
  • 預測面積爲 1000 平方英尺的房子價格
print(model.predict([[1000]]))
  • 畫圖
import matplotlib.pyplot as plt
plt.scatter(data['square_feet'], y, color='blue')
plt.plot(x, model.predict(x), 'r-')
plt.grid(True)
plt.show()
  • 實驗結果
    • 預測面積爲 1000 平方英尺的房子價格爲35839.34
    • 從得到的圖片可以直觀看出,模型的擬合效果不錯。

2. 建立多元迴歸模型——波士頓房價預測

數據集

下載鏈接

屬性 含義 屬性 含義
CRIM 城鎮人均犯罪率 ZN 住宅用地超過 25000 sq.ft. 的比例。
INDUS 城鎮非零售商用土地的比例 CHAS 查理斯河空變量(如果邊界是河流,則爲1;否則爲0)
NOX 一氧化氮濃度 RM 住宅平均房間數
DIS 到波士頓五個中心區域的加權距離 RAD 輻射性公路的接近指數
TAX 每 10000 美元的全值財產稅率 PTRATIO 城鎮師生比例
B 1000(Bk-0.63)^ 2,其中 Bk 指代城鎮中黑人的比例 LSTAT 人口中地位低下者的比例。
MEDV 自住房的平均房價,以千美元計

使用的第三方庫

  • pandas: csv文件讀取、數據集整理
  • sklearn.linear_model.LinearRegression: 線性迴歸模型
  • sklearn.model_selection.train_test_split 數據集劃分爲訓練集、測試集
import pandas as pd
from sklearn.linear_model import LinearRegression
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split

讀取並處理數據

  • 讀取數據
data = pd.read_csv('data/housing.csv')
  • 不考慮城鎮人均犯罪率,模型評分較高
# 不要第一列的數據
new_data = data.iloc[:, 1:]

查看數據

  • 查看處理後的數據集
# 得到數據集且查看
print('head:', new_data.head(), '\nShape:', new_data.shape)
  • 檢查是否存在缺失值
# 缺失值檢驗
print(new_data[new_data.isnull().sum())

查看數據分散情況——繪製箱形圖

  • 輸出行數count平均值mean標準差std最小值min最大值max上四分位數75%, 中位數50%下四分位數25%
print(new_data.describe())
  • 箱型圖繪製代碼
new_data.boxplot()
plt.show()

數據集分割

將原始數據按照2:8比例分割爲“測試集”和“訓練集”

X_train, X_test, Y_train, Y_test = train_test_split(new_data.iloc[:, :13], new_data.MEDV, train_size=.80)

建立多元迴歸模型

根據訓練集建立模型

model = LinearRegression()
model.fit(X_train, Y_train)
a = model.intercept_
b = model.coef_
print("最佳擬合線:截距", a, ",迴歸係數:", b)

score = model.score(X_test, Y_test)
print(score)
原始數據特徵: (506, 13) ,訓練數據特徵: (404, 13) ,測試數據特徵: (102, 13)
原始數據標籤: (506,) ,訓練數據標籤: (404,) ,測試數據標籤: (102,)
最佳擬合線:截距 0.0 ,迴歸係數: [-1.74325842e-16  1.11629233e-16 -1.79794258e-15  7.04652389e-15
 -2.92277767e-15  2.97853711e-17 -8.23334194e-16  1.17159575e-16
  1.88696229e-17 -3.41643920e-16 -1.28401929e-17 -5.78208730e-17
  1.00000000e+00]
1.0

測試

Y_pred = model.predict(X_test)
print(Y_pred)
plt.plot(range(len(Y_pred)), Y_pred, 'b', label="predict")
plt.show()

畫圖表示結果

分別畫出實際值和預測值的折線

X_train, X_test, Y_train, Y_test = train_test_split(new_data.iloc[:, :13], new_data.MEDV, train_size=.80)

plt.figure()
plt.plot(range(len(X_test)), Y_pred, 'b', label="predict")
plt.plot(range(len(X_test)), Y_test, 'r', label="test")
plt.legend(loc="upper right")
plt.xlabel("the number of MEDV")
plt.ylabel('value of MEDV')
plt.show()

在這裏插入圖片描述

實驗結果分析

  • 根據預測值畫出的折線和根據實際值畫出的折線走向大致吻合,可以看出訓練的模型效果挺好的,用sklearn的score得到1.0的評分,也可以印證這一點。

知識點總結

  • 迴歸分析

    迴歸分析的目的是考察變量之間的數量關係,主要解決以下幾個問題:

    (1)利用一組樣本數據,確定變量之間的數學關係式;

    (2)對這些關係式的可信程度進行各種統計檢驗,找出哪些變量的影響是顯著的,哪些是不顯著的;

    (3)利用關係式,根據一個或幾個變量的取值來估計另一個變量的取值,並給出估計的可靠程度。

  • 一元線性迴歸

    只涉及一個自變量的迴歸稱爲一元迴歸,描述兩個具有線性關係的變量之間關係的方程稱爲迴歸模型,一元線性迴歸模型可表示爲:
    y=ax+b y=ax+b

    上式稱爲理論迴歸模型,對它有以下假定:

    (1)y與x之間具有線性關係;

    (2)x是非隨機的,在重複抽樣中,x的取值是固定的。一元線性迴歸的估計的迴歸方程形式爲:

  • 多元迴歸分析

    用迴歸方程定量地刻畫一個因變量與多個自變量間的線性依存關係,稱爲多元迴歸分析,簡稱多元迴歸。

    img
  • DataFrame.iloc常見用法

    得到屬性名、第一行數據、數據類型

    print(data.iloc[0])
    
    No                1
    square_feet     150
    loaction          4
    built            10
    price          6450
    Name: 0, dtype: int64
    

    得到屬性名、第二行數據、數據類型

    print(data.iloc[1])
    
    No                2
    square_feet     200
    loaction          5
    built             9
    price          7450
    Name: 1, dtype: int64
    

    得到全部數據

    方法一

    print(data.iloc[:])
    

    方法二

    print(data.iloc[0:])
    

    方法三

    print(data.iloc[:, :])
    
       No  square_feet  loaction  built  price
    0   1          150         4     10   6450
    1   2          200         5      9   7450
    2   3          250         3      7   8450
    3   4          300         3      4   9450
    4   5          350         4      3  11450
    5   6          400         2      4  15450
    6   7          400         1      2  18450
    

    得到第二行開始的數據

    print(data.iloc[1:])
    
       No  square_feet  loaction  built  price
    1   2          200         5      9   7450
    2   3          250         3      7   8450
    3   4          300         3      4   9450
    4   5          350         4      3  11450
    5   6          400         2      4  15450
    6   7          400         1      2  18450
    

    得到第3-n行,第4-m列的數據(假設共有n行,m列)

    print(data.iloc[2:, 3:])
    
       built  price
    2      7   8450
    3      4   9450
    4      3  11450
    5      4  15450
    6      2  18450
    
  • 箱形圖(Box-plot)是一種用作顯示一組數據分散情況資料的統計圖。

  • 箱線圖的繪製方法是:先找出一組數據的上邊緣、下邊緣、中位數和兩個四分位數;然後, 連接兩個四分位數畫出箱體;再將上邊緣和下邊緣與箱體相連接,中位數在箱體中間。

過擬合&欠擬合

【2】 結合下圖(a)和(b)解釋什麼是過擬合和欠擬合?通常用什麼方法解決這兩個問題?

在這裏插入圖片描述

  • 過擬合

    過擬合是指學習時選擇的模型所包含的參數過多,以至於出現這一模型對已知數據預測得很好,對未知數據預測的很差的現象。

  • 過擬合出現原因

    • 建模樣本選取有誤,如樣本數量太少,選樣方法錯誤,樣本標籤錯誤等,導致選取的樣本數據不足以代表預定的分類規則
    • 樣本噪音干擾過大,使得機器將部分噪音認爲是特徵從而擾亂了預設的分類規則
    • 假設的模型無法合理存在,或者說是假設成立的條件實際並不成立
    • 參數太多,模型複雜度過高
    • 對於決策樹模型,如果我們對於其生長沒有合理的限制,其自由生長有可能使節點只包含單純的事件數據(event)或非事件數據(no event),使其雖然可以完美匹配(擬合)訓練數據,但是無法適應其他數據集
    • 對於神經網絡模型:
      對樣本數據可能存在分類決策面不唯一,隨着學習的進行,,BP算法使權值可能收斂過於複雜的決策面;
      b)權值學習迭代次數足夠多(Overtraining),擬合了訓練數據中的噪聲和訓練樣例中沒有代表性的特徵
  • 過擬合解決方法

    1. 正則化方法

      正則化方法是指在進行目標函數或代價函數優化時,在目標函數或代價函數後面加上一個正則項,一般有L1正則與L2正則等。

      • L1正則:

        L1正則即在原有的損失函數的基礎上添加參數向量的L1範數,正則項的係數用於平衡原有損失函數和正則項之間的關係。

      • L2正則

        L2正則即在原有的損失函數的基礎上添加參數向量的L2範數。

        在損失函數中添加正則項符合奧斯卡姆剃刀原理:在所有可能選擇的模型中,能夠很好解釋已知數據並且十分簡單的模型纔是最好的模型。

    2. 交叉驗證

      如果給定的樣本數據充足,進行模型選擇的一種簡單方法是隨機地將數據集切分爲三部分,分別爲訓練集,驗證集和測試集

      訓練集用來訓練模型,驗證集用於模型的選擇,測試集用於方法的評估。

      在學習到不同複雜度的模型中,選擇對驗證集有最小預測誤差的模型

      交叉驗證有以下幾種方法:

      1、簡單交叉驗證
      隨機地將已給數據分爲兩部分,一部分作爲訓練集,一部分作爲測試集,然後用訓練集在各種條件下訓練模型,從而得到不同的模型,在測試集上評價各個模型的測試誤差,選出測試誤差最小的模型。

      2、S折交叉驗證
      首先隨機地將已給數據切分爲S個互不相交的大小相同的子集,然後利用S-1個子集的數據訓練模型,利用餘下的子集測試模型,將這一過程對可能的S種選擇重複進行,最後選出S次評測中平均測試誤差最小的模型。

      3、留一交叉驗證
      S折交叉驗證的一種特殊情況,即S=N(N爲訓練集樣本的數量)

    3. Early stopping

      對模型進行訓練的過程即是對模型的參數進行學習更新的過程,這個參數學習的過程往往會用到一些迭代方法,如梯度下降(Gradient descent)學習算法。

      Early stopping便是一種迭代次數(epochs)截斷的方法來防止過擬合,即在模型對訓練數據集迭代收斂之前停止迭代來防止過擬合。

      具體做法是,在每一詞迭代結束時(一個迭代集爲對所有的訓練數據的一輪遍歷)計算驗證集的accuracy,當accuracy不再提高時,就停止訓練。

      在訓練的過程中,記錄到目前爲止最好的validation accuracy,當連續10次迭代(或者更多次)沒達到最佳accuracy時,則可以認爲accuracy不再提高了。此時便可以停止迭代了。這種策略也稱爲“No-improvement-in-n”,n即迭代的次數,可以根據實際情況取,如10、20、30……

    4. 數據集擴充

      在數據挖掘領域流行着這樣的一句話,“有時候往往擁有更多的數據勝過一個好的模型”。

      訓練數據與將來的數據是獨立同分布的
        
      一般有以下方法:

    • 從數據源採集更多數據
    • 複製原有數據並加上隨機噪聲
    • 重採樣
    • 根據當前數據集估計數據分佈參數,使用該分佈產生更多數據等
    1. Dropout

      Dropout方法是通過修改ANN中隱藏層的神經元個數來防止ANN的過擬合。

  • 欠擬合

    “欠擬合”常常在模型學習能力較弱,而數據複雜度較高的情況出現,此時模型由於學習能力不足,無法學習到數據集中的“一般規律”,因而導致泛化能力弱。

  • 欠擬合出現原因

    • 模型複雜度過低
    • 特徵量過少
  • 常見解決方法

    • 增加新特徵,可以考慮加入進特徵組合、高次特徵,來增大假設空間
    • 添加多項式特徵,這個在機器學習算法裏面用的很普遍,例如將線性模型通過添加二次項或者三次項使模型泛化能力更強
    • 減少正則化參數,正則化的目的是用來防止過擬合的,但是模型出現了欠擬合,則需要減少正則化參數
    • 使用非線性模型,比如核SVM 、決策樹、深度學習等模型
    • 調整模型的容量(capacity),通俗地,模型的容量是指其擬合各種函數的能力
    • 容量低的模型可能很難擬合訓練集;使用集成學習方法,如Bagging ,將多個弱學習器Bagging
  • 參考文章

    過擬合
    欠擬合和過擬合出現原因及解決方案

【3】 預測鮑魚的年齡。網上下載“鮑魚數據集”(見微信羣,鮑魚數據集.csv),建立線性迴歸模型,指出簡單線性迴歸模型進行預測的問題,思考如何解決?

import pandas as pd
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
# 讀取鮑魚數據集(數據集下載鏈接:https://aistudio.baidu.com/aistudio/datasetdetail/361)
data = pd.read_csv('鮑魚年齡.csv')
# 清理數據
new_data = data.iloc[:, 1:]
# 得到數據集且查看
print('head:', new_data.head(), '\nShape:', new_data.shape)

print(new_data.describe())
# 缺失值檢驗
print(new_data[new_data.isnull() == True].count())
new_data.boxplot()
plt.show()
print(data.corr())
print(new_data.corr())
X_train, X_test, Y_train, Y_test = train_test_split(new_data.iloc[:, :8], new_data.年齡, train_size=.80)
print("原始數據特徵:", new_data.iloc[:, :8].shape, ",訓練數據特徵:", X_train.shape, ",測試數據特徵:", X_test.shape)
print("原始數據標籤:", new_data.年齡.shape, ",訓練數據標籤:", Y_train.shape, ",測試數據標籤:", Y_test.shape)

model = LinearRegression()
model.fit(X_train, Y_train)
a = model.intercept_
b = model.coef_
print("最佳擬合線:截距", a, ",迴歸係數:", b)
# 模型評分
score = model.score(X_test, Y_test)
print(score)

Y_pred = model.predict(X_test)
print(Y_pred)
plt.plot(range(len(Y_pred)), Y_pred, 'b', label="predict")
plt.show()

X_train, X_test, Y_train, Y_test = train_test_split(new_data.iloc[:, :8], new_data.年齡, train_size=.80)

plt.figure()
plt.plot(range(len(Y_pred)), Y_pred, 'b', label="predict")
plt.plot(range(len(X_test)), Y_test, 'r', label="test")
plt.legend(loc="upper right")
plt.xlabel("the number of age")
plt.ylabel('value of age')
plt.show()
  • 問題

    (1)線性迴歸在數據量較少的情況下會出現過擬合的現象

    (2)對於非線性數據或者數據特徵間具有相關性多項式迴歸難以建模.

    (3)難以很好地表達高度複雜的數據。

  • 解決

    • 使用ridge、Lasso迴歸可以在一定程度上解決過擬合問題。

【4】 Python實現新型冠狀病毒傳播模型及預測(選做)

數據清洗

【1】 數據清洗是數據挖掘模型建立過程中很重要的一步嗎一般,清洗的方法包括什麼?

  • 數據清洗是數據挖掘模型建立過程中很重要的一步。 數據清洗是指發現並糾正數據文件中可識別的錯誤的最後一道程序,包括檢查數據一致性,處理無效值和缺失值等。

  • 數據清洗的方法:

    數據清理一般針對具體應用,因而難以歸納統一的方法和步驟,但是根據數據不同可以給出相應的數據清理方法。

    1.解決不完整數據( 即值缺失)的方法

    大多數情況下,缺失的值必須手工填入( 即手工清理)。當然,某些缺失值可以從本數據源或其它數據源推導出來,這就可以用平均值、最大值、最小值或更爲複雜的概率估計代替缺失的值,從而達到清理的目的。

    2.錯誤值的檢測及解決方法

    統計分析的方法識別可能的錯誤值或異常值,如偏差分析、識別不遵守分佈或迴歸方程的值,也可以用簡單規則庫( 常識性規則、業務特定規則等)檢查數據值,或使用不同屬性間的約束、外部的數據來檢測和清理數據。

    3.重複記錄的檢測及消除方法

    數據庫中屬性值相同的記錄被認爲是重複記錄,通過判斷記錄間的屬性值是否相等來檢測記錄是否相等,相等的記錄合併爲一條記錄(即合併/清除)。合併/清除是消重的基本方法。

    4.不一致性( 數據源內部及數據源之間)的檢測及解決方法

    從多數據源集成的數據可能有語義衝突,可定義完整性約束用於檢測不一致性,也可通過分析數據發現聯繫,從而使得數據保持一致。


【2】 採用刪除和填補兩種方法進行清洗數據。

import pandas as pd
import numpy as np
from sklearn.experimental import enable_iterative_imputer
from sklearn.impute import IterativeImputer

data = pd.read_csv('Iris.csv')

data1 = data.dropna() # 刪除數據缺失的行
data2 = data.dropna(axis=1)# 刪除數據缺失的列

print(data.fillna(method='pad'))# 同一列上一行的數填補
print(data.fillna(0)
print(data.fillna(data.mean()))# 同一列平均值填補
print(data.fillna(data.median()))# 同一列中位數填補
print(data.fillna(data.mode()))# 同一列衆數填補

【3】 對【2】題數據採用迴歸方法填補。

import pandas as pd
import numpy as np
from sklearn.experimental import enable_iterative_imputer
from sklearn.impute import IterativeImputer

data = pd.read_csv('Iris.csv')

new_data = data.iloc[:, 0:4]

model = IterativeImputer(max_iter=10, random_state=0)
model.fit(new_data)

print(model.transform(new_data))

作業清單(5/11)

【1】 Pandas Series是什麼? Pandas中的DataFrame是什麼?如何將numpy數據轉成DataFrame格式的數據?如何將Series數據轉成DataFrame格式的數據?如何將DataFrame轉換爲NumPy數組?如何對DataFrame進行排序?什麼是數據聚合?(注:每一小問,舉例說明)

Pandas是什麼?

Pandas是一個強大的分析結構化數據的工具集;它的使用基礎是Numpy(提供高性能的矩陣運算);用於數據挖掘數據分析,同時也提供數據清洗功能。

  • 導入
import pandas as pd
  • Pandas排序方式
    • 根據索引排序sort_index()
    • 根據實際值排序sort_values()排序
    • 默認升序,參數ascending=False時爲降序

【一維數據:Pandas Series】

它是一種類似於一維數組的對象,是由一組數據(各種NumPy數據類型)以及一組與之相關的數據標籤(即索引)組成。僅由一組數據也可產生簡單的Series對象。

>>> import pandas as pd
>>> import numpy as np
>>> b = pd.Series(np.arange(5))
>>> b
0    0
1    1
2    2
3    3
4    4
dtype: int32
>>> s = pd.Series(np.arange(5),index=['a','b','c','d','e'])
>>> print(s)
a    0
b    1
c    2
d    3
e    4
dtype: int32
  • 通過字典創建Series對象
>>> dict={'apple':45,'orange':23,'peach':12}
>>> s =Series(dict)
>>> s = pd.Series(dict)
>>> s
apple     45
orange    23
peach     12
dtype: int64
  • 取值
>>> a = pd.Series([1,2,3])
>>> a.values
array([1, 2, 3], dtype=int64)
>>> print(a.values)
[1 2 3]
  • 根據索引取值
>>> a[2]
3
  • 根據索引進行排序(升序)
>>> s = pd.Series([56,34,45,67,12,67,84],index = list('gbcadfe'))
>>> s.sort_index()
a    67
b    34
c    45
d    12
e    84
f    67
g    56
dtype: int64
  • 根據值進行排序(升序)
>>> s.sort_values()
d    12
b    34
c    45
g    56
a    67
f    67
e    84
dtype: int64

【二維數據:DataFrame】

DataFrame是Pandas中的一個表格型的數據結構,包含有一組有序的列,每列可以是不同的值類型(數值、字符串、布爾型等),DataFrame即有行索引也有列索引,可以被看做是由Series組成的字典。

  • 創建
import pandas as pd

a = [[1,2,3],
	[4,5,6],
	[7,8,9]]
df = pd.DataFrame(a,index=list('123'),columns = ['A', 'B', 'C'])
print(df)

​ 運行結果

   A  B  C
1  1  2  3
2  4  5  6
3  7  8  9
  • 將numpy數據轉成DataFrame格式的數據
>>> import pandas as pd
>>> import numpy as np
>>> c = np.arange(20,32).reshape(3,4)
>>> c
array([[20, 21, 22, 23],
       [24, 25, 26, 27],
       [28, 29, 30, 31]])
>>> c = pd.DataFrame(c)
>>> c
    0   1   2   3
0  20  21  22  23
1  24  25  26  27
2  28  29  30  31
>>> c = pd.DataFrame(c,index=list('abc'),columns=list('ABCD'))
>>> c
    A   B   C   D
a NaN NaN NaN NaN
b NaN NaN NaN NaN
c NaN NaN NaN NaN
>>> d = np.arange(20,32).reshape(3,4)
>>> c = pd.DataFrame(d,index=list('abc'),columns=list('ABCD'))
>>> c
    A   B   C   D
a  20  21  22  23
b  24  25  26  27
c  28  29  30  31
  • 將DataFrame轉換爲NumPy
>>> a = [[4,6,1],[3,5,2],[9,5,7]]
>>> df = pd.DataFrame(a,index=list('cbd'),columns=list('ahg'))
>>> df.values
array([[4, 6, 1],
       [3, 5, 2],
       [9, 5, 7]], dtype=int64)
  • 將Series數據轉成DataFrame格式的數據
>>> import pandas as pd
>>> import numpy as np
>>> s = pd.Series(np.arange(5),index = list('abcde'))
>>> s
a    0
b    1
c    2
d    3
e    4
dtype: int32
>>> s = pd.DataFrame(s)
>>> s
   0
a  0
b  1
c  2
d  3
e  4   
  • 數組對DataFrame進行排序
    • 根據索引排序
>>> a = [[4,6,1],[3,5,2],[9,5,7]]
>>> df = pd.DataFrame(a,index=list('cbd'),columns=list('ahg'))
>>> df.sort_index() # 根據行索引排列
   a  h  g
b  3  5  2
c  4  6  1
d  9  5  7
>>> df.sort_index(ascending=False) # 根據行索引降序排列
   a  h  g
d  9  5  7
c  4  6  1
b  3  5  2
>>> df.sort_index(axis=1) # 根據列索引排列
   a  g  h
c  4  1  6
b  3  2  5
d  9  7  5
>>> df.sort_index(axis=1,ascending=False) # 根據列索引排列
   h  g  a
c  6  1  4
b  5  2  3
d  5  7  9

​ - 根據實際值排序

​ 通過by參數指定需要排序的列值,可指定多列

>>> df.sort_values(by='h') # 根據h列的值升序排列
   a  h  g
b  3  5  2
d  9  5  7
c  4  6  1
>>> df.sort_values(by='h',ascending=False)# 根據h列的值降序排列
   a  h  g
c  4  6  1
b  3  5  2
d  9  5  7
>>> df.sort_values(by=['h','g'],ascending=False)# 優先考慮排在前面的列
   a  h  g
c  4  6  1
d  9  5  7
b  3  5  2

【2】 利用iris.csv數據集,建立KNN模型,預測Sepal.Length\Sepal.Width\Petal.Length\Petal.Width分別爲(6.3,3.1,4.8,1.4)時,屬於鳶尾花的哪個類別?編寫KNN源代碼。

KNN(K-NearestNeighbor) 算法思想:

令D爲訓練數據集,當測試集d出現時,將d與D中所有的樣本進行比較,計算他們之間的相似度(或者距離)。從D中選出前k個最相似的樣本,則d的類別由k個最近鄰的樣本中出現最多的類別決定。

from numpy import *
import pandas as pd

data = pd.read_csv('Iris.csv')
dataSet = data.iloc[:, 0:4]
labels = data['Species']
print(labels)
# 行數
numSamples = dataSet.shape[0]
print(numSamples)

# 測試數據 Iris-versicolor
new_t = array([6.3, 3.1, 4.8, 1.4])

# 求歐式距離
diff = tile(new_t, (numSamples, 1))-dataSet
squreDiff = diff**2
squreDist = sum(squreDiff, axis=1)
distance = squreDist ** 0.5
print(distance)

# 從小到大排序
sortedDistIndices = argsort(distance)

print(sortedDistIndices)
classCount = {}
K = 4
for i in range(K):
    voteLabel = labels[sortedDistIndices[i]]
    print(voteLabel)
    classCount[voteLabel] = classCount.get(voteLabel, 0) + 1
print(classCount)

maxCount = 0
for k, v in classCount.items():
    if v > maxCount:
        maxCount = v
        maxIndex = k

print("Your input is:", new_t, "and classified to class: ", maxIndex)

  • 預測Sepal.Length\Sepal.Width\Petal.Length\Petal.Width分別爲(6.3,3.1,4.8,1.4)時,屬於鳶尾花setosa.

【3】 計算X = [1,2,3]和Y = [0,1,2]的曼哈頓距離(Manhattan Distance),切比雪夫距離 ,閔可夫斯基距離,標準化歐氏距離,馬氏距離。給出計算公式,並根據公式計算。利用Python實現上述距離。

【曼哈頓距離(Manhattan Distance)】

  • 公式:

d(x,y)=k=1nxkyk d(x,y)=\sum_{k=1}^{n}{|x_k-y_k|}

  • python 代碼

    def ManhattanDist(A,B):
    	return sum([abs(a-b) for (a,b) in zip(A,B)])
    

【切比雪夫距離 ( Chebyshev distance )】

  • 公式

d(x,y)=max(xkyk) d(x,y)=max(|x_k-y_k|)

​ 其中k = 1,2,3,…,n

  • python代碼

    # 切比雪夫距離
    def cheDist(A, B):
    	return max([abs(a - b) for (a, b) in zip(A, B)])
    

【閔可夫斯基距離( Minkowski distance )】

  • 公式

d(x,y)=(k=1nxkykp)1/p d(x,y)=(\sum_{k=1}^{n}|x_k-y_k|^p)^{1/p}

​ p = 1: 曼哈頓距離

​ p = 2: 歐式距離

  • python代碼

    # 閔可夫斯基距離
    def minkowskiDist(A, B, p):
    	return sum([abs(a - b)**p for (a, b) in zip(A,B)])**(1/p)
    

【歐氏距離】

  • 公式:
    d(x,y)=k=1n(xkyk)2 d(x,y)=\sqrt{\sum_{k=1}^{n}(x_k-y_k)^2}

  • python 代碼

    import math
    def eculiDist(A,B):
        return math.sqrt(sum([(a - b)**2i for (a.b) in zip(A,B)]))
    

【標準化歐氏距離】

  • 公式
    d(x,y)=k=1n(xkyksk)2    sk d(x,y)=\sqrt{\sum_{k=1}^{n}(\frac{x_k-y_k}{s_k})^2}~~~,~s_k爲各分量的方差

    def standardEucliDist(A,B):
    	X=np.vstack([A,B])
    	sk=np.var(X,axis=0,ddof=1)
    	#print(sk)
    	return np.sqrt(sum([((x - y) ** 2 /sk) for (x,y) in zip(A,B)]))
    

【馬氏距離】

  • 公式

在這裏插入圖片描述
如果Σ是單位矩陣,則馬氏距離退化成歐式距離;
如果Σ是對角矩陣,則稱爲歸一化後的歐式距離。

def mashi_distance(x,y):
    X=np.vstack([x,y])
    print(X)
    XT=X.T
    print(XT)


    S=np.cov(X)   #兩個維度之間協方差矩陣
    SI = np.linalg.inv(S) #協方差矩陣的逆矩陣
    #馬氏距離計算兩個樣本之間的距離,此處共有4個樣本,兩兩組合,共有6個距離。
    n=XT.shape[0]
    d1=[]
    for i in range(0,n):
        for j in range(i+1,n):
            delta=XT[i]-XT[j]
            d=np.sqrt(np.dot(np.dot(delta,SI),delta.T))
            print(d)
            d1.append(d)

作業清單(5/13)

【1】選擇4名同學A、B、C、D,兩次小測成績,利用Kmeans算法分爲“優秀”和“及格”兩類。@注意:不能直接調用sklearn第三方庫的KMeans函數,根據課堂講授的分類過程,編寫代碼。撰寫實驗報告。

學生姓名 小測1 小測2
A 1 1
B 2 1
C 4 3
D 5 4

KMeans實驗報告(K=2)

實驗目的

  • 選擇4名同學A、B、C、D,兩次小測成績,利用Kmeans算法分爲“優秀”和“及格”兩類。

  • 限制:不能直接調用sklearn第三方庫的KMeans函數。

學生姓名 小測1 小測2
A 1 1
B 2 1
C 4 3
D 5 4

實驗步驟

1. 數據準備

  • 將數據儲存爲字典對象

    data = {'A': [1, 1], 'B': [2, 1], 'C': [4, 3], 'D': [5, 4]}
    
  • 爲了方便取值,將數據進一步轉化成Series對象

    import pandas as pd
    
    # 轉成Series, 方便取值
    data = pd.Series(data)
    
    # print(data.values[1])
    # print(data.index[2])
    

2. KMeans算法實現

  • KMeans算法涉及兩點之間距離的計算,我們提前寫好一個函數:輸入兩個點的座標,返回兩點之間的歐氏距離

  • def eucliDist(A, B):
        return math.sqrt(sum([(a - b) ** 2 for (a, b) in zip(A, B)]))
    
  • 函數k_means(c,data)實現KMeans算法:

    a. 輸入質心列表c,待聚類Series對象data

    b. 計算data中的每個點分到2個質心的距離,得到一個矩陣,如

    [[0.0, 1.0, 3.605551275463989, 5.0], [1.0, 0.0, 2.8284271247461903, 4.242640687119285]]
    

    c. 比較矩陣同一列的數值大小,將對應的學生劃歸距離較短的質心所屬的類,將標籤存儲爲列表,如

    ['及格', '優秀', '優秀', '優秀']
    

    d. 重新計算質心的座標,新質心的座標=被劃歸同一類點的座標的平均值

    e. 重複b~d,直到質心座標不再變化

    f. 返回標籤列表

    • 完整函數如下
    def k_means(c,data):
        # a. 輸入質心列表`c`,待聚類Series對象`data`
    
        # b. 計算data中的每個點分到2個質心的距離,得到一個矩陣,如
        metrix = [[eucliDist(a, b) for a in data.values] for b in c]
        print(metrix)
        # c. 比較矩陣同一列的數值大小,將對應的學生劃歸距離較短的質心所屬的類,將標籤存儲爲列表
        classifier = ['及格' if a < b  else '優秀' for(a,b) in zip(metrix[0],metrix[1]) ]
        print(classifier)
    
        # d. 重新計算質心的座標,新質心的座標=被劃歸同一類點的座標的平均值
        n1 = 0
        c1,c2 = [0, 0], [0, 0]
        num = len(data)
        for i in range(0, num):
    
            if classifier[i] == '及格':
                c1 = [a + b for (a,b) in zip(c1,data.values[i])]
                n1 = n1 + 1
            elif classifier[i] == '優秀':
                c2 = [a + b for (a,b) in zip(c2,data.values[i])]
    
        c1 = [a /n1 for a in c1]
        c2 = [a/(num - n1) for a in c2]
    
        # e. 重複b~d,直到質心座標不再變化
        if c != [c1, c2]:
            c = [c1,c2]
            print("center:" + str(c))
            k_means(c, data)
        return classifier
    

3. 設置參數,調用函數,得到實驗結果

  • 因爲要把數據分成兩類,所以我們要選取K=2個點作爲初始質心,分別爲(1,1),(2,1)

    • 選取不同的初始質心,聚類結果不同
    # 選擇K=2個點作爲初始質心
    c = [[1,1], [2,1]]
    
  • 調用函數

    label = k_means(c, data)
    
  • 整理結果:以Series格式輸出

    print(pd.Series((label), index = data.index))
    

實驗結果

  • 初始質點取[1,1], [2,1]時,結果爲
學生姓名 小測1 小測2
A 1 1 及格
B 2 1 優秀
C 4 3 優秀
D 5 4 優秀

【2】根據下列成績單,將5名同學成績歸爲A類、B類、C類,利用Kmeans算法實現。@注意:不能直接調用sklearn第三方庫的KMeans函數,根據課堂講授的分類過程,編寫代碼。撰寫實驗報告。

學生姓名 小測1 小測2 小測3 期末成績 項目答辯 成績
張三 12 15 13 28 24
李四 7 11 10 19 21
王五 12 14 11 27 23
趙六 6 7 4 13 20
劉七 13 14 13 27 25

KMeans實驗報告(K=3)

實驗目的

  • 根據下列成績單,將5名同學成績歸爲A類、B類、C類。

  • 限制:使用Kmeans算法實現,但不直接調用sklearn第三方庫的KMeans函數。

學生姓名 小測1 小測2 小測3 期末成績 項目答辯 成績
張三 12 15 13 28 24
李四 7 11 10 19 21
王五 12 14 11 27 23
趙六 6 7 4 13 20
劉七 13 14 13 27 25

實驗步驟

1. 數據準備

  • 將數據儲存爲csv文件,格式如下

    學生姓名,小測1,小測2,小測3,期末成績,項目答辯
    張三,12,15,13,28,24
    李四,7,11,10,19,21
    王五,12,14,11,27,23
    趙六,6,7,4,13,20
    劉七,13,14,13,27,25
    
  • 在從csv文件中讀取數據,並選取可用的數據(排除姓名列)

    data = pd.read_csv('grade.csv')
    new_data = data.iloc[:, 1:].values
    

2. KMeans算法實現

  • KMeans算法涉及兩點之間距離的計算,我們提前寫好一個函數:輸入兩個點的座標,返回兩點之間的歐氏距離

    def eucliDist(A, B):
        return math.sqrt(sum([(a - b) ** 2 for (a, b) in zip(A, B)]))
    
  • 函數k_means(c,data,max,label)實現KMeans算法:

    a. 輸入:質心列表c,待聚類數據data,最大迭代次數max,標籤列表label

    b. 計算data中的每個點分別到3個質心的歐式距離,得到一個矩陣metrix

    metrix = [[eucliDist(a, b) for a in data] for b in c]
    

    c. 比較矩陣metrix同一列的數值大小,將對應的學生劃歸距離較短的質心所屬的類,將標籤存儲爲列表.

    classifier = []
        for (d, e, f) in zip(metrix[0], metrix[1], metrix[2]):
            m = min(d, e, f)
            if d == m:
                classifier.append(label[0])
            elif e == m:
                classifier.append(label[1])
            else:
                classifier.append(label[2])
    

    d. 重新計算質心的座標,新質心的座標=被劃歸同一類點的座標的平均值

    n1, n2 = 0, 0
    c1 = [0, 0, 0, 0, 0]
    c2 = c1
    c3 = c1
        for i in range(0, num):
    
            if classifier[i] == label[0]:
                c1 = [a + b for (a, b) in zip(c1, data[i])]
                n1 = n1 + 1
            elif classifier[i] == label[1]:
                c2 = [a + b for (a, b) in zip(c2, data[i])]
                n2 = n2 + 1
            else:
                c3 = [a + b for (a, b) in zip(c3, data[i])]
    
        c1 = [a / n1 for a in c1]
        c2 = [a / n2 for a in c2]
        c3 = [a / (num - n1 - n2) for a in c3]
    

    e. 重複b~d,直到質心座標不再變化或達到最大迭代次數

    f. 返回標籤列表

    • 完整函數如下
    def k_means(c, data, max,label):
        # a. 輸入質心列表c,待聚類數據`data`,最大迭代次數max
        max = max - 1
        num = len(data)
        # b. 計算data中的每個點分到k個質心的距離,得到一個矩陣,如
        metrix = [[eucliDist(a, b) for a in data] for b in c]
        print(metrix)
        # c. 比較矩陣同一列的數值大小,將對應的學生劃歸距離較短的質心所屬的類,將標籤存儲爲列表
        classifier = []
        for (d, e, f) in zip(metrix[0], metrix[1], metrix[2]):
            m = min(d, e, f)
            if d == m:
                classifier.append(label[0])
            elif e == m:
                classifier.append(label[1])
            else:
                classifier.append(label[2])
    
        print(classifier)
    
        # d. 重新計算質心的座標,新質心的座標=被劃歸同一類點的座標的平均值
        n1, n2 = 0, 0
        c1 = [0, 0, 0, 0, 0]
        c2 = c1
        c3 = c1
        for i in range(0, num):
    
            if classifier[i] == label[0]:
                c1 = [a + b for (a, b) in zip(c1, data[i])]
                n1 = n1 + 1
            elif classifier[i] == label[1]:
                c2 = [a + b for (a, b) in zip(c2, data[i])]
                n2 = n2 + 1
            else:
                c3 = [a + b for (a, b) in zip(c3, data[i])]
    
        c1 = [a / n1 for a in c1]
        c2 = [a / n2 for a in c2]
        c3 = [a / (num - n1 - n2) for a in c3]
    
        print(max)
        print([c1,c2,c3])
        # e. 重複b~d,直到質心座標不再變化,或達到最大迭代次數
        if c != [c1, c2, c3] and max > 0:
    
            c = [c1, c2, c3]
            print(c)
            k_means(c, data, max, label)
        return classifier
    

3. 設置參數,調用函數,得到結果

  • 設置初始質心、標籤列表、最大迭代次數

    # 選擇K個點作爲初始質心
    c = [[12, 15, 13, 28, 24], [ 7, 11, 10, 19, 21],[12, 14, 11, 27, 23]]
    label = ['A', 'B', 'C']
    max = 20
    
  • 調用函數,整理結果

    grade = k_means(c, new_data, max, label)
    grade = pd.Series(grade, index=data['學生姓名'])
    print(grade)
    

實驗結果

  • 初始質心爲[12, 15, 13, 28, 24], [ 7, 11, 10, 19, 21],[12, 14, 11, 27, 23]時,迭代2次即收斂,結果如下
學生姓名 小測1 小測2 小測3 期末成績 項目答辯 成績
張三 12 15 13 28 24 A
李四 7 11 10 19 21 B
王五 12 14 11 27 23 C
趙六 6 7 4 13 20 B
劉七 13 14 13 27 25 A

【3】 利用Sklearn的標準KNN和KMeans方法,數據集爲“wine.csv”(見微信羣),通過KNN算法,對葡萄酒的測試集進行標註,然後對比預測標籤值和已知標籤值,得到KNN算法的預測準確率。通過Kmeans算法,對無標籤的“wine.csv”進行分類,自己設定K值和初始中心點值。

Sklearn的標準KNN類

from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.neighbors import KNeighborsClassifier
import pandas as pd

data = pd.read_csv('wine.csv')

x_train,x_test,y_train,y_test = train_test_split(data.iloc[:, 0:13], data.iloc[:, 13])

# 標準化
transfer = StandardScaler()
x_train = transfer.fit_transform(x_train)
x_test = transfer.fit_transform(x_test)

estimator = KNeighborsClassifier()
estimator.fit(x_train, y_train)
predict = estimator.predict(x_test)
score = estimator.score(x_test,y_test)
print(score)
  • 預測準確率:0.9333333333333333

Sklearn的標準K-means類

KMeans類主要參數

  • KMeans類的主要參數有:
參數 描述
n_clusters k值,類別數
max_iter 最大迭代次數
(一般如果是凸數據集的話可以不管這個值,如果數據集不是凸的,可能很難收斂,此時可以指定最大的迭代次數讓算法可以及時退出循環。)
n_init 用不同的初始化質心運行算法的次數。
(由於K-Means是結果受初始值影響的局部最優的迭代算法,因此需要多跑幾次以選擇一個較好的聚類效果,默認是10,一般不需要改。如果你的k值較大,則可以適當增大這個值。)
init 初始值選擇的方式
(可以爲完全隨機選擇’random’,優化過的’k-means++‘或者自己指定初始化的k個質心。一般建議使用默認的’k-means++’)
algorithm 有“auto”, “full” or “elkan”三種選擇。“full"就是傳統的K-Means算法, “elkan”是elkan K-Means算法。默認的"auto"則會根據數據值是否是稀疏的,來決定如何選擇"full"和“elkan”。一般數據是稠密的,就是 “elkan”,否則就是"full”。一般來說建議直接用默認的"auto"
  • 設定k = 3,初始質心爲前三組數據
from sklearn.datasets import load_iris
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
import pandas as pd
data = pd.read_csv('wine.csv')
x = data.values[:, 0:13]
# plt.scatter(x[:,0],x[:,:1],c='red',marker = 'o')
estimator = KMeans(n_clusters = 3, init=x[0:3],n_init=1)
estimator.fit(x)

# 打標籤
Label_pred = estimator.labels_

x0 = x[Label_pred == 0]
x1 = x[Label_pred == 1]
x2 = x[Label_pred == 2]

plt.scatter(x0[:,3],x0[:,12],c='blue',marker = 'o')
plt.scatter(x1[:,3],x1[:,12],c='green',marker = 'o')
plt.scatter(x2[:,3],x2[:,12],c='red',marker = 'o')
plt.show()
1589632495803

【4】 利用KMeans算法對“iris.csv”數據集的無標籤數據分爲3類,用三維圖形可視化分類結果。

K-means可視化——三維散點圖

  • 代碼

    import matplotlib.pyplot as plt
    from sklearn.cluster import KMeans
    from mpl_toolkits.mplot3d import Axes3D
    import pandas as pd
    iris = pd.read_csv('iris.csv')
    
    x = iris.iloc[:, 1:5]
    # plt.scatter(x[:,0],x[:,:1],c='red',marker = 'o')
    
    estimator = KMeans(n_clusters = 3)
    estimator.fit(x)
    
    # 打標籤
    Label_pred = estimator.labels_
    
    x0 = x[Label_pred == 0]
    x1 = x[Label_pred == 1]
    x2 = x[Label_pred == 2]
    
    # 三維散點圖
    fig = plt.figure()
    ax = fig.gca(projection='3d')
    
    ax.scatter(x0.iloc[:,0],x0.iloc[:,2],x0.iloc[:, 3],c='blue',marker = 'o')
    ax.scatter(x1.iloc[:,0],x1.iloc[:,2],x1.iloc[:, 3],c='green',marker = 'o')
    ax.scatter(x2.iloc[:,0],x2.iloc[:,2],x2.iloc[:, 3],c='red',marker = 'o')
    ax.set_xlabel(iris.columns[1]+"(cm)")
    ax.set_ylabel(iris.columns[3]+"(cm)")
    ax.set_zlabel(iris.columns[4]+"(cm)")
    plt.show()
    
  • 得到三維散點圖

在這裏插入圖片描述

【5】 利用KMeans算法對“iris.csv”數據集的無標籤數據分爲3類,任取2個特徵值,顯示分類結果

K-means可視化——多個子圖

import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
from mpl_toolkits.mplot3d import Axes3D
import pandas as pd
import numpy as np
iris = pd.read_csv('iris.csv')

x = iris.iloc[:, 1:5]
# plt.scatter(x[:,0],x[:,:1],c='red',marker = 'o')

estimator = KMeans(n_clusters = 3)
estimator.fit(x)

# 打標籤
Label_pred = estimator.labels_

x0 = x[Label_pred == 0]
x1 = x[Label_pred == 1]
x2 = x[Label_pred == 2]

#劃分子圖
fig,axes=plt.subplots(4,4, sharex='col',figsize=(10, 10))
#plt.ylim(0,10)
for i in range(0,4):
    for j in range(0, 4):
        if i != j:
            axes[i, j].scatter(x0.iloc[:, j],x0.iloc[:, i],c='blue', marker='.')
            axes[i, j].scatter(x1.iloc[:, j], x1.iloc[:, i], c='red', marker='.')
            axes[i, j].scatter(x2.iloc[:, j],x2.iloc[:, i],c='green', marker='.')


    axes[i,i].hist(x.iloc[:,i], bins=20, facecolor="blue", edgecolor="black", alpha=0.7)
    
    axes[i, 0].set_ylabel(iris.columns[i+1]+"(cm)")
    axes[3, i].set_xlabel(iris.columns[i+1] + "(cm)")
#plt.tight_layout()

plt.show()

【6】你認爲KMeans算法和KNN算法的缺陷是什麼?針對這些缺點,通過查閱資料,瞭解到有什麼改進的方法?

KMeans算法的缺陷及改進辦法

(1) K-Means聚類算法需要用戶事先指定聚類的個數k值.在很多時候,在對數據集進行聚類的時候,用戶起初並不清楚數據集應該分爲多少類合適,對k值難以估計.

改進方法—— 安徽大學李芳的碩士論文中提到了k-Means算法的k值自適應優化方法 :可以通過在一開始給定一個適合的數值給k,通過一次K-means算法得到一次聚類中心。對於得到的聚類中心,根據得到的k個聚類的距離情況,合併距離最近的類,因此聚類中心數減小,當將其用於下次聚類時,相應的聚類數目也減小了,最終得到合適數目的聚類數。可以通過一個評判值E來確定聚類數得到一個合適的位置停下來,而不繼續合併聚類中心。重複上述循環,直至評判函數收斂爲止,最終得到較優聚類數的聚類結果。

*(2) **對初始聚類中心敏感,*選擇不同的聚類中心會產生不同的聚類結果和不同的準確率.隨機選取初始聚類中心的做法會導致算法的不穩定性,有可能陷入局部最優的情況.

改進方法—— K-Means++算法: 假設已經選取了n個初始聚類中心(0<n<k),則在選取第n+1個聚類中心時:距離當前n個聚類中心越遠的點會有更高的概率被選爲第n+1個聚類中心.在選取第一個聚類中心(n=1)時同樣通過隨機的方法.

(3) 對噪聲和孤立點數據敏感,K-Means算法將簇的質心看成聚類中心加入到下一輪計算當中,因此少量的該類數據都能夠對平均值產生極大影響,導致結果的不穩定甚至錯誤.

改進方法——離羣點檢測的LOF算法,通過去除離羣點後再聚類,可以減少離羣點和孤立點對於聚類效果的影響。

(4) 一旦集羣具有複雜的幾何形狀,kmeans就不能很好地對數據進行聚類。

主要原因在於選取距離度量的方法。因爲K-Means算法主要採用歐式距離函數度量數據對象之間的相似度,並且採用誤差平方和作爲準則函數,通常只能發現數據對象分佈較均勻的球狀簇.

改進方法:如果要處理不規則的數據,可以使用基於密度的DESCAN聚類算法。

  • 參考文獻

    《Kmeans聚類算法及其評價方法和改進方向的研究》 周銀平 李衛國 (102206 華北電力大學 北京)

    https://blog.csdn.net/u013129109/article/details/80063111

    https://blog.csdn.net/u010536377/article/details/50884416

KNN算法的缺陷及改進方法

(1)需要更精確的距離函數代替歐氏距離

  • 改進方法:
    • 消除不相關屬性:纏繞法、貪婪搜索、遺傳搜索
    • 屬性加權距離函數
    • 基於頻率的距離函數,稱之爲相異性度量
    • 標稱屬性的距離函數:值差分度量(VDM)

(2)搜索一個最優的近鄰大小代替k

  • 改進方法:
    • 交叉驗證法
    • DKNN即動態確定K值

(3)找出更精確的類別概率估計代替簡單的投票方法。

  • 改進方法:

    • 基於概率的局部分類模型,即結合NB算法

作業清單(5/20)

【1】 數據集如下圖所示,根據我們對決策樹的理解,設計一棵決策樹,並輸入{Age:36,Salary:H,STU:No,Credit:OK} 測試數據,是否與預期結果一致?@注意,不允許直接調用Sklearn提供的決策樹方法。

*Age* *Salary* *STU* *Credit* *Buy Computer*
<30 H No OK No
<30 H No Good No
30-40 H No OK Yes
>40 M No OK Yes
>40 L Yes OK Yes
>40 L Yes Good No
30-40 L Yes Good Yes
<30 M No OK No
<30 L Yes OK Yes
>40 M Yes OK Yes
<30 M Yes Good Yes
30-40 M No Good Yes
30-40 H Yes OK Yes
>40 M No Good No
  • 決策樹算法思想

    1. 使用信息增益選擇最佳屬性以拆分數據集。
    2. 使該屬性成爲決策節點,並將數據集分成較小的子集。
    3. 通過對每個子節點遞歸地重複此過程來開始樹的構建,直到其中一個條件匹配:
      • 所有元組都屬於相同的屬性值。
      • 沒有更多的剩餘屬性。
      • 沒有更多實例了。
  • 數據整理爲csv格式

    Age,Salary,STU,Credit,BuyComputer
    <30,H,No,OK,No
    <30,H,No,Good,No
    30-40,H,No,OK,Yes
    >40,M,No,OK,Yes
    >40,L,Yes,OK,Yes
    >40,L,Yes,Good,No
    30-40,L,Yes,Good,Yes
    <30,M,No,OK,No
    <30,L,Yes,OK,Yes
    >40,M,Yes,OK,Yes
    <30,M,Yes,Good,Yes
    30-40,M,No,Good,Yes
    30-40,H,Yes,OK,Yes
    >40,M,No,Good,No
    
  • 編碼實現

    from math import log
    import pandas as pd
    # 計算信息熵
    def Ent(dataset):
        n = len(dataset)
        label_counts = {}
        for item in dataset:
            label_current = item[-1]
            if label_current not in label_counts.keys():
                label_counts[label_current] = 0
            label_counts[label_current] += 1
        ent = 0.0
        for key in label_counts:
            prob = label_counts[key]/n
            ent -= prob * log(prob,2)
        return ent
    
    #按照權重計算各分支的信息熵
    def sum_weight(grouped,total_len):
        
        weight = len(grouped)/total_len
        #print(grouped.iloc[:,-1])
        return weight * Ent(grouped.iloc[:,-1])
    
    #根據公式計算信息增益
    def Gain(column, data):
        lenth = len(data)
        ent_sum = data.groupby(column).apply(lambda x:sum_weight(x,lenth)).sum() 
        ent_D = Ent(data.iloc[:,-1])
        return ent_D - ent_sum
    
    # 計算獲取最大的信息增益的feature,輸入data是一個dataframe,返回是一個字符串
    def get_max_gain(data):
        max_gain = 0
        cols = data.columns[:-1]
        for col in cols:
            gain = Gain(col,data)
            if gain > max_gain:
                max_gain = gain
                max_label = col
        return max_label
    #獲取data中最多的類別作爲節點分類,輸入一個series,返回一個索引值,爲字符串
    def get_most_label(label_list):
        return label_list.value_counts().idxmax()
    
    # 創建決策樹,傳入的是一個dataframe,最後一列爲label
    def TreeGenerate(data):
        feature = data.columns[:-1]
        label_list = data.iloc[:, -1]
        #如果樣本全屬於同一類別C,將此節點標記爲C類葉節點
        if len(pd.unique(label_list)) == 1:
            return label_list.values[0]
        #如果待劃分的屬性集A爲空,或者樣本在屬性A上取值相同,則把該節點作爲葉節點,並標記爲樣本數最多的分類
        elif len(feature)==0 or len(data.loc[:,feature].drop_duplicates())==1:
            return get_most_label(label_list)
        #從A中選擇最優劃分屬性
        best_attr = get_max_gain(data)
        tree = {best_attr: {}}
        #對於最優劃分屬性的每個屬性值,生成一個分支
        for attr,gb_data in data.groupby(by=best_attr):
            print(gb_data)
            if len(gb_data) == 0:
                tree[best_attr][attr] = get_most_label(label_list)
            else:
                #在data中去掉已劃分的屬性
                new_data = gb_data.drop(best_attr,axis=1)
                #遞歸構造決策樹
                tree[best_attr][attr] = TreeGenerate(new_data)
        return tree
    
    #使用遞歸函數進行分類
    def tree_predict(tree, data):
      feature = list(tree.keys())[0]
      label = data[feature]
      next_tree = tree[feature][label]
      if type(next_tree) == str:
        return next_tree
      else:
        return tree_predict(next_tree, data)
    
    data = pd.read_csv('computer.csv')
    
    #得到經過訓練後的決策樹
    mytree = TreeGenerate(data)
    print(mytree)
    test_data = {'Age':'30-40','Salary':'H','STU':'No','Credit':'OK'}
    predict = tree_predict(mytree,test_data)
    print(predict)
    
  • 實驗結果

    • 測試結果

      Yes

【2】數據集如下圖所示,計算這棵決策樹的類別信息熵,並計算基於每個特徵值的類別信息熵。根據公式計算每個特徵值的信息增益,畫出一棵決策樹(用簽字筆畫出即可)。
在這裏插入圖片描述

標籤/特徵值 類別特徵值 信息增益
是否給貸款 -6/15 * log(6/15,2)-9/15 * log(9/15,2)=0.971
年齡 1/3 *(-2/5 * log(2/5,2) - 3/5 * log(3/5,2))+ 1/3 * (-2/5 * log(2/5,2) - 3/5 * log(3/5,2)) + 1/3 * (-1/5 * log(1/5,2)-4/5 * log(4/5,2)) = 0.888 0.971-0.888=0.083
有工作 1/3 * 0 + 2/3 * (-4/10 * log(4/10,2)-6/10 * log(6/10,2)) = 0.647 0.971-0.647=0.324
有自己的房子 6/15 * 0 + 9/15 * (- 3/9 * log(3/9,2)-6/9 * log(6/9,2)) = 0.551 0.971-0.551=0.420
信貸情況 5/15 * (-1/5 * log(1/5,2)-4/5 * log(4/5,2)) + 6/15 * (-2/6 * log(2/6,2)-4/6 * log(4/6,2)) + 4/15 * 0 = 0.608 0.971-0.608=0.363

【3】 根據下述數據集,編寫代碼實現信息熵、條件熵、信息增益等決策樹的關鍵環節,並撰寫實驗報告。(最後一列是類別:是否提供貸款)

dataSet = [ [0, 0, 0, 0, ‘no’], #數據集

[0, 0, 0, 1, ‘no’],

[0, 1, 0, 1, ‘yes’],

​ [0, 1, 1, 0, ‘yes’],

​ [0, 0, 0, 0, ‘no’],

​ [1, 0, 0, 0, ‘no’],

​ [1, 0, 0, 1, ‘no’],

​ [1, 1, 1, 1, ‘yes’],

​ [1, 0, 1, 2, ‘yes’],

​ [1, 0, 1, 2, ‘yes’],

​ [2, 0, 1, 2, ‘yes’],

​ [2, 0, 1, 1, ‘yes’],

​ [2, 1, 0, 1, ‘yes’],

​ [2, 1, 0, 2, ‘yes’],

​ [2, 0, 0, 0, ‘no’]]

labels = [‘年齡’, ‘有工作’, ‘有自己的房子’, ‘信貸情況’]

實驗報告

  • 實驗中使用的第三方庫

    • math: 進行對數計算
    • pandas: 對數據進行整理劃分
    from math import log
    import pandas as pd
    
  • 計算信息熵

    • 公式
      Ihfo(D)=i=1mpilog2pi Ihfo(D)=-\sum^{m}_{i=1}p_ilog_2p_i
    def Ent(dataset):
        n = len(dataset)
        label_counts = {}
        for item in dataset:
            label_current = item
            if label_current not in label_counts.keys():
                label_counts[label_current] = 0
            label_counts[label_current] += 1
        ent = 0.0
        for key in label_counts:
            prob = label_counts[key]/n
            ent -= prob * log(prob,2)
        return ent
    
  • 計算條件熵

    • 公式:
      InfoA(D)=j=1v[(DjD)×Info(Dj)] Info_A(D)=\sum^{v}_{j=1}[(\frac{|D_j|}{|D|})\times{Info(D_j)}]

      #按照權重計算各分支的信息熵
      def sum_weight(grouped,total_len):
          
          weight = len(grouped)/total_len
          #print(grouped.iloc[:,-1])
          return weight * Ent(grouped.iloc[:,-1])
      
    • 計算條件熵時利用DataFrame.groupby,apply 進行分組

      # 計算條件熵
      ent_sum = data.groupby(column).apply(lambda x:sum_weight(x,lenth)).sum()
      
  • 計算信息增益

    • 公式:
      Grain(D)=Info(D)InfoA(D) Grain(D) = Info(D)-Info_A(D)

      #根據公式計算信息增益
      def Gain(column, data):
          lenth = len(data)
          ent_sum = data.groupby(column).apply(lambda x:sum_weight(x,lenth)).sum() 
          ent_D = Ent(data.iloc[:,-1])
          print(column,ent_D-ent_sum)
          return ent_D - ent_sum
      
  • 遞歸生成決策樹

    1. 使用信息增益選擇最佳屬性以拆分數據集。
    2. 使該屬性成爲決策節點,並將數據集分成較小的子集。
    3. 通過對每個子節點遞歸地重複此過程來開始樹的構建,直到其中一個條件匹配:
      • 所有元組都屬於相同的屬性值。
      • 沒有更多的剩餘屬性。
      • 沒有更多實例了
    # 計算獲取最大的信息增益的feature,輸入data是一個dataframe,返回是一個字符串
    def get_max_gain(data):
        max_gain = 0
        cols = data.columns[:-1]
        for col in cols:
            gain = Gain(col,data)
            if gain > max_gain:
                max_gain = gain
                max_label = col
        return max_label
    #獲取data中最多的類別作爲節點分類,輸入一個series,返回一個索引值,爲字符串
    def get_most_label(label_list):
        return label_list.value_counts().idxmax()
    
    # 創建決策樹,傳入的是一個dataframe,最後一列爲label
    def TreeGenerate(data):
        feature = data.columns[:-1]
        label_list = data.iloc[:, -1]
        #如果樣本全屬於同一類別C,將此節點標記爲C類葉節點
        if len(pd.unique(label_list)) == 1:
            return label_list.values[0]
        #如果待劃分的屬性集A爲空,或者樣本在屬性A上取值相同,則把該節點作爲葉節點,並標記爲樣本數最多的分類
        elif len(feature)==0 or len(data.loc[:,feature].drop_duplicates())==1:
            return get_most_label(label_list)
        #從A中選擇最優劃分屬性
        best_attr = get_max_gain(data)
        tree = {best_attr: {}}
        #對於最優劃分屬性的每個屬性值,生成一個分支
        for attr,gb_data in data.groupby(by=best_attr):
            #print(gb_data)
            if len(gb_data) == 0:
                tree[best_attr][attr] = get_most_label(label_list)
            else:
                #在data中去掉已劃分的屬性
                new_data = gb_data.drop(best_attr,axis=1)
                #遞歸構造決策樹
                tree[best_attr][attr] = TreeGenerate(new_data)
        return tree
    
  • 輸入數據,生成決策樹

    dataSet=[[0,0,0,0,'no'],[0,0,0,1,'no'],[0,1,0,1,'yes'],[0,1,1,0,'yes'],[0,0,0,0,'no'],
    [1,0,0,0,'no'],[1,0,0,1,'no'],[1,1,1,1,'yes'],[1,0,1,2,'yes'],[1,0,1,2,'yes'],
    [2,0,1,2,'yes'],[2,0,1,1,'yes'],[2,1,0,1,'yes'],[2,1,0,2,'yes'],[2,0,0,0,'no']]
    
    labels = ['年齡', '有工作', '有自己的房子', '信貸情況','是否提供貸款']  
    
    data = pd.DataFrame(dataSet,columns=labels)
    tree = TreeGenerate(data)
    print(tree)
    
    • 結果以字典形式輸出

      {'有自己的房子': {0: {'有工作': {0: 'no', 1: 'yes'}}, 1: 'yes'}}
      

【1】 對問題【3】中實現的決策樹,實現可視化。(選做)

import matplotlib.pyplot as plt
#爲了matplotlib中文正常顯示,指定字體爲SimHei
plt.rcParams['font.sans-serif']=['SimHei'] 
plt.rcParams['font.family']='sans-serif'

# 獲取樹的葉子節點數目
def get_num_leafs(decision_tree):
    num_leafs = 0
    first_str = next(iter(decision_tree))
    second_dict = decision_tree[first_str]
    for k in second_dict.keys():
        if isinstance(second_dict[k], dict):
            num_leafs += get_num_leafs(second_dict[k])
        else:
            num_leafs += 1
    return num_leafs

# 獲取樹的深度
def get_tree_depth(decision_tree):
    max_depth = 0
    first_str = next(iter(decision_tree))
    second_dict = decision_tree[first_str]
    for k in second_dict.keys():
        if isinstance(second_dict[k], dict):
            this_depth = 1 + get_tree_depth(second_dict[k])
        else:
            this_depth = 1
        if this_depth > max_depth:
            max_depth = this_depth
    return max_depth

# 繪製節點
def plot_node(node_txt, center_pt, parent_pt, node_type):
    arrow_args = dict(arrowstyle='<-')
    create_plot.ax1.annotate(node_txt, xy=parent_pt,  xycoords='axes fraction', xytext=center_pt, textcoords='axes fraction', va="center", ha="center", bbox=node_type,arrowprops=arrow_args)

# 標註劃分屬性
def plot_mid_text(cntr_pt, parent_pt, txt_str):
    x_mid = (parent_pt[0] - cntr_pt[0]) / 2.0 + cntr_pt[0]
    y_mid = (parent_pt[1] - cntr_pt[1]) / 2.0 + cntr_pt[1]
    create_plot.ax1.text(x_mid, y_mid, txt_str, va="center", ha="center", color='red')

# 繪製決策樹
def plot_tree(decision_tree, parent_pt, node_txt):
    d_node = dict(boxstyle="sawtooth", fc="0.8")
    leaf_node = dict(boxstyle="round4", fc='0.8')
    num_leafs = get_num_leafs(decision_tree)
    first_str = next(iter(decision_tree))
    cntr_pt = (plot_tree.xoff + (1.0 +float(num_leafs))/2.0/plot_tree.totalW, plot_tree.yoff)
    plot_mid_text(cntr_pt, parent_pt, node_txt)
    plot_node(first_str, cntr_pt, parent_pt, d_node)
    second_dict = decision_tree[first_str]
    plot_tree.yoff = plot_tree.yoff - 1.0/plot_tree.totalD
    for k in second_dict.keys():
        if isinstance(second_dict[k], dict):
            plot_tree(second_dict[k], cntr_pt, k)
        else:
            plot_tree.xoff = plot_tree.xoff + 1.0/plot_tree.totalW
            plot_node(second_dict[k], (plot_tree.xoff, plot_tree.yoff), cntr_pt, leaf_node)
            plot_mid_text((plot_tree.xoff, plot_tree.yoff), cntr_pt, k)
    plot_tree.yoff = plot_tree.yoff + 1.0/plot_tree.totalD

def create_plot(dtree):
    fig = plt.figure(1, facecolor='white')
    fig.clf()
    axprops = dict(xticks=[], yticks=[])
    create_plot.ax1 = plt.subplot(111, frameon=False, **axprops)
    plot_tree.totalW = float(get_num_leafs(dtree))
    plot_tree.totalD = float(get_tree_depth(dtree))
    plot_tree.xoff = -0.5/plot_tree.totalW
    plot_tree.yoff = 1.0
    plot_tree(dtree, (0.5, 1.0), '')
    plt.show()

tree = {'有自己的房子': {0: {'有工作': {0: 'no', 1: 'yes'}}, 1: 'yes'}}

create_plot(tree)
  • 參考資料:https://zhuanlan.zhihu.com/p/43819989

作業清單(5/27)

【1】在下列事務數據集中

TID 項集
1 {麪包,牛奶}
2 {麪包,尿布,啤酒,雞蛋}
3 {牛奶,尿布,啤酒,可樂}
4 {麪包,牛奶,尿布,啤酒}
5 {麪包,牛奶,尿布,可樂}
  • 項集{啤酒,尿布,牛奶}的支持度爲 2/5=40%

  • 如果將最小支持度定爲3,則數據集中的頻繁項集有

    L1

頻繁項集 支持度
{麪包} 4
{啤酒} 3
{牛奶} 4
{尿布} 4

​ L2

頻繁項集 支持度
{麪包,牛奶} 3
{麪包,尿布} 3
{啤酒,尿布} 3
{牛奶,尿布} 3
  • 規則{牛奶,尿布}→{啤酒}的支持度爲 2/5=40% ,置信度爲 2/3=66.7%

【2】閱讀微信羣發佈的“關聯規則例子.py”,並根據交易單爲(T1,T2,T3,T4,T5,T6,T7,T8,T9),每筆交易的貨物清單爲{{I1,I2,I5},{I2,I4},{I2,I3},{I1,I2,I4},{I1,I3},{I2,I3},{I1,I3},{I1,I2,I3,I5},{I1,I2,I3}},編寫代碼得到關聯規則。

A strong rule is a rule that is frequent and its confidence is higher than Minimum confidence Φ

  • 模型搭建

    • 模型初始化及參數設置(最小支持度=0.1,最小置信度=0.5)
    def __init__(self,minSupport=0.1,minConfidence=0.5):
    		'''
    		minSuport:最小支持度
    		minConfidence:最小置信度
    		dataset:數據集
    		count:存放frequent itemsets 以及 support
    		associationRules:滿足minConfidence的關聯規則
    		num:元素數量
    		threshold = num*minSupport:由num和minSupport算出的閾值
    		'''
    		self.minSupport = minSupport
    		self.minConfidence = minConfidence
    		self.dataset = None
    		self.count = None
    		self.associationRules = None
    		self.num = 0
    		self.threshold = 0
    
    • 生成頻繁項集

      思路:

      1. 先遍歷數據集,統計元素數量num,得到候選集C1;

      2. 由num*minSupport得到閾值threhold;

      3. 去掉支持度小於threhold的項集,得到size=1的頻繁項集L1;

      4. 將上一步得到的頻繁項集兩兩合併,得到新的候選集;

      5. 去掉支持度小於threhold的項集,得到新的頻繁項集;

      6. 循環4~5,直至找到所有頻繁項集.

      修改微信羣發佈的“關聯規則例子.py”:

      1.  原例子中對元素數量的計算有誤,作業中進行了修正
      2.  修改微信羣發佈的“關聯規則例子.py”,避免項集集合化數據項不會被拆分
      
      • 原例子

        tmp = set(list(element[i]))
        tmp.update(list(element[j]))
        

        [‘I1’]和[‘I2’]合併爲[‘I’,‘1’,‘2’]

      • 修改爲

        tmp = set([element[i],element[j]])
        

        [‘I1’]和[‘I2’]合併爲[‘I1’,‘I2’]

      class Association_rules:
          #計算frequent itemset
      	def countItem(self,upDict,elength):
      		currentDict = {}
      		element = list(upDict.keys())
      		for i in range(len(element)-1):
      			for j in range(i+1,len(element)):
      				#print(element[i])
      				tmp = set([element[i],element[j]])
      				#tmp.update(list(element[j]))
      				#print(tmp)
      
      				if len(tmp) > elength:
      					continue
      				if tmp in list(set(item) for item in currentDict.keys()):
      					continue
      
      				
      				for item in self.dataset:
      					if tmp.issubset(set(item)):
      						if tmp in list(set(item) for item in currentDict.keys()):
      							currentDict[tuple(tmp)] += 1
      						else:
      							currentDict[tuple(tmp)] = 1
      
      		for item in list(currentDict.keys()):
      			if currentDict[item] < self.threshold:
      				del currentDict[item]
      		if len(list(currentDict.keys())) < 1:
      			return None
      		else:
      			return currentDict
       
      	#生成frequent itemsets
      	def fit(self,dataset):
      		self.dataset = dataset
      		count = []
      		count.append({})
      		for item in self.dataset:
      			for i in range(len(item)):
      				if item[i] in list(count[0].keys()):
      					count[0][item[i]] += 1
      				else:
      					count[0][item[i]] = 1
      				self.num += 1
       
      		self.threshold = self.num * self.minSupport
      		print(self.num, self.threshold)
      		for item in list(count[0].keys()):
      			if count[0][item] < self.threshold:
      				del count[0][item]
      		
      			
      		i = 0
      		while(True):
      			if len(count[i]) < 2:
      				break
      			else:
      				tmp = self.countItem(count[i],i+2)
      				if tmp == None:
      					break
      				else:
      					count.append(tmp)
      				i += 1
      
      
      		self.count = count
       
      	#打印並返回frequent itemsets
      	def frequentItemsets(self):
      		#print('threshold:',self.threshold)
      		for item in self.count:
      			print(item)
      			print()
      		return self.count
      
    • 計算置信度

      公式:
      Confidence(X=>Y)=XYXItems Confidence (X => Y) = \frac{|{X,Y}|} {|X-Items|}\quad

      class Association_rules:
          #計算置信度。set = (X),set2 = (X^Y)
      	def countConfidence(self,set1,set2):
      		len1 = len(set1)
      		len2 = len(set2)
      		#去除元素位置干擾。例如:set2 = ('a','b'),而self.count中存儲爲('b','a')
      		if not tuple(set2) in self.count[len2-1].keys():
      			set2[0],set[1] = set2[1],set2[0]
      		#寫代碼的時候出現的疏忽,當元素只有一個時count中存儲格式是str,而元素多於一個時格式是tuple
      		if len1 == 1:
      			return self.count[len2-1][tuple(set2)] / self.count[len1-1][set1[0]]
      		else:
      			if not tuple(set1) in self.count[len1-1].keys():
      				set1[0],set1[1] = set1[1],set1[0]
      			return self.count[len2-1][tuple(set2)] / self.count[len1-1][tuple(set1)] 
      
    • 二進制求子集

      算法思想:

      ​ 例如求4個元素 3 2 1 0 的子集,那麼用二進制的0,1代表每一位是否選中。
      十進制 二進制
      0 0000 代表空集
      1 0001 代表{0}
      2 0010 代表{1}
      3 0011 代表{0,1}
      4 0100 代表{2}
      ​ …
      15 1110 代表{3,2,1}
      16 1111 代表{3,2,1,0}

      	#二進制法求每個itemset的所有子集
      	def subsets(self,itemset):
      		N = len(itemset)
      		subsets = []
      		for i in range(1,2**N-1):
      			tmp = []
      			for j in range(N):
      				if (i >> j) % 2 == 1:
      					tmp.append(itemset[j])
      			subsets.append(tmp)
      		return subsets
      
    • 生成關聯規則

      • 判斷頻繁項集中的每個規則的置信度是否大於最小置信度
      	def associationRule(self):
      		associationRules = []
      		for i in range(1,len(self.count)):
      			for itemset in list(self.count[i].keys()):
      				#用字典存每個itemset的關聯規則
      				tmp = {}
      				#print(itemset)
      				subset = self.subsets(itemset)
      				#print(subset)
      				for i in range(len(subset)-1):
      					for j in range(i+1,len(subset)):
      						#判斷subset[i]與subset[j]完整組成一個itemset,而且沒有相同的元素
      						if len(subset[i]) + len(subset[j]) == len(itemset) and len(set(subset[i]) & set(subset[j])) == 0:
      							confidence = self.countConfidence(subset[i],itemset)
      							#print(subset[i],' > ',subset[j],' ',confidence)
      							if confidence > self.minConfidence:
      								#生成相應鍵值對
      								tmpstr = str(subset[i]) + ' > ' + str(subset[j])
      								tmp[tmpstr] = confidence
      							#將subset[i]與subset[j]反過來生成另外一個規則
      							confidence = self.countConfidence(subset[j],itemset)
      							#print(subset[j],' > ',subset[i],' ',confidence)
      							if confidence > self.minConfidence:
      								tmpstr = str(subset[j]) + ' > ' + str(subset[i])
      								tmp[tmpstr] = confidence
      				if tmp.keys():
      					associationRules.append(tmp)
      		for item in associationRules:
      			print(item)
      		return associationRules
      
    1. 調用模型,得出結果

      if __name__ == '__main__':
      	num = 10
      	#dataset = set_data(num)
      	dataset = [['I1','I2','I5'],['I2','I4'],['I2','I3'],
      	['I1','I2','I4'],['I1','I3'],['I2','I3'],['I1','I3'],['I1','I2','I3','I5'],['I1','I2','I3']]
      	for item in dataset:
      		print(item)
      
      	ar = Association_rules()
      	ar.fit(dataset)
      	freItemsets = ar.frequentItemsets()
      	associationRules = ar.associationRule()
      

      得到關聯規則

      關聯規則 支持度 置信度
      {I1} --> {I2} 4 0.67
      {I2} --> {I1} 4 0.57
      {I1} --> {I3} 4 0.67
      {I3} --> {I1} 4 0.67
      {I3} --> {I2} 4 0.67
      {I2} --> {I3} 4 0.57

​ 例如求4個元素 3 2 1 0 的子集,那麼用二進制的0,1代表每一位是否選中。
十進制 二進制
0 0000 代表空集
1 0001 代表{0}
2 0010 代表{1}
3 0011 代表{0,1}
4 0100 代表{2}
​ …
15 1110 代表{3,2,1}
16 1111 代表{3,2,1,0}

```python
	#二進制法求每個itemset的所有子集
	def subsets(self,itemset):
		N = len(itemset)
		subsets = []
		for i in range(1,2**N-1):
			tmp = []
			for j in range(N):
				if (i >> j) % 2 == 1:
					tmp.append(itemset[j])
			subsets.append(tmp)
		return subsets
```
  • 生成關聯規則

    • 判斷頻繁項集中的每個規則的置信度是否大於最小置信度
    	def associationRule(self):
    		associationRules = []
    		for i in range(1,len(self.count)):
    			for itemset in list(self.count[i].keys()):
    				#用字典存每個itemset的關聯規則
    				tmp = {}
    				#print(itemset)
    				subset = self.subsets(itemset)
    				#print(subset)
    				for i in range(len(subset)-1):
    					for j in range(i+1,len(subset)):
    						#判斷subset[i]與subset[j]完整組成一個itemset,而且沒有相同的元素
    						if len(subset[i]) + len(subset[j]) == len(itemset) and len(set(subset[i]) & set(subset[j])) == 0:
    							confidence = self.countConfidence(subset[i],itemset)
    							#print(subset[i],' > ',subset[j],' ',confidence)
    							if confidence > self.minConfidence:
    								#生成相應鍵值對
    								tmpstr = str(subset[i]) + ' > ' + str(subset[j])
    								tmp[tmpstr] = confidence
    							#將subset[i]與subset[j]反過來生成另外一個規則
    							confidence = self.countConfidence(subset[j],itemset)
    							#print(subset[j],' > ',subset[i],' ',confidence)
    							if confidence > self.minConfidence:
    								tmpstr = str(subset[j]) + ' > ' + str(subset[i])
    								tmp[tmpstr] = confidence
    				if tmp.keys():
    					associationRules.append(tmp)
    		for item in associationRules:
    			print(item)
    		return associationRules
    
  1. 調用模型,得出結果

    if __name__ == '__main__':
    	num = 10
    	#dataset = set_data(num)
    	dataset = [['I1','I2','I5'],['I2','I4'],['I2','I3'],
    	['I1','I2','I4'],['I1','I3'],['I2','I3'],['I1','I3'],['I1','I2','I3','I5'],['I1','I2','I3']]
    	for item in dataset:
    		print(item)
    
    	ar = Association_rules()
    	ar.fit(dataset)
    	freItemsets = ar.frequentItemsets()
    	associationRules = ar.associationRule()
    

    得到關聯規則

    關聯規則 支持度 置信度
    {I1} --> {I2} 4 0.67
    {I2} --> {I1} 4 0.57
    {I1} --> {I3} 4 0.67
    {I3} --> {I1} 4 0.67
    {I3} --> {I2} 4 0.67
    {I2} --> {I3} 4 0.57
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章