问题是为kde的“带宽”选择了默认算法。 The default method是'scott',当有许多相等的值时,它并不是很有用。
带宽是位于每个采样点并求和的高斯宽度。较低的带宽更接近数据,较高的带宽使一切平滑。最佳地点在中间。在这种情况下,bw=0.3
是一个不错的选择。为了比较不同的kde,建议每次选择完全相同的带宽。
下面是一些示例代码,用于显示bw='scott'
和bw=0.3
之间的区别。示例数据是标准正态分布中的150个值以及400、450或500个固定值。
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns; sns.set()
fig,axs = plt.subplots(nrows=2,ncols=3,figsize=(10,5),gridspec_kw={'hspace':0.3})
for i,bw in enumerate(['scott',0.3]):
for j,num_same in enumerate([400,450,500]):
y = np.concatenate([np.random.normal(0,1,150),np.repeat(-3,num_same)])
sns.kdeplot(y,bw=bw,ax=axs[i,j])
axs[i,j].set_title(f'bw:{bw}; fixed values:{num_same}')
plt.show()
第三幅图警告说,无法使用Scott的建议带宽来绘制kde。
PS:如@mwascom在评论中所述,在这种情况下,使用scipy.statsmodels.nonparametric.kde
(而不是scipy.stats.gaussian_kde
)。那里的默认值为 "scott" - 1.059 * A * nobs ** (-1/5.),where A is min(std(X),IQR/1.34)
。 min()
阐明了行为上的突然变化。 IQR
是"interquartile range",第75个百分点与第25个百分点之间的差异。
,
如果样本具有重复值,则表示基础分布不连续。在为说明问题而显示的数据中,我们可以在左侧看到Dirac分布。内核平滑可能会应用于此类数据,但要格外小心。确实,为了近似此类数据,我们可以使用内核平滑,其中与Dirac相关的带宽为零。但是,在大多数KDE方法中,所有内核原子只有一个带宽。此外,用于计算带宽的各种规则是基于对分布的PDF的二阶导数的近似性的某种估计。这不能应用于不连续的分布。
但是,我们可以尝试将样本分为两个子样本:
(Johanc已经提到了这个想法)。
以下是尝试执行此分类的尝试。 np.unique
方法用于计算复制实现的出现次数。复制值与狄拉克斯相关,混合物中的重量由样品中这些复制值的分数估算。剩下的实现,唯一性,然后用于估计KDE的连续分布。
以下功能将有助于克服draw
带有OpenTURNS的Mixtures方法的当前实现方式的局限性。
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
以下脚本使用包含Dirac和高斯分布的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)
对于所有实现,复制值都与Diracs相关联,唯一值放在单独的列表中。
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)
Etvoilà:混合物已准备就绪。
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,即整个样本的一半。
本文链接:https://www.f2er.com/2355951.html