【问题标题】:Fitting "multimodal" lognormal distributions to data using python使用 python 将“多模式”对数正态分布拟合到数据中
【发布时间】:2019-11-25 15:02:49
【问题描述】:

我使用实验室中的仪器测量了以下数据。由于仪器根据其直径将不同尺寸的颗粒收集在箱中,因此测量基本上是“分箱”的:

import numpy as np
import matplotlib.pylab as plt
from lmfit import models

y = np.array([196, 486, 968, 2262, 3321, 4203, 15072, 46789, 95201, 303494, 421484, 327507, 138931, 27973])
bins = np.array([0.0150, 0.0306, 0.0548, 0.0944, 0.1540, 0.2560, 0.3830, 0.6050, 0.9510, 1.6400, 2.4800, 3.6700, 5.3800, 9.9100, 15])

bin_width=np.diff(bins)
x_plot = np.add(bins[:-1],np.divide(bin_width,2))
x=x_plot
y=y

这里绘制的是数据的外观。以 x-scale 为单位,有一个 0.1 左右的众数和一个 2 左右的众数。

在该研究领域内,通常将“多峰”对数正态分布拟合到此类数据:鉴于此,我使用 LMFIT 拟合了 2 左右的模式:

model = models.LognormalModel()
params = model.make_params(center=1.5, sigma=0.6, amplitude=2214337)

result = model.fit(y, params, x=x)
print(result.fit_report())

plt.plot(x, y, label='data')
plt.plot(x, result.best_fit, label='fit')
plt.xscale("log")
plt.yscale("log")
plt.legend()
plt.show()

正如预期的那样,这会很好地拟合 2 左右的第二种模式。我的问题是,我如何才能在 0.1 左右拟合第二种模式(基本上这两种模式的总和应该适合数据)?我意识到也可以说三种模式会更好,但我认为一旦我了解如何使用两种模式,添加第三种模式应该是微不足道的。

【问题讨论】:

    标签: python curve-fitting lmfit


    【解决方案1】:

    lmfit.Models 可以加在一起,如下所示:

    model = (models.LognormalModel(prefix='p1_') +
             models.LognormalModel(prefix='p2_') +
             models.LognormalModel(prefix='p3_') )
    
    params = model.make_params(p1_center=0.3, p1_sigma=0.2, p1_amplitude=1e4,
                               p2_center=1.0, p2_sigma=0.4, p2_amplitude=1e6,
                               p3_center=1.5, p3_sigma=0.6, p3_amplitude=2e7)
    

    在复合模型中,模型的每个组件都有自己的“前缀”(任何字符串),该前缀位于参数名称之前。您可以通过以下方式获得模型组件的字典:

    components = result.eval_components()
    # {'p1_': Array, 'p2_': Array, 'p3_': Array}
    for compname, comp in components.keys():
        plt.plot(x, comp, label=compname)
    

    为了拟合要在半对数或对数对数图上表示的数据,您可以考虑将模型拟合到 log(y)。否则,在非常低的y 值下,拟合不会对错配非常敏感。

    注意lmfit 模型和参数支持边界。您可能想要或发现您需要设置边界,例如

    params['p1_amplitude'].min = 0
    params['p1_sigma'].min = 0
    params['p1_center'].max = 0.5
    params['p3_center'].min = 1.0
    

    【讨论】:

    • 您好,感谢您的 cmets。关于 lmfit,我认为有可能以某种方式获得最终参数(以使用线下)?
    • 另一个问题 - 如果我按照您的建议记录转换数据,我假设有一种方法可以将拟合参数转换回原始的未转换数据。我以为我只需要对每种模式的幅度做一个 exp() ,但这似乎不起作用。
    • @user1912925 最适合的参数将在result.params 中。如果您适合log(y),则适合的参数将用于建模...巧合的是,如果您适合log(y),您可以将GaussianModels 拟合到那个(如另一个答案中指出的那样),而amplitudes 将是每个组件的优势(或领域)。这就是我要开始的......
    【解决方案2】:

    这是您尝试拟合的对数正态混合分布。您可以简单地记录您的数据并拟合高斯混合:

    import numpy as np
    from sklearn.mixture import GaussianMixture
    
    # Make data from two log-normal distributions
    # NOTE: 2d array of shape (n_samples, n_features)
    n = 10000
    x = np.zeros((n,1))
    x[:n//2] = np.random.lognormal(0,1, size=(n//2,1))
    x[n//2:] = np.random.lognormal(2,0.5, size=(n//2,1))
    
    # Log transform the data
    x_transformed = np.log(x)
    
    # Make gaussian mixture model.
    # n_init makes multiple initial guesses and
    # depending on data, 1 might be good enough
    # Decrease tolerance for speedup or increase for better precision
    m = GaussianMixture(n_components=2, n_init=10, tol=1e-6)
    
    # Fit the model
    m.fit(x_transformed)
    
    # Get the fitted parameters
    # NOTE: covariances are stdev**2
    print(m.weights_) # [0.50183897 0.49816103]
    print(m.means_) # [1.99866785, -0.00528186]
    print(m.covariances_) # [0.25227372,0.99692494]
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-03-06
      • 2014-09-02
      • 1970-01-01
      • 2017-08-31
      • 2019-07-10
      • 2016-04-17
      • 2021-07-20
      • 2020-12-04
      相关资源
      最近更新 更多