【问题标题】:How to fit a multimodal log-normal distribution in Matlab?如何在 Matlab 中拟合多峰对数正态分布?
【发布时间】:2023-11-25 21:29:01
【问题描述】:

我需要拟合代表粒度测量值的多峰分布。例如,这些测量结果可能如下所示:

现在我想拟合这些曲线。在this answer 的帮助下,我能够为单峰分布函数获得相当不错的结果:

fun = @(p,x)(p(1)./x .* 1./(p(3)*sqrt(2*pi)).*exp(-(log(x)-p(2)).^2./(2*p(3)^2)));

通过像这样缩放结果参数:

[yM_in, pp_in] = max(DPF_in_mean);
xM_in = x_data(pp_in);
[yM_out, pp_out] = max(DPF_out_mean);
xM_out = x_data(pp_out);

xR_in = x_data / xM_in;
yR_in = DPF_in_mean / yM_in;
xR_out = x_data / xM_out;
yR_out = DPF_out_mean / yM_out;

opts = optimoptions('lsqcurvefit','TolX',1e-4,'TolFun',1e-8);
p0 = [1,1,1];
p_in = lsqcurvefit(fun,p0,xR_in,yR_in,[],[],opts);
p_out = lsqcurvefit(fun,p0,xR_out,yR_out,[],[],opts);

p_in_scaled = [ yM_in * p_in(1) * xM_in, p_in(2) + log(xM_in), p_in(3) ];
p_out_scaled = [ yM_out * p_out(1) * xM_out, p_out(2) + log(xM_out), p_out(3) ];

但是,如果我绘制结果拟合,很明显单峰分布不足以拟合测量值:

在关于 multimodal distribution 的 Wikipedia 文章中,我似乎可以像这样混合第二个分布:

fun = @(p,x)(p(7)*(p(1)./x .* 1./(p(3)*sqrt(2*pi)).*exp(-(log(x)-p(2)).^2./(2*p(3)^2))) + (1 - p(7))*(p(4)./x .* 1./(p(5)*sqrt(2*pi)).*exp(-(log(x)-p(6)).^2./(2*p(5)^2))));

但是我不知道我需要如何在缩放中整合额外的参数

p_in_scaled = [ yM_in * p_in(1) * xM_in, p_in(2) + log(xM_in), p_in(3) ];

因为我并不真正了解在这个扩展步骤中发生了什么。

如何使用多峰分布来拟合我的测量结果?

编辑

用到的数据如下:

x_data = [4.87000000000000e-09 5.62000000000000e-09 6.49000000000000e-09 7.50000000000000e-09 8.66000000000000e-09 ...
          1.00000000000000e-08 1.15500000000000e-08 1.33400000000000e-08 1.54000000000000e-08 1.77800000000000e-08 ...
          2.05400000000000e-08 2.37100000000000e-08 2.73800000000000e-08 3.16200000000000e-08 3.65200000000000e-08 ...
          4.21700000000000e-08 4.87000000000000e-08 5.62300000000000e-08 6.49400000000000e-08 7.49900000000000e-08 ...
          8.66000000000000e-08 1.00000000000000e-07 1.15480000000000e-07 1.33350000000000e-07 1.53990000000000e-07 ...
          1.77830000000000e-07 2.05350000000000e-07 2.37140000000000e-07 2.73840000000000e-07 3.16230000000000e-07 ...
          3.65170000000000e-07 4.21700000000000e-07 4.86970000000000e-07 5.62340000000000e-07 6.49380000000000e-07 ...
          7.49890000000000e-07 8.65960000000000e-07 1.00000000000000e-06];

DPF_in_mean = [188318640795.745 360952462222.222 750859638450.704 2226776878843.93 4845941940346.82 7979258430057.80 ...
               11010887350289.0 13462058712138.7 15090350247398.8 15991756383815.0 16680978441618.5 17862081914450.9 ...
               20071390890173.4 23460963364161.9 27630428508670.5 31777265780346.8 35520451433526.0 38587652184971.1 ...
               40516972485549.1 41326812092485.6 41127130682080.9 40038712485549.1 37976259664739.9 34725415132948.0 ...
               30177578265896.0 24546703179190.8 18400851109826.6 12500471611560.7 7540309609248.56 3912091102658.96 ...
               1632974141040.46 458500289086.705 126012891030.303 0 0 0 7276263267.44526 11203995842.0392];

