如果样本具有重复值,这意味着基础分布不连续。在您为说明问题而显示的数据中,我们可以在左侧看到狄拉克分布。内核平滑可能适用于此类数据,但要小心。事实上,为了近似这些数据,我们可以使用与狄拉克相关的带宽为零的内核平滑。然而,在大多数 KDE 方法中,所有内核原子只有一个带宽。此外,用于计算带宽的各种规则是基于对分布 PDF 的二阶导数的粗糙度的某种估计。这不能应用于不连续分布。
但是,我们可以尝试将样本分成两个子样本:
(johanc 已经提到过这个想法)。
以下是执行此分类的尝试。 np.unique 方法用于计算复制实现的出现次数。重复值与狄拉克相关,混合物中的重量是根据样本中这些重复值的分数估计的。剩下的实现,uniques,然后用 KDE 来估计连续分布。
以下函数将有助于克服与 OpenTURNS 混合的 draw 方法的当前实现的限制。
def DrawMixtureWithDiracs(distribution):
"""Draw a distributions which has Diracs.
https://github.com/openturns/openturns/issues/1489"""
graph = distribution.drawPDF()
graph.setLegends(["Mixture"])
for atom in distribution.getDistributionCollection():
if atom.getName() == "Dirac":
curve = atom.drawPDF()
curve.setLegends(["Dirac"])
graph.add(curve)
return graph
以下脚本使用包含狄拉克和高斯分布的 Mixture 创建用例。
import openturns as ot
import numpy as np
distribution = ot.Mixture([ot.Dirac(-3.0),
ot.Normal()], [0.5, 0.5])
DrawMixtureWithDiracs(distribution)
这是结果。
然后我们创建一个样本。
sample = distribution.getSample(100)
这是您的问题开始的地方。我们计算每个实现的出现次数。
array = np.array(sample)
unique, index, count = np.unique(array, axis=0, return_index=True,
return_counts=True)
对于所有实现,复制值都与狄拉克相关联,并且唯一值放在单独的列表中。
sampleSize = sample.getSize()
listOfDiracs = []
listOfWeights = []
uniqueValues = []
for i in range(len(unique)):
if count[i] == 1:
uniqueValues.append(unique[i][0])
else:
atom = ot.Dirac(unique[i])
listOfDiracs.append(atom)
w = count[i] / sampleSize
print("New Dirac =", unique[i], " with weight =", w)
listOfWeights.append(w)
连续原子的权重是狄拉克的权重之和的补数。这样,权重之和将等于 1。
complementaryWeight = 1.0 - sum(listOfWeights)
weights = list(listOfWeights)
weights.append(complementaryWeight)
简单的部分来了:独特的实现可用于拟合内核平滑。然后将 KDE 添加到原子列表中。
sampleUniques = ot.Sample(uniqueValues, 1)
factory = ot.KernelSmoothing()
kde = factory.build(sampleUniques)
atoms = list(listOfDiracs)
atoms.append(kde)
等等:混合物准备好了。
mixture_estimated = ot.Mixture(atoms, weights)
以下脚本比较初始混合和估计混合。
graph = DrawMixtureWithDiracs(distribution)
graph.setColors(["dodgerblue3", "dodgerblue3"])
curve = DrawMixtureWithDiracs(mixture_estimated)
curve.setColors(["darkorange1", "darkorange1"])
curve.setLegends(["Est. Mixture", "Est. Dirac"])
graph.add(curve)
graph
这个数字似乎令人满意,因为连续分布是从一个大小仅等于 50 的子样本(即整个样本的一半)估计的。