1 问题缘起

在数学建模中,我经常遇到这样一个问题:

在某一步中,需要把数据分成好几个类别或者是按照数据大小分级划分。

放到一维数据中形象一点解释就是我有这么一条线,x轴没有任何意义,y轴代表数据的大小,我需要把这些数据分为5类(在图中切4刀),应该怎么划分?

在这里插入图片描述

经典的例子是 2023 年美赛C题 Wordle 的倒数第二问,很多团队评估了每个单词的难度系数,但是不知道怎么按照这个难度系数划分难度等级。这是个细节问题,很容易钻牛角尖。

看上去确实没什么,但是如果乱划分或者直接划分,似乎糙了点,或者说找不到什么理论依据,不严谨。

个人总结出了两类方法:

  1. 数据离散化
  2. 均值标准差分级

2. 数据离散化

注意了,这边用的是数据离散化的思想解决问题,并不是指在一开始预处理时的数据离散化。相当于为我们划分数据找了个理论依据。

这边提一下几种常见的离散化方式:

等距离散

等距分布的思想就是如果这是一个上下限已知的数据集,取数据的值域[min, max],然后再等分值域。

以这张图为例:

在这里插入图片描述

365条数据是分布在[0,1]之间,那么5等分之后的结果就是

类别1:(-∞,0.2]
类别2:(0.2,0.4]
类别3:(0.4,0.6]
类别4:(0.6,0.8]
类别5:(0.8,+∞)

新的数据落到哪个区间就算哪个类别。

python代码:

n = 5
data["score_label"] = pd.cut(x=data["score"], bins=n, labels=[str(i+1) for i in rnage(n)])

等频离散

等频分布的思想就是如果这是一个数据量已知的数据集 dataset,对数据排序得到 dataset_sorted,取排序后的数据 reset_index,按照 reset_index 等分的 dataset_sorted 离散化数据。

还是以这张图为例:

在这里插入图片描述

365条数据是分布在[0,1]之间,那么5等分之后的结果就是

类别1:(-∞,dataset_sorted[73]]
类别2:(dataset_sorted[73],dataset_sorted[146]]
类别3:(dataset_sorted[146],dataset_sorted[219]]
类别4:(dataset_sorted[219],dataset_sorted[292]]
类别5:(dataset_sorted[292],+∞)

新的数据的值落到哪个区间就算哪个类别。

python代码:

n = 5
data = data.sort_values("score").reset_index()
data["score_label"] = (data.index / data.shape[0] * (n-1)).astype(int)

聚类离散

可以用聚类的方法计算。将需要划分的数据放入聚类算法中,按照聚类的结果划分数据。一般建议K-means聚类(密度的 DBSCAN 也不错),因为没必要玩花活。

不过感觉这个方法也不严谨,因为如果我们的任务是5分类,然后我们采用K-means聚类划分,那么我们的k值势必会取5,但是k=5不一定是当前数据集的最佳聚类数量。

所以这个方法更适合在不知道需要划分为几类数据的时候使用,顺便还能确认下划分成几类比较好~

python代码:

k=5
km = KMeans(n_clusters=k).fit(data)
data["score_label"] = km.labels_

其他

当然还有基于其他数据离散化的想法:1R离散法、基于卡方分裂的离散法、二值化离散法等等等等,这边就不细讲了。具体的操作和步骤这篇讲的很棒。

3. 均值标准差分级

这个方法是我刷论文的时候偶然发现的,当时感觉是在瞎扯,没怎么关注,后来被朋友安利了一下才开始重视它。

论文是《基于加权马尔可夫模型的股票预测》:程丽娟,冯洁明。

在这里插入图片描述

大概理解了一下方法:

在这里插入图片描述

对于奇数个划分区域,使用第二个划分方法,对于偶数个划分趋于使用第一个划分方法。

当然,如果数据在两边的分布比较稀疏,导致本方法划分之后样本不平衡。我们也可以发挥下主观能动性,这样划分:

在这里插入图片描述

不过个人认为如果要追求划分结果的样本平衡,那直接用等频离散不是更香嘛

python代码:

import numpy as np
import pandas as pd
# 仅限单列

file = pd.read_csv("data.csv", encoding="utf-8-sig")
col = file.columns
print("输入你要分成几类")
choice = int(input())
print(file.describe())
mean = file.describe().loc["mean"][col[0]]
std = file.describe().loc["std"][col[0]]
ret = []
for i in range(int(choice / 2)):
    ret.insert(0, mean - (i + ((choice % 2) / 2)) * std)
    ret.append(mean + (i + ((choice % 2) / 2)) * std)
ret.insert(0, -np.inf)
ret.append(np.inf)
ret = list(set(ret))
ret.sort()
file["std_clf"] = pd.cut(x=file[col[0]], bins=ret, right=True, labels=[i+1 for i in range(len(ret)-1)])
print(file)
Logo

GitCode 天启AI是一款由 GitCode 团队打造的智能助手,基于先进的LLM(大语言模型)与多智能体 Agent 技术构建,致力于为用户提供高效、智能、多模态的创作与开发支持。它不仅支持自然语言对话,还具备处理文件、生成 PPT、撰写分析报告、开发 Web 应用等多项能力,真正做到“一句话,让 Al帮你完成复杂任务”。

更多推荐