python-層次聚類-離散變數
阿新 • • 發佈:2019-02-16
根據機器學習中“層次聚類演算法”的原理,我用python程式碼實現了一個具體的例子。層次聚類演算法只能單獨處理“連續變數”或者“離散變數”,這個例項中實現了僅針對“離散變數”的例子。
需要在聚類前進行最大最小歸一化、正態歸一化。
資料、下載程式碼可以訪問我的github : https://github.com/1066196847/clustering 點選開啟連結
# coding=utf-8 import csv import os import pickle import cPickle import math from math import ceil import matplotlib.pyplot as plt import csv from tqdm import tqdm import numpy as np import pandas as pd from pandas import Series from pandas import DataFrame import operator import random ''' 函式說明:傳入一個矩陣,找到最小的一個數字,返回對應的 ij索引 ''' def find_min_ij(matrix): min_num = matrix[1][2] min_i = 0 min_j = 0 for i in range(0,matrix.shape[0]): for j in range(matrix.shape[1]): if(i != j): if(matrix[i][j]<min_num): min_i = i min_j = j min_num = matrix[i][j] return min(min_i,min_j),max(min_i,min_j) ''' 函式說明:返回兩個“聚類簇”間的距離。這個僅僅針對的是離散距離,所以採用VDM距離來衡量 兩個樣本簇間的距離 引數說明: data -> 第一列是id列,並且這個dataframe變數的索引就是那個id列,其餘所有的列是 特徵列(這個演算法只支援“離散”變數。但是層次聚類是支援連續、分類) cluster_1、cluster_2 -> list變數,表示兩個樣本簇,裡面每一個元素是一個樣本的索引 ''' def distance_cluster_ave(data, cluster_1, cluster_2): ''' 計算兩個樣本簇間 一一對應的兩個樣本 間的 VDM距離,最後取均值返回 ''' dis = 0.0 for i in cluster_1: for j in cluster_2: # VDM距離計算的時候,是一個屬性一個屬性的計算,所以下面需要用過for迴圈,每一次計算一個屬性的 VDM距離 for k in range(1,len(data.columns)): # i j 是一個樣本的“索引號”,k 是一個 屬性(或者說是特徵) 所在的列號(我需要這個列號,使用ix採取到 資料中的某個值) # 找出來這個屬性中 i j兩個樣本取值。在總體資料中 各具有 幾個? i_quzhi = data.ix[i, k] j_quzhi = data.ix[j, k] i_quzhi_num = len(data[data[k] == i_quzhi]) j_quzhi_num = len(data[data[k] == j_quzhi]) dis += (float(1)/i_quzhi_num)**2 + (float(1)/j_quzhi_num)**2 return float(dis)/(i*j*k) ''' 函式說明:將原始資料進行層次聚類 引數說明:data->第一列是id列,並且這個dataframe變數的索引就是那個id列,其餘所有的列是 特徵列(這個演算法只支援連續變數。但是層次聚類是支援連續、分類) ''' def arrange(data): # 初始化 len(data) 個聚類簇,每一個簇裡面放一個樣本的“索引”。每一個簇是一個“list”變數,所有的簇放在一個“大list變數”。之後會不斷更新 # cluster_all的索引是從0開始的,But 資料樣本的索引是從1開始的 cluster_all = range(1, len(data)+1) for i in range(0, len(cluster_all)): cluster_all[i] = [cluster_all[i]] # 首先初始化“距離矩陣”。m是原始資料的“樣本數”,我們現在要做出來一個矩陣,矩陣對應的 i j處 是 第i個聚類簇、第j個聚類簇 間的距離 m = data.shape[0] # 下面一行程式碼中,必須要是0.0,否則python會預設自動型別轉換 matrix = DataFrame([[0.0] * m for i in range(m)]) for i in range(m): for j in range(m): matrix[i][j] = distance_cluster_ave(data, cluster_all[i], cluster_all[j]) matrix[j][i] = matrix[i][j] # 設定當前聚類簇個數:q=n q = m # 假設我們要求得的聚類 不多於 k 個 k = 7 while(q > k): print(q) # 找出來距離最近的兩個聚類簇 cluster_all[min_i] cluster_all[min_j] (min_i<min_j)。min_i min_j是“matrix”中的索引,也是“cluster_all”中的索引(從0開始的) min_i,min_j = find_min_ij(matrix) print 'min_i = ',min_i,'min_j = ',min_j # 合併 min_i min_j 兩個聚類,將 min_j 這個聚類中的所有樣本 放到 min_i中,又因為 min_j>min_i,所以將min_j之後的所有 聚類簇 的索引都在 cluster_all 中前進一位 cluster_all[min_i] += cluster_all[min_j] for j in range(min_j,len(cluster_all)-1): cluster_all[j] = cluster_all[j+1] # 刪除掉 cluster_all 最後一個索引處的資料 del cluster_all[len(cluster_all)-1] ''' 到此時為止,cluster_all 中 還有 q-1 個聚類簇(包括那個添加了其他簇的 新簇) ''' # 刪除距離矩陣 中 列名為 del matrix[min_j] # 刪除列 cols = list(matrix.index)# 刪除行 cols.remove(min_j) matrix = matrix.loc[cols] # 重置matrix的列名、索引 matrix.columns = range(0,len(matrix.columns)) matrix.index = range(0,len(matrix)) # 更新距離矩陣。在這更新矩陣的時候,並不需要進行完全更新,只需要更新和 min_i 簇 有關的距離 for j in range(0, q-1): matrix[min_i][j] = distance_cluster_ave(data, cluster_all[min_i], cluster_all[j]) matrix[j][min_i] = matrix[min_i][j] q -= 1 # 每次處理完都列印下 cluster_all 中的資料 for i in cluster_all: print i if __name__ == "__main__": # 載入資料 data = pd.read_csv('arrangement_vdm.txt') data.columns = list(range(0,len(data.columns))) data.index = list(data[0]) # 進行聚類(在一次層次聚類的時候,只可以支援一種 變數型別),下面這個函式是支援 離散變數的,計算距離的時候,是計算 樣本簇間的“vdm距離” arrange(data)