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()

猜你喜欢

转载自blog.csdn.net/runs_after_the_wind/article/details/82889215