Refactored to simplify

master
Per A Brodtkorb 8 years ago
parent 28d0b8b709
commit 9205e1b19d

@ -410,7 +410,7 @@ class Profile(object):
self._set_indexes(fit_dist, i) self._set_indexes(fit_dist, i)
method = fit_dist.method.lower() method = fit_dist.method.lower()
self._set_plot_labels(fit_dist, method) self._set_plot_labels(method)
Lmax = self._loglike_max(fit_dist, method) Lmax = self._loglike_max(fit_dist, method)
self.Lmax = Lmax self.Lmax = Lmax
@ -419,13 +419,16 @@ class Profile(object):
self._set_profile() self._set_profile()
def _set_plot_labels(self, fit_dist, method): def _set_plot_labels(self, method, title='', xlabel=''):
if not title:
title = '{:s} params'.format(self.fit_dist.dist.name)
if not xlabel:
xlabel = 'phat[{}]'.format(np.ravel(self.i_fixed)[0])
percent = 100 * (1.0 - self.alpha) percent = 100 * (1.0 - self.alpha)
self.title = '{:g}% CI for {:s} params'.format(percent, self.title = '{:g}% CI for {:s}'.format(percent, title)
fit_dist.dist.name)
like_txt = 'likelihood' if method == 'ml' else 'product spacing' like_txt = 'likelihood' if method == 'ml' else 'product spacing'
self.ylabel = 'Profile log' + like_txt self.ylabel = 'Profile log' + like_txt
self.xlabel = 'phat[{}]'.format(np.ravel(self.i_fixed)[0]) self.xlabel = xlabel
@staticmethod @staticmethod
def _loglike_max(fit_dist, method): def _loglike_max(fit_dist, method):
@ -551,7 +554,17 @@ class Profile(object):
warnings.warn(str(err)) warnings.warn(str(err))
def _get_variance(self): def _get_variance(self):
return self.fit_dist.par_cov[self.i_fixed, :][:, self.i_fixed] invfun = getattr(self, '_myinvfun', None)
if invfun is not None:
i_notfixed = self.i_notfixed
pcov = self.fit_dist.par_cov[i_notfixed, :][:, i_notfixed]
gradfun = nd.Gradient(invfun)
phatv = self._par
drl = gradfun(phatv[i_notfixed])
pvar = np.sum(np.dot(drl, pcov) * drl)
return pvar
pvar = self.fit_dist.par_cov[self.i_fixed, :][:, self.i_fixed]
return pvar
def _approx_p_min_max(self, p_opt): def _approx_p_min_max(self, p_opt):
pvar = self._get_variance() pvar = self._get_variance()
@ -565,9 +578,9 @@ class Profile(object):
p_low, p_up = self._approx_p_min_max(p_opt) p_low, p_up = self._approx_p_min_max(p_opt)
pmin, pmax = self.pmin, self.pmax pmin, pmax = self.pmin, self.pmax
if pmin is None: if pmin is None:
pmin = self._search_pminmax(phatfree0, p_low, p_opt, 'min') pmin = self._search_p_min_max(phatfree0, p_low, p_opt, 'min')
if pmax is None: if pmax is None:
pmax = self._search_pminmax(phatfree0, p_up, p_opt, 'max') pmax = self._search_p_min_max(phatfree0, p_up, p_opt, 'max')
return pmin, pmax return pmin, pmax
def _adaptive_pvec(self, p_opt, pmin, pmax): def _adaptive_pvec(self, p_opt, pmin, pmax):
@ -589,16 +602,9 @@ class Profile(object):
return self._adaptive_pvec(p_opt, pmin, pmax) return self._adaptive_pvec(p_opt, pmin, pmax)
return np.linspace(self.pmin, self.pmax, self.n) return np.linspace(self.pmin, self.pmax, self.n)
def _search_pminmax(self, phatfree0, p_minmax0, p_opt, direction): def _update_p_opt(self, p_minmax_opt, dp, Lmax, p_minmax, j):
phatfree = phatfree0.copy()
sign = -1 if direction == 'min' else 1
dp = np.maximum(sign*(p_minmax0 - p_opt) / 40, 0.01) * 10
Lmax, phatfree = self._profile_optimum(phatfree, p_opt)
p_minmax_opt = p_minmax0
for j in range(51):
p_minmax = p_opt + sign * dp
Lmax, phatfree = self._profile_optimum(phatfree, p_minmax)
# print((dp, p_minmax, p_minmax_opt, Lmax)) # print((dp, p_minmax, p_minmax_opt, Lmax))
converged = False
if np.isnan(Lmax): if np.isnan(Lmax):
dp *= 0.33 dp *= 0.33
elif Lmax < self.alpha_cross_level - self.alpha_Lrange * 5 * (j + 1): elif Lmax < self.alpha_cross_level - self.alpha_Lrange * 5 * (j + 1):
@ -606,12 +612,30 @@ class Profile(object):
dp *= 0.33 dp *= 0.33
elif Lmax < self.alpha_cross_level: elif Lmax < self.alpha_cross_level:
p_minmax_opt = p_minmax p_minmax_opt = p_minmax
break converged = True
else: else:
dp *= 1.67 dp *= 1.67
else: return p_minmax_opt, dp, converged
msg = 'Exceeded max iterations. (p_{0}0={1}, p_{0}={2}, p={3})'
warnings.warn(msg.format(direction, p_minmax0, p_minmax_opt, def _search_p_min_max(self, phatfree0, p_minmax0, p_opt, direction):
phatfree = phatfree0.copy()
sign = dict(min=-1, max=1)[direction]
dp = np.maximum(sign*(p_minmax0 - p_opt) / 40, 0.01) * 10
Lmax, phatfree = self._profile_optimum(phatfree, p_opt)
p_minmax_opt = p_minmax0
j = 0
converged = False
# for j in range(51):
while j < 51 and not converged:
j += 1
p_minmax = p_opt + sign * dp
Lmax, phatfree = self._profile_optimum(phatfree, p_minmax)
p_minmax_opt, dp, converged = self._update_p_opt(p_minmax_opt, dp,
Lmax, p_minmax, j)
_assert_warn(j < 50, 'Exceeded max iterations. '
'(p_{0}0={1}, p_{0}={2}, p={3})'.format(direction,
p_minmax0,
p_minmax_opt,
p_opt)) p_opt))
# print('search_pmin iterations={}'.format(j)) # print('search_pmin iterations={}'.format(j))
return p_minmax_opt return p_minmax_opt
@ -689,8 +713,24 @@ class Profile(object):
def plot_all_profiles(phats, plot=None): def plot_all_profiles(phats, plot=None):
if plot is not None: def _remove_title_or_ylabel(plt, n, j):
plt = plot if j != 0:
plt.title('')
if j != n // 2:
plt.ylabel('')
def _profile(phats, k):
profile_phat_k = Profile(phats, i=k)
m = 0
while hasattr(profile_phat_k, 'best_par') and m < 7:
phats.fit(*profile_phat_k.best_par)
profile_phat_k = Profile(phats, i=k)
m += 1
return profile_phat_k
if plot is None:
plot = plt
if phats.par_fix: if phats.par_fix:
indices = phats.i_notfixed indices = phats.i_notfixed
@ -699,20 +739,12 @@ def plot_all_profiles(phats, plot=None):
n = len(indices) n = len(indices)
for j, k in enumerate(indices): for j, k in enumerate(indices):
plt.subplot(n, 1, j+1) plt.subplot(n, 1, j+1)
profile_phat_k = Profile(phats, i=k) profile_phat_k = _profile(phats, k)
m = 0
while hasattr(profile_phat_k, 'best_par') and m < 7:
phats.fit(*profile_phat_k.best_par)
profile_phat_k = Profile(phats, i=k)
m += 1
profile_phat_k.plot() profile_phat_k.plot()
if j != 0: _remove_title_or_ylabel(plt, n, j)
plt.title('') plot.subplots_adjust(hspace=0.5)
if j != n//2:
plt.ylabel('')
plt.subplots_adjust(hspace=0.5)
par_txt = ('{:1.2g}, '*len(phats.par))[:-2].format(*phats.par) par_txt = ('{:1.2g}, '*len(phats.par))[:-2].format(*phats.par)
plt.suptitle('phat = [{}] (fit metod: {})'.format(par_txt, phats.method)) plot.suptitle('phat = [{}] (fit metod: {})'.format(par_txt, phats.method))
return phats return phats
@ -809,21 +841,10 @@ class ProfileQuantile(Profile):
prb = exp(self.log_sf) prb = exp(self.log_sf)
return self.fit_dist.dist.isf(prb, *mphat) return self.fit_dist.dist.isf(prb, *mphat)
def _get_variance(self): def _set_plot_labels(self, method):
i_notfixed = self.i_notfixed title = '{:s} quantile'.format(self.fit_dist.dist.name)
phatv = self._par super(ProfileQuantile, self)._set_plot_labels(method, title,
gradfun = nd.Gradient(self._myinvfun) xlabel='x')
drl = gradfun(phatv[self.i_notfixed])
pcov = self.fit_dist.par_cov[i_notfixed, :][:, i_notfixed]
pvar = np.sum(np.dot(drl, pcov) * drl)
return pvar
def _set_plot_labels(self, fit_dist, method):
super(ProfileQuantile, self)._set_plot_labels(fit_dist, method)
percent = 100 * (1.0 - self.alpha)
self.title = '{:g}% CI for {:s} quantile'.format(percent,
fit_dist.dist.name)
self.xlabel = 'x'
class ProfileProbability(Profile): class ProfileProbability(Profile):
@ -911,27 +932,17 @@ class ProfileProbability(Profile):
fix_par = self.link(self.x, fixed_log_sf, par, self.i_fixed) fix_par = self.link(self.x, fixed_log_sf, par, self.i_fixed)
return fix_par return fix_par
def _myprbfun(self, phatnotfixed): def _myinvfun(self, phatnotfixed):
"""_myprbfun"""
mphat = self._par.copy() mphat = self._par.copy()
mphat[self.i_notfixed] = phatnotfixed mphat[self.i_notfixed] = phatnotfixed
logsf = self.fit_dist.dist.logsf(self.x, *mphat) logsf = self.fit_dist.dist.logsf(self.x, *mphat)
return np.where(np.isfinite(logsf), logsf, np.nan) return np.where(np.isfinite(logsf), logsf, np.nan)
def _get_variance(self): def _set_plot_labels(self, method):
i_notfixed = self.i_notfixed title = '{:s} probability'.format(self.fit_dist.dist.name)
phatv = self._par xlabel = 'log(sf)'
gradfun = nd.Gradient(self._myprbfun) super(ProfileProbability, self)._set_plot_labels(method, title, xlabel)
drl = gradfun(phatv[self.i_notfixed])
pcov = self.fit_dist.par_cov[i_notfixed, :][:, i_notfixed]
pvar = np.sum(np.dot(drl, pcov) * drl)
return pvar
def _set_plot_labels(self, fit_dist, method):
super(ProfileProbability, self)._set_plot_labels(fit_dist, method)
percent = 100 * (1.0 - self.alpha)
self.title = '{:g}% CI for {:s} probability'.format(percent,
fit_dist.dist.name)
self.xlabel = 'log(sf)'
class FitDistribution(rv_frozen): class FitDistribution(rv_frozen):
@ -1463,7 +1474,7 @@ class FitDistribution(rv_frozen):
phatvstr) phatvstr)
subtxt = ('Fit method: {0:s}, Fit p-value: {1:2.2f} {2:s}, ' + subtxt = ('Fit method: {0:s}, Fit p-value: {1:2.2f} {2:s}, ' +
'phat=[{3:s}], {4:s}') 'phat=[{3:s}], {4:s}')
par_txt = '{:1.2g}, ' * len(self.par)[:-2].format(*self.par) par_txt = ('{:1.2g}, ' * len(self.par))[:-2].format(*self.par)
try: try:
LL_txt = 'Lps_max={:2.2g}, Ll_max={:2.2g}'.format(self.LPSmax, LL_txt = 'Lps_max={:2.2g}, Ll_max={:2.2g}'.format(self.LPSmax,
self.LLmax) self.LLmax)
@ -1691,8 +1702,8 @@ def test1():
phat = FitDistribution(dist, R, floc=0.5, method='ml') phat = FitDistribution(dist, R, floc=0.5, method='ml')
phats = FitDistribution(dist, R, floc=0.5, method='mps') phats = FitDistribution(dist, R, floc=0.5, method='mps')
# import matplotlib.pyplot as plt # import matplotlib.pyplot as plt
# plt.figure(0) plt.figure(0)
# plot_all_profiles(phat, plot=plt) plot_all_profiles(phat, plot=plt)
plt.figure(1) plt.figure(1)
phats.plotfitsummary() phats.plotfitsummary()
@ -1727,5 +1738,5 @@ def test1():
if __name__ == '__main__': if __name__ == '__main__':
test1() # test1()
# test_doctstrings() test_doctstrings()

Loading…
Cancel
Save