I am trying to estimate different distributions' parameters via scipy.stats."some_dist".fit()
, and I'm having extreme difficulty retrieving any information on the optimization process being used. Specifically, I am looking for the Hessian, which most implemented algorithms output, as documented here.
For all its optimizations, SciPy returns an object called OptimizeResult
, which contains useful info such as the Hessian, so how would one get this after .fit()
'ing some data, which calls the same optimizations?
Looks like a bit of source-diving is needed; fortunately, it does not require copying a lot of source code. This is how basic fit works:
from scipy.stats import cauchy
data = [0, 3, 4, 4, 5, 9]
res = cauchy.fit(data) # (3.9798237305661255, 0.9205374643383732)
and this is how it is modified to return OptimizeResult:
from scipy.optimize import minimize
args = cauchy._fitstart(data)
x0, func, restore, args = cauchy._reduce_func(args, {})
res = minimize(func, x0, args=(data,), method='BFGS')
Now res
is
fun: 14.337039523098689
hess_inv: array([[ 0.23321703, -0.0117229 ],
[-0.0117229 , 0.36807373]])
jac: array([ 0.0000000e+00, -1.1920929e-07])
message: 'Optimization terminated successfully.'
nfev: 32
nit: 5
njev: 8
status: 0
success: True
x: array([3.9798262 , 0.92055376])
where you may recognize the parameters as being res.x
. The function being minimized is "penalized NNLF" (nonnegative likelihood function).
By the way,
For all its optimizations, SciPy returns an object called OptimizeResult
is an over-generalization. This is true for minimize
method. The scipy.stats.fit
uses fmin
by default, which returns no such thing.
So, if want identical output to fmin
, that can be arranged, but there'll be no extra information there.
from scipy.optimize import fmin
args = cauchy._fitstart(data)
x0, func, restore, args = cauchy._reduce_func(args, {})
res = fmin(func, x0, args=(data,))
print(tuple(res)) #(3.9798237305661255, 0.9205374643383732)
Using minimize
with method='Nelder-Mead' has essentially the same effect. You do get a bit of extra information, but given that the method is simplex-based (no derivatives being computed), this information is of limited use.
res = minimize(func, x0, args=(data,), method='Nelder-Mead')
print(res)
print(tuple(res.x))
prints
final_simplex: (array([[3.97982373, 0.92053746],
[3.97983057, 0.92060317],
[3.97977536, 0.92059568]]), array([14.33703952, 14.33703953, 14.33703953]))
fun: 14.337039523477827
message: 'Optimization terminated successfully.'
nfev: 58
nit: 31
status: 0
success: True
x: array([3.97982373, 0.92053746])
(3.9798237305661255, 0.9205374643383732)