如何将 Weibull 分布拟合到包含零的输入数据?

机器算法验证 分布 曲线拟合
2022-01-30 12:28:21

我正在尝试重现现有的预测算法,该算法由一位退休的研究人员传下来。第一步是将一些观察到的数据拟合到 Weibull 分布,以获得将用于预测未来值的形状和尺度。我正在使用 R 来执行此操作。这是我的代码示例:

x<-c(23,19,37,38,40,36,172,48,113,90,54,104,90,54,157,51,77,78,144,34,29,45,16,15,37,218,170,44,121)
f<-fitdistr(x, 'weibull')

这工作正常,除非输入数组中有任何零,这会导致它完全失败。同样的事情也发生在 SAS 中。据我了解,这是因为计算 Weibull 分布的步骤之一是采用自然对数,对于 0 未定义。有没有合理的方法来解决这个问题?

到目前为止,我发现的最好的方法是将所有输入值加 1,拟合曲线,然后从我的预测值中减去 1(向上“移动”曲线,然后再向下移动 1)。这与之前预测的数据相当吻合,但似乎这一定是错误的做法。

编辑:输入数组中的值是观察到的,真实世界的数据(某事的出现次数)在几年的范围内。因此,在某些年份,出现次数为零。不管它是不是最好的方法(我同意它可能不是),原始算法作者声称使用了 Weibull 分布,我不得不尝试复制他们的过程。

4个回答

(正如其他人所指出的,当数据仅为整数时,Weibull 分布不太可能是一个合适的近似值。以下内容旨在帮助您确定之前的研究人员所做的事情,无论是对还是错。)

有几种不受数据中零点影响的替代方法,例如使用各种矩估计方法。这些通常需要对涉及 gamma 函数的方程进行数值求解,因为 Weibull 分布的矩是根据该函数给出的。我不熟悉 R,但这里有一个Sage程序,它说明了一种更简单的方法——也许它可以适应 R?(您可以在例如 Horst Rinne的“The Weibull distribution: a handbook” ,第 455 页 ff中阅读有关此方法和其他此类方法的信息——但是,他的 eq.12.4b 中有一个错字,如“-1”是多余的)。

"""
Blischke-Scheuer method-of-moments estimation of (a,b)
for the Weibull distribution F(t) = 1 - exp(-(t/a)^b)
""" 

x = [23,19,37,38,40,36,172,48,113,90,54,104,90,54,157,
      51,77,78,144,34,29,45,16,15,37,218,170,44,121]
xbar = mean(x)
varx = variance(x)
var("b"); f(b) = gamma(1+2/b)/gamma(1+1/b)^2 - 1 - varx/xbar^2
bhat = find_root(f, 0.01, 100)
ahat = xbar/gamma(1+1/bhat)
print "Estimates: (ahat, bhat) = ", (ahat, bhat)

这产生了输出

Estimates: (ahat, bhat) =  (81.316784310814455, 1.3811394719075942)


如果上述数据被修改(仅用于说明),将三个最小值替换为0, IE

x = [23,0,37,38,40,36,172,48,113,90,54,104,90,54,157,
      51,77,78,144,34,29,45,0,0,37,218,170,44,121]

然后相同的过程产生输出

Estimates: (ahat, bhat) =  (78.479354097488923, 1.2938352346035282)


编辑:我刚刚安装了 R 来试一试。冒着使这个答案过长的风险,对于任何感兴趣的人来说,这里是我的 Blischke-Scheuer 方法的 R 代码:

fit_weibull <- function(x)
{
    xbar <- mean(x)
    varx <- var(x)
    f <- function(b){return(gamma(1+2/b)/gamma(1+1/b)^2 - 1 - varx/xbar^2)}
    bhat <- uniroot(f,c(0.02,50))$root
    ahat <- xbar/gamma(1+1/bhat)
    return(c(ahat,bhat))
}

这再现了(至五个有效数字)上面的两个 Sage 示例:

x <- c(23,19,37,38,40,36,172,48,113,90,54,104,90,54,157,
     51,77,78,144,34,29,45,16,15,37,218,170,44,121)
fit_weibull(x)
[1] 81.316840  1.381145

x <- c(23,0,37,38,40,36,172,48,113,90,54,104,90,54,157,
      51,77,78,144,34,29,45,0,0,37,218,170,44,121)
fit_weibull(x)
[1] 78.479180  1.293821

您也可以尝试拟合三参数 Weibull,其中第三个参数是位置参数,让我们说θ. 这相当于估计您应该添加到数据中以使您最适合 Weibull 的常数。您可以通过在 周围放置“包装器”来使用配置文件似然方法来执行此操作fitdistr,其中包装器的值为θ和数据,补充说θ到数据,调用fitdistr函数,并返回关联的对数似然:

foo <- function(theta, x)
{
  if (theta <= -min(x)) return(Inf);
  f <- fitdistr(x+theta, 'weibull')
  -2*f$loglik
}

然后使用一维优化最小化这个函数:

bar <- optimize(foo, lower=-min(x)+0.001, upper=-min(x)+10, x=x)

我刚刚根据什么都没有编造“+10”。

对于三个最小值被零替换的数据,我们得到:

> bar
$minimum
[1] 2.878442

$objective
[1] 306.2792

> fitdistr(x+bar$minimum, 'weibull')
     shape        scale   
   1.2836432   81.1678283 
 ( 0.1918654) (12.3101211)
> 

bar$minimum是 MLEθfitdistr输出是 Weibull 参数的 MLE,与θ那是。正如你所看到的,它们非常接近上面演示的矩估计器@res。

它应该失败,你应该感谢它失败了。

您的观察表明,故障发生在您开始观察它们的那一刻。如果这是一个真实的过程,来自真实的(而不是模拟的数据),你需要以某种方式解释你得到零的原因。我看过生存研究,其中 0 次出现是由于以下几件事之一:

  1. 数据实际上被截断了:在研究开始之前,物体处于危险之中并且失败了,你想假装你一直都在观察它们。
  2. 仪器校准不佳:您没有足够的测量精度进行研究,因此在开始时间附近发生的故障被编码为零。
  3. 编码为零的东西不是零。他们是以某种方式被排除在分析之外的人或物体。零只是由于合并、排序或以其他方式重新编码缺失值而出现在数据中。

因此,对于案例 1:您需要使用适当的审查方法,即使这意味着追溯性地提取记录。案例 2 意味着您可以使用 EM 算法,因为您有精度问题。贝叶斯方法在这里也同样有效。案例 3 意味着您只需要排除应该丢失的值。

我同意上面红衣主教的回答。但是,添加一个常数以避免零也是很常见的。另一个常用的值是 0.5,但也可能使用任何正常数。您可以尝试一系列值,以查看您是否可以确定先前研究人员使用的确切值。然后你可以确信你能够重现他的结果,然后再继续寻找更好的分布。