计算从连续分布中采样的数据的模式

机器算法验证 分布 配件 模式
2022-01-22 05:10:31

拟合从连续分布中采样的数据的“模式”的最佳方法是什么?

由于连续分布的模式在技术上是未定义的(对吗?),我真的在问“你如何找到最常见的值”?

如果您假设父分布是高斯分布,您可以对数据进行分箱并发现模式是计数最多的分箱位置。但是,如何确定 bin 大小?是否有可靠的实现可用?(即,对异常值具有鲁棒性)。我使用python/ scipy/ numpy,但我可能可以R毫不费力地翻译。

4个回答

在 R 中,应用不基于底层分布的参数建模并使用密度的默认核估计器对 10000 个伽马分布变量的方法:

x <- rgamma(10000, 2, 5)
z <- density(x)
plot(z) # always good to check visually
z$x[z$y==max(z$y)]

返回 0.199,这是估计具有最高密度的 x 的值(密度估计存储为“z$y”)。

假设您从大小为 n 的总样本中制作了一个大小为 b 的直方图,并且最大的 bin 有 k 个条目。然后该 bin 内的平均 PDF 可以估计为 b*k/n。

问题在于,另一个具有较少成员总数的 bin 可能具有较高的点密度。只有当您对 PDF 的变化率有合理的假设时,您才能知道这一点。如果这样做,那么您可以估计第二大 bin 实际包含众数的概率。

根本问题是这样的。通过 Kolmogorov-Smirnov 定理,样本可以很好地了解 CDF,因此可以很好地估计中位数和其他分位数。但是知道 L1 中函数的近似值并不能提供其导数的近似知识。因此,在没有额外假设的情况下,没有样本可以很好地了解 PDF。

为了计算 python 中连续分布的模式,我建议三个选项。Scipy 有scipy.stats.mode(data)[0]但它并不准确。对于这三个选项之后我们需要得到一个近似的de PDF函数的数据。一个很好的方法是高斯核密度估计(维基百科)。使用 scipy,我们可以使用distribution = scipy.stats.gaussian_kde(data)并使用distribution.pdf(x)[0]获取数据集的 pdf 值三种方法搜索分布的最大值,得到域中值的原像。第一次使用 scipy 最小化方法,第二次使用 max() 本机函数,第三次使用 SHGO scipy 方法。这里和比较的​​图像: 在此处输入图像描述 以及完整的代码:

import numpy as np
import scipy.stats
import scipy.optimize
import matplotlib as mpl
import matplotlib.pyplot as plt
import random

mpl.style.use("ggplot")

def plot_histogram(data, distribution, modes):
    plt.figure(figsize=(8, 4))
    plt.hist(data, density=True, ec='white')
    plt.title('HISTOGRAM')
    plt.xlabel('Values')
    plt.ylabel('Frequencies')
    
    
    x_plot = np.linspace(min(data), max(data), 1000)
    y_plot = distribution.pdf(x_plot)
    plt.plot(x_plot, y_plot, linewidth=4, label="PDF KDE")
    
    for name, mode in modes.items():
        plt.axvline(mode, linewidth=2, label=name+": "+str(mode)[:7], color=(random.uniform(0, 1), random.uniform(0, 1), random.uniform(0, 1)))
    
    plt.legend(title='DISTRIBUTIONS', bbox_to_anchor=(1.05, 1), loc='upper left')
    plt.show()
    

## SCIPY MODE
def calc_scipy_mode(data):
    return scipy.stats.mode(data)[0]

## METHOD 1: MAXIMIZE PDF SCIPY MINIMIZE
def calc_minimize_mode(data, distribution):
    def objective(x):
        return 1/distribution.pdf(x)[0]
    
    bnds = [(min(data), max(data))]
    solution = scipy.optimize.minimize(objective, [1], bounds = bnds)
    
    return solution.x[0]

## METHOD 2: MAXIMIZE PDF AND GET PREIMAGE
def calc_max_pdf_mode(data, distribution):
    x_domain = np.linspace(min(data), max(data), 1000)
    y_pdf = distribution.pdf(x_domain)
    i = np.argmax(y_pdf)
    return x_domain[i]

## METHOD 3: ## METHOD 3: MAXIMIZE PDF SCIPY SHGO
def calc_shgo_mode(data, distribution):
    def objective(x):
        return 1/distribution.pdf(x)[0]
    
    bnds = [[min(data), max(data)]]
    solution = scipy.optimize.shgo(objective, bounds= bnds, n=100*len(data))
    return solution.x[0]



def calculate_mode(data):
    ## KDE
    distribution = scipy.stats.gaussian_kde(data)
    
    scipy_mode = calc_scipy_mode(data)[0]
    minimize_mode = calc_minimize_mode(data, distribution)
    max_pdf_mode = calc_max_pdf_mode(data, distribution)
    shgo_mode = calc_shgo_mode(data, distribution)
    
    modes = {
        "scipy_mode": scipy_mode, 
        "minimize_mode": minimize_mode, 
        "max_pdf_mode": max_pdf_mode,
        "shgo_mode": shgo_mode
    }
    plot_histogram(data, distribution, modes)
    
    
if __name__ == "__main__":
  
    data = [d1, d2, d3, ..........]
    
    calculate_mode(data)

最近我遇到了类似的问题,并在Wolfram Mathematica中提出了以下代码:

ModeEstimate[data_?VectorQ] := 
  MaximalBy[data, PDF[SmoothKernelDistribution[data]], 1][[1]];

但请记住,这是一个粗略的估计,如果实际的连续分布具有在样本中没有充分表示的窄峰,或者样本恰好包含零星的值簇,则它甚至可能是完全错误的。我相信如果没有关于实际分布的额外信息,就无法量化计算估计中的不确定性。

SmoothKernelDistribution函数有各种选项,您可以尝试调整这些选项以获得针对特定用例的更好结果。