1. 程式人生 > >python-層次聚類演算法例項-僅針對連續性變數

python-層次聚類演算法例項-僅針對連續性變數

根據機器學習中“層次聚類演算法”的原理,我用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)


'''
函式說明:返回兩個“聚類簇”間的距離。可以採用“平均距離”,也就是計算兩個簇間每兩個樣本的距離,最後求均值;最小距離;最大距離
引數說明:
data -> 第一列是id列,並且這個dataframe變數的索引就是那個id列,其餘所有的列是 特徵列(這個演算法只支援連續變數。但是層次聚類是支援連續、分類)
cluster_1、cluster_2 -> list變數,裡面每一個元素是一個樣本的索引
'''
def distance_cluster_ave(data, cluster_1, cluster_2):
    '''
    平均距離
    '''
    dis = 0.0
    for i in cluster_1:
        for j in cluster_2:
            dis += math.sqrt((data.ix[i, 1] - data.ix[j, 1]) ** 2 + (data.ix[i, 2] -data.ix[j, 2]) ** 2)
    return float(dis)/(i*j)

    '''
    最小聚類距離 判定方法
    '''
    # #
    # i = cluster_1[0]
    # j = cluster_2[0]
    # dis = math.sqrt((data.ix[i, 1] - data.ix[j, 1]) ** 2 + (data.ix[i, 2] - data.ix[j, 2]) ** 2)
    # for i in cluster_1:
    #     for j in cluster_2:
    #         if( math.sqrt((data.ix[i, 1] - data.ix[j, 1]) ** 2 + (data.ix[i, 2] - data.ix[j, 2]) ** 2) < dis):
    #             dis = math.sqrt((data.ix[i, 1] - data.ix[j, 1]) ** 2 + (data.ix[i, 2] - data.ix[j, 2]) ** 2)
    # return float(dis) / (i * j)

    '''
    最大聚類距離 判定方法
    '''
    # #
    # i = cluster_1[0]
    # j = cluster_2[0]
    # dis = math.sqrt((data.ix[i, 1] - data.ix[j, 1]) ** 2 + (data.ix[i, 2] - data.ix[j, 2]) ** 2)
    # for i in cluster_1:
    #     for j in cluster_2:
    #         if( math.sqrt((data.ix[i, 1] - data.ix[j, 1]) ** 2 + (data.ix[i, 2] - data.ix[j, 2]) ** 2) > dis):
    #             dis = math.sqrt((data.ix[i, 1] - data.ix[j, 1]) ** 2 + (data.ix[i, 2] - data.ix[j, 2]) ** 2)
    # return float(dis) / (i * j)

'''
函式說明:將原始資料進行層次聚類
引數說明: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_euclidean_distance.txt',header=None)
    data.columns = ['id','midu','hantang']
    data.index = list(data['id'])
    # 進行聚類(在一次層次聚類的時候,只可以支援一種 變數型別),下面這個函式是支援 連續性變數的,計算距離的時候,是計算 樣本簇間的“歐式距離”
    arrange(data)