DPF_out_mean = [444898373533.333 1032357396444.44 1675044380444.44 2316141430222.22 2852971589555.56 3151959865111.11 ...
                3134892475777.78 2828026308000.00 2325761940666.67 1745907627777.78 1192912799111.11 742253282222.222 ...
                430349362888.889 255820144555.556 188235813444.444 181970493622.222 204829338533.333 233009821977.778 ...
                243007623333.333 230736732777.778 202426609488.889 169758857200.000 140604138622.222 116482776222.222 ...
                95076737155.5556 74172071777.7778 53672033733.3333 35251323911.1111 20813708255.5556 11102006362.8889 ...
                5497173092.96089 2625918349.76536 1471042995.80373 1012939492.96541 751738952.194595 589422111.731818 ...
                479373451.936508 378359645.767442]; 

【问题讨论】:

  • 嗨詹姆斯,不是真的。我需要这个来拟合测量作为我作为博士论文的一部分建立的模型的输入。我不得不承认,在我上学的时候,我们没有使用任何 Matlab 或 Python,所以我对你的问题感到非常惊讶。尽管如果您将博士学位视为一项非常扩展的学校工作,那可能是学校工作......
  • 明白。请您发布示例数据或示例数据的链接?
  • 是的!为 * 提供数据文件的推荐方式是什么?
  • 我直接在问题中添加了数据。我认为这是在 SO 上处理数据的最佳方式,因为否则无法保证数据在以后可用,对吧?
  • 变量x_dataDPF_in_meanDPF_out_mean 两种情况的x 数据。这些只是两个独立的测量值,如第一张图所示。这里DPF_in_mean 对应于测量的上游DPF_out_mean 对应于测量的下游。然后我尝试用fun 拟合两条曲线。

标签: matlab distribution curve-fitting


【解决方案1】:

这是一种可能有用的可能性。由于数据中的大峰掩盖了较小的峰,因此减去较大的峰数据会使小峰数据被隔离以进行分析。如果您知道大峰的形式,则可以将数据与其拟合,然后在每个数据集中仅保留两个双峰中的一个用于分析。一旦找到第二个峰的形式,您可以通过使用之前的分析拟合值作为最终分析的初始参数值来拟合两者的总和来重新开始。

我已经对两个数据集进行了方程搜索,以便为每个数据集中的主峰找到合适的峰方程,这是我的结果。没有进行数据转换或预处理,我使用的是发布的原始数据。

对于 DPF_in_mean,我的主峰为:

def Peak_LogNormalAShifted_model(x_in): # from zunzun.com using DPF_in_mean
    # coefficients
    a = 4.2518873952455000E+13
    b = -1.6088042345890798E+01
    c = 6.0084502637701809E-01
    d = -2.3091703726006359E-08

    return a * numpy.exp(-0.5 * numpy.power((numpy.log(x_in-d)-b) / c, 2.0))

对于 DPF_out_mean,我的主峰为:

def Peak_LogNormalA_model(x_in): # from zunzun.com using DPF_out_mean
    # coefficients
    a = 3.1863877879345913E+12
    b = -1.8334716040160675E+01
    c = 4.4913908739937525E-01

    return a * numpy.exp(-0.5 * numpy.power((numpy.log(x_in)-b) / c, 2.0))

【讨论】:

  • 嗨詹姆斯,很抱歉我的回复晚了。我发现这些是单峰分布的最佳拟合,但我如何使用多峰分布(即具有两个或多个峰)?
  • 这个想法是从观测数据中减去主峰方程的预测值,只留下次峰进行分析。在分析第二个峰的数据并找到合适的方程后,使用来自两个单独回归的参数值作为初始参数值,通过拟合“y = [主峰方程] + [次峰方程”重新开始最终回归分析。
  • 啊,我明白了!我今天会试试这个。
最近更新 更多