1. 程式人生 > >K-means演算法(將表中的點分為n類)

K-means演算法(將表中的點分為n類)

客戶分類:
1、將客戶分為三類:超級VIP、vip、普通使用者
2、需要你將不同的類的資料,在圖上顯示出來,用不同的顏色
3、返回三個類中,各包含哪些點

資料在下面的的表中,因為無法上傳表格所以截了個圖!

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import random


def decimal_clean(arr):
    '''
    將輸入序列進行小數定標標準化
    :param arr:輸入的待優化的序列
    :return:標準化後的序列
    '''
    k = np.ceil(np.log10(np.max(np.abs(arr))))
    return arr / 10 ** k


def iter_self(xy_line, n_type):
    '''
    自我迭代函式, 自我生成n_type個隨機參考點進行迴圈迭代, 每次迭代的最後利用本次分割槽結果求新的參考點
    迭代退出條件, 新的參考點跟上一次的參考點位置一致
    :param xy_line: 輸入的x_y序列
    :param n_type: 分類數目
    :return: 劃分完成的區域
    '''
    # 進入函式, 初始化舊參考點位置序列與新參考點位置序列
    old_pos = [[random.random(), random.random()] for i in range(n_type)]
    new_pos = [[] for i in range(n_type)]
    # 進入迴圈迭代
    while True:
        # 每一次的迴圈都重新整理一次分割槽列表
        area = [[] for i in range(n_type)]
        # 對每一個點遍歷
        for pos in range(len(xy_line)):
            # 獲得這個點的座標
            this_pos = [xy_line.iloc[pos, 0], xy_line.iloc[pos, 1]]
            # 建立空列表,用於存放該點與各個參考點的歐式距離
            distance = []
            # 針對每一個點的座標對所有中心點求歐式距離
            for i in range(n_type):
                distance.append(Oup_distance(this_pos, old_pos[i]))
            # 找到所有距離中最小點的索引, 即該點屬於第幾區
            min_index = distance.index(min(distance))
            # 根據該點的區號, 進行分割槽
            area[min_index].append(this_pos)
        # 判斷是否有空區域, 如果有, 重新呼叫本函式, 得到返回結果之後直接break
        if [] in area:
            area = iter_self(xy_line, n_type)
            break
        # 來到此步, 證明各個分割槽都是有值的, 用均值法求出各區對應的新的參考點座標
        new_pos = reflush_pos(area, n_type, new_pos)
        # 判斷新參考點與就參考點是否一致, 一致即可退出迴圈
        if new_pos == old_pos:
            break
        else:  # 否則將新的點序列深拷貝給舊的點序列, 新的點序列置空
            old_pos = new_pos.copy()
            new_pos = [[] for i in range(n_type)]
    return area


def reflush_pos(area, n_type, pos):
    '''
    重新整理參考點位置
    :param area: 分割槽序列
    :param n_type: 種類數目
    :param pos: 用於存放新參考點的序列
    :return: 新的參考點序列
    '''
    # 對area進行遍歷
    for i in range(n_type):
        # 將area[i]深拷貝賦值給this, 在進行numpy陣列轉換
        this = area[i].copy()
        # 如果不進行深拷貝, 此步會將整個area轉換成numpy陣列
        this_area = np.array(this)
        # 求x序列和y序列的均值, 並賦值給pos
        x_mean = np.mean(this_area[:, 0])
        y_mean = np.mean(this_area[:, 1])
        pos[i] = [x_mean, y_mean].copy()
    return pos


def Oup_distance(pos1, pos2):
    '''
    計算兩點之間的歐式距離
    :param pos1:點1的(x, y)座標
    :param pos2:點2的(x, y)座標
    :return:兩點之間的歐式距離值
    '''
    Oup = np.sqrt((pos1[1] - pos2[1]) ** 2 + (pos1[0] - pos2[0]) ** 2)
    return Oup


company = pd.read_csv(r"C:\Users\Administrator\Desktop\company.csv", encoding="gbk")
# 這裡用的是iloc的提取方法, 也可以根據個人習慣使用其他提取方法
# 此處特意將時間和消費金額兩列作為x和y使用concat函式合併到一個dataframe, 方便後面的處理
x_y = pd.concat([decimal_clean(company.iloc[:, 2]), decimal_clean(company.iloc[:, 1])], axis=1, join="outer")

# 得到三維的列表AREA
AREA = iter_self(x_y, 3)

# 畫圖
plt.figure()
for area in AREA:
    # area是二維列表,將其變為二維陣列
    area_np = np.array(area)
    x = area_np[:, 0]
    y = area_np[:, 1]
    # 將x,y聯絡起來
    plt.scatter(x, y)
# 顯示畫圖
plt.show()