文章目录
- 1 问题缘起
- 2. 数据离散化
- 等距离散
- 等频离散
- 聚类离散
- 其他
- 3. 均值标准差分级
1 问题缘起
在数学建模中,我经常遇到这样一个问题:
在某一步中,需要把数据分成好几个类别或者是按照数据大小分级划分。
放到一维数据中形象一点解释就是我有这么一条线,x轴没有任何意义,y轴代表数据的大小,我需要把这些数据分为5类(在图中切4刀),应该怎么划分?
经典的例子是 2023 年美赛C题 Wordle 的倒数第二问,很多团队评估了每个单词的难度系数,但是不知道怎么按照这个难度系数划分难度等级。这是个细节问题,很容易钻牛角尖。
看上去确实没什么,但是如果乱划分或者直接划分,似乎糙了点,或者说找不到什么理论依据,不严谨。
个人总结出了两类方法:
- 数据离散化
- 均值标准差分级
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)