我在 1d 中有一组点,其中一个区域更密集。在 scikit-learn(或任何其他库)中是否有合适的方法来找到这个密集区域?看起来它应该是一个聚类问题,聚类数设置为 1,但它也需要对噪声有弹性。或者它可能是一个异常值检测问题?这是我正在谈论的数据类型的直方图。
我无法上传真实数据,但这是一个简单的模拟:
import random
import matplotlib.pyplot as plt
N = 100
start = 0
points = []
rate = 0.1
for i in range(N):
points.append(start)
start = start + random.expovariate(rate)
rate = 10
for i in range(N*10):
points.append(start)
start = start + random.expovariate(rate)
rate = 0.1
for i in range(N):
points.append(start)
start = start + random.expovariate(rate)
plt.hist(points, bins = 100)
plt.show()
最佳答案
在不随意选择离散化、高度等的情况下做到这一点的一种方法是将两个均匀分布的总和拟合到数据中。一个均匀分布的支持度可以是固定的:它的支持度是点的范围,[a, b] 下面。另一个有支持[c, d],代表密集部分。
虽然 scipy.stats 有内置的方法来拟合许多分布,但我没有在列表中看到这个特定的方法,所以我编写了自己的 nlf
函数,它返回 log likelihood 函数的否定。对数似然是数据点上 pdf 的对数之和。这里 pdf 只有两个值,密集部分内的 1/(d-c) + 1/(b-a)
和密集部分外的 1/(b-a)
。所以计算是基于计算密集部分内的点。
from numpy import np
from scipy.optimize import fmin
points = np.array(points) # should be a numpy array
a, b = points.min(), points.max()
def nlf(params):
c, d = params
within = ((points > c) & (points < d)).sum()
return -np.log(1/(d-c) + 1/(b-a))*within - np.log(1/(b-a))*(len(points) - within)
res = fmin(nlf, (0.9*a + 0.1*b, 0.1*a + 0.9*b), disp=0)
答案(
res
)是 [1046.32119001, 1149.31175184]
(对于我的模拟数据实例)。很合身。c, d
的起点被选择为比 [a, b]
本身稍微窄一些,以将优化器推向正确的方向。这不会强制密集部分在这个区间内 (0.9*a + 0.1*b, 0.1*a + 0.9*b)
;如果需要,优化器可以扩展它。关于python - 如何在 1d 中找到密集区域,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/49803562/