美文网首页
Python-平均分组算法

Python-平均分组算法

作者: 蜡笔不好吃 | 来源:发表于2019-03-02 19:50 被阅读0次

这几天老大给了一个任务,比较有意思,也比较头疼。

Q:
现在有供应商的退货件数、送货件数、退货单数、送货单数(4个维度)。将供应商分组(k组),要求各组的个数差不多,且各组4个维度的平均值差不多。


开始想了一个算法,是根据之前做的分类算法——K-Means算法——改进的。

from _vector import squared_distance, vector_mean
import pandas as pd

class ZMeans:
    def __init__(self, k):
        self.k=k
        self.means = None

    def train(self,inputs):
        # 初始点 全部设为均值
        self.means=[ [inputs['退货件数'].mean(),inputs['送货件数'].mean()] for _ in range(self.k) ]
        # 标记 全部设为7先
        assignments = [7 for _ in range(len(inputs))]
        # 简陋的设定循环次数
        for x in range(1):
            for i in range(len(inputs)):
                assignments[i] = max(range(self.k),key=lambda j:squared_distance([inputs.ix[i,1],inputs.ix[i,2]],self.means[j]))
                # 调整均值
                for z in range(self.k):
                    i_point = [ [item1[1],item1[2]] for (idx1,item1),a in zip(inputs.iterrows() ,assignments) if a==z]
                    if i_point:
                        self.means[z]=vector_mean(i_point)
                inputs['组别'] = assignments
        print(inputs)
        save_path =r'  '
        write = pd.ExcelWriter(save_path)
        data.to_excel(write,sheet_name='Sheet1',header=True,index=False)
        write.save()


if __name__ == '__main__':
    # 读数据 数据已经按送货降序降序了
    path = r' '
    data = pd.read_excel(path, sheet_name='Sheet1', header=0)
    # 缺失值补0
    data = data.fillna(0)
    data['退货件数'] = data['退货件数'].astype('float')
    data['送货件数'] = data['送货件数'].astype('float')
    # 分类 将dataframe传入
    cluster = ZMeans(6)
    cluster.train(data)



思路是参考Kmeans算法,代码是根据《数据科学入门》里面Kmeans算法的代码改写的。
①将数据按降序排好
②设定初始均值全为均值
③对每一个数据遍历,找出离这个数据最远的组别,将数据标记为该组别
④遍历dataframe,直到每行数据都标记上组

当时的思路是,Kmeans是分类算法,找最近的点即是找相似点。
而这里的分组正好相反,因为已经按顺序排好,将数据分配给最远的组别,使得全部组别在“靠近".

代码参照这个思路,可能你会有疑问。
Q:为什么初始标记全为7呢?
A:因为当时做的急,要分6个组即数字0~5分组,而初始化一个指定长度的list脑子一抽就这样写了,当然这样写不好扩展。

Q:for x in range(1):这个循环有什么?
A:一次循环就可以将所以组分配好了,但是可以再循环一次再重新分配,所以这个是想着手动控制次数用的。

刚做完输出结果的时候挺满意的,各组组内平均值确实都达到了平均,但是忽略了一个问题,要求各组个数差不多,所以一求和就发现,有的组特别多,有的组特别少。崩溃ing


后来又想了一个v2版本——随机分组版。
每个维度可以分为两种,高于平均值和低于平均值的。比如送货件数,高于平均值的标记为“+”,低于平均值的标记为“-”。退货件数、送货件数、退货单数、送货单数(4个维度)。
例:
退货件数、送货件数、退货单数、送货单数都高于平均值的就是(++++)、
退货件数、送货件数高于平均值,退货单数、送货单数低于平均值就是(++--)

一共(2^4=16)16种组合,每种组合里面按随机的方法分成k组。再将16个组合里面相同标记的组成同组。所以最后每个组里面,既有(----)这种,也有(++++)、(-+-+)。这种随机抽取的应该能达到平均的效果。不过因为某些原因没有写代码验证行不行。


最后就是终于想出一种相对而言比较好的分类算法。
v3版——Z分数法

先上代码

import pandas as pd
from sklearn import preprocessing as pp


def f_class(row):
    print(row)


if __name__ == '__main__':
    # 组的个数
    k = int(input("要分多少组?\n"))

    # 标记list 长度k
    assignments = [None]*k

    # 读取数据
    open_path = r' '
    data = pd.read_excel(open_path ,sheet_name='Sheet1',header = 0 )
    # 设置新索引
    data = data.set_index('供应商')

    # 缺失值补0
    data.fillna(0,inplace=True)

    data = data.astype('float')
    # 标准化 Z分数
    scaled_data = data.copy()
    scaled_data.ix[:, :] = pp.scale(data.ix[:, :])

    # Z分数都加100 求和 排序降序
    scaled_data.ix[:, :] = scaled_data.ix[:, :]+100
    scaled_data['score'] = scaled_data['退货件数']+scaled_data['送货件数']+scaled_data['退货单数']+scaled_data['单数']
    scaled_data.sort_values('score',inplace=True,ascending=False)

    # 关键部分
    #
    scaled_data['mark'] = None
    for x in range(len(scaled_data)):
        for i in range(k):
            assignments[i] = [item1[4] for (idx1, item1) in scaled_data.iterrows() if item1[5] == i]
            assignments[i] = sum(assignments[i])
        # 最小索引
        min_index = assignments.index(min(assignments))
        scaled_data.ix[x,'mark'] = min_index
    print(scaled_data)

    save_path = r' '
    write = pd.ExcelWriter(save_path)
    scaled_data.to_excel(write, sheet_name='分组结果', header=True, index=True)
    data.to_excel(write, sheet_name='源数据', header=True, index=True)
    write.save()

    print('完成')    

代码做法
①将四个维度全部数据标准化,即算出对应的Z分数,
②将全部Z分数加上100,消除负数,
③求出每行四个维度Z分数的合计——Z分数合计,按降序排序
④从第一个开始,算出当前哪个组别Z分数合计最小,将改行数据分配给这个组别。

思路
这个解法最开始是看的老大的思路,他将所有供应商按送货件数排好,然后1~6这样循环。
这种方法简单粗暴但是缺点很明显—— 第一组 > 第二组> ... > 最后一组
改进一下就是1~ 6一次后6~ 1一次然后不断循环。但是效果也不够好。

最后综合这几天的思考,最终想到这个解法。

可以看到,前面①~③都是为④准备。
最后出来的效果还可以,但这是什么原因呢?
首先要明白什么是Z分数,或者说数据标准化,也有称为归一化。这样处理数据之后可以消除单位的影响,Z分数可以告诉你,这个数据离平均值大概有多少。大于0表示高于平均值,越大离平均值越远,小于0表示低于平均值,越小离平均值越远。
第二,因为Z分数有正有负,但Z分数不会太大,加上100后全部都是正数(其实+10就可以了,Z分数>3或<-3已经算是异常值了),将四个维度的Z分数合计,这个Z分数合计一定程度上可以理解为将四个维度缩小到一个维度,用这一个数据来表示数据的相对大小。然后需要排序,从大到小分。
最后,关键这点:算出当前数据哪个组的Z分数合计最小,将当前数据分配给这个组。可以理解为一种补偿机制,这样可以保证最后每个组的和差不多。

所以这个解法的关键点就是,
①将四维数据降为一维,用这个一维一定程度上代表数据的位置。
②先排序,再分组(补偿)。

而缺点也明显:排序后的数据间隔不能太大。否则可能末尾数据全部到某一个组去了。


也许还有更好的解法,但这个是目前我能想到且能实现的解法了。这次要解决的问题不像之前,这次的解法各种各样,网上也参考了很多但直觉觉得不对。

这次也算是把以前了解的比较不常见的点都复习了一遍
统计学的Z分数(数据标准化)、数据降维、向量的距离。


不过后来想想,好像不用消除负数也可以啊(」゜ロ゜)」

相关文章

  • Python-平均分组算法

    这几天老大给了一个任务,比较有意思,也比较头疼。 Q:现在有供应商的退货件数、送货件数、退货单数、送货单数(4个维...

  • Java分组密码算法DES

    Java分组密码算法DES 1实验内容 掌握分组密码算法DES方法,能用高级语言实现分组密码算法DES。DES算法...

  • 分组再平均

    分组再平均

  • cfs调度之优先级,虚拟时间,权重

    cfs调度算法有两部分组成.1 虚拟时间(Vruntime)2 平均负载 (内核3.8开始加入) 虚拟时间主要是根...

  • DES 加密算法

    DES 加密算法算是分组加密算法中最简单的算法了,了解 DES 加密算法开源对分组加密算法有一个初步的了解。因此,...

  • 数据库基础06分组、关联查询

    分组 分组查询 示例:按部门编号进行分组,查询出各个部门的平均工资 HAVING 句子 HAVING子句用来对分组...

  • 密码学的安全性浅析2

    分组密码 分组密码是一种对称密钥算法。它将明文分成多个等长的模块,使用确定的算法和对称密钥对每组分别加密解密。分组...

  • 分组密码DES

    分组密码的原理 DES是分组密码,分组密码将消息进行等长分组,使用同一密钥对每个分组进行加密。 DES算法 DES...

  • SM4对称加密算法

    一、简介 与DES和AES算法类似,SM4算法是一种分组密码算法。 其分组长度为128bit,密钥长度也为128b...

  • 机器学习算法之旅

    这篇文章,我们将了解最流行的机器学习算法。 用两种方法来对机器学习算法进行分组。 按照学习风格进行分组 按照算法的...

网友评论

      本文标题:Python-平均分组算法

      本文链接:https://www.haomeiwen.com/subject/jyuyuqtx.html