def get_errors(self, quiet=False): """ Compute the errors on the parameters using the profile likelihood method. :return: a dictionary containing the asymmetric errors for each parameter. """ # Check that the user performed a fit first assert (self._current_minimum is not None ), "You have to run the .fit method before calling errors." errors = self._minimizer.get_errors() # Set the parameters back to the best fit value self.restore_best_fit() # Print a table with the errors parameter_names = list(self._free_parameters.keys()) best_fit_values = [ x.value for x in list(self._free_parameters.values()) ] negative_errors = [errors[k][0] for k in parameter_names] positive_errors = [errors[k][1] for k in parameter_names] units = [par.unit for par in list(self._free_parameters.values())] results_table = ResultsTable(parameter_names, best_fit_values, negative_errors, positive_errors, units) if not quiet: results_table.display() return results_table.frame
def fit(self, quiet=False, compute_covariance=True, n_samples=5000): """ Perform a fit of the current likelihood model on the datasets :param quiet: If True, print the results (default), otherwise do not print anything :param compute_covariance:If True (default), compute and display the errors and the correlation matrix. :return: a dictionary with the results on the parameters, and the values of the likelihood at the minimum for each dataset and the total one. """ # Update the list of free parameters, to be safe against changes the user might do between # the creation of this class and the calling of this method self._update_free_parameters() # Empty the call recorder self._record_calls = {} self._ncalls = 0 # Check if we have free parameters, otherwise simply return the value of the log like if len(self._free_parameters) == 0: custom_warnings.warn( "There is no free parameter in the current model", RuntimeWarning) # Create the minimizer anyway because it will be needed by the following code self._minimizer = self._get_minimizer(self.minus_log_like_profile, self._free_parameters) # Store the "minimum", which is just the current value self._current_minimum = float(self.minus_log_like_profile()) else: # Instance the minimizer # If we have a global minimizer, use that first (with no covariance) if isinstance(self._minimizer_type, minimization.GlobalMinimization): # Do global minimization first global_minimizer = self._get_minimizer( self.minus_log_like_profile, self._free_parameters) xs, global_log_likelihood_minimum = global_minimizer.minimize( compute_covar=False) # Gather global results paths = [] values = [] errors = [] units = [] for par in list(self._free_parameters.values()): paths.append(par.path) values.append(par.value) errors.append(0) units.append(par.unit) global_results = ResultsTable(paths, values, errors, errors, units) if not quiet: print( "\n\nResults after global minimizer (before secondary optimization):" ) global_results.display() print("\nTotal log-likelihood minimum: %.3f\n" % global_log_likelihood_minimum) # Now set up secondary minimizer self._minimizer = self._minimizer_type.get_second_minimization_instance( self.minus_log_like_profile, self._free_parameters) else: # Only local minimization to be performed self._minimizer = self._get_minimizer( self.minus_log_like_profile, self._free_parameters) # Perform the fit, but first flush stdout (so if we have verbose=True the messages there will follow # what is already in the buffer) sys.stdout.flush() xs, log_likelihood_minimum = self._minimizer.minimize( compute_covar=compute_covariance) if log_likelihood_minimum == minimization.FIT_FAILED: raise FitFailed("The fit failed to converge.") # Store the current minimum for the -log likelihood self._current_minimum = float(log_likelihood_minimum) # First restore best fit (to make sure we compute the likelihood at the right point in the following) self._minimizer.restore_best_fit() # Now collect the values for the likelihood for the various datasets # Fill the dictionary with the values of the -log likelihood (dataset by dataset) minus_log_likelihood_values = collections.OrderedDict() # Keep track of the total for a double check total = 0 # sum up the total number of data points total_number_of_data_points = 0 for dataset in list(self._data_list.values()): ml = dataset.inner_fit() * (-1) minus_log_likelihood_values[dataset.name] = ml total += ml total_number_of_data_points += dataset.get_number_of_data_points() assert ( total == self._current_minimum ), "Current minimum stored after fit and current do not correspond!" # compute additional statistics measures statistical_measures = collections.OrderedDict() # for MLE we can only compute the AIC and BIC as they # are point estimates statistical_measures["AIC"] = aic(-total, len(self._free_parameters), total_number_of_data_points) statistical_measures["BIC"] = bic(-total, len(self._free_parameters), total_number_of_data_points) # Now instance an analysis results class self._analysis_results = MLEResults( self.likelihood_model, self._minimizer.covariance_matrix, minus_log_likelihood_values, statistical_measures=statistical_measures, n_samples=n_samples, ) # Show the results if not quiet: self._analysis_results.display() return ( self._analysis_results.get_data_frame(), self._analysis_results.get_statistic_frame(), )
def _get_results_table(self, error_type, cl, covariance=None): if error_type == "equal tail": errors_gatherer = RandomVariates.equal_tail_interval elif error_type == "hpd": errors_gatherer = RandomVariates.highest_posterior_density_interval elif error_type == "covariance": assert covariance is not None, "If you use error_type='covariance' you have to provide a cov. matrix" errors_gatherer = None else: raise ValueError( "error_type must be either 'equal tail' or 'hpd'. Got %s" % error_type) # Build the data frame parameter_paths = [] values = [] negative_errors = [] positive_errors = [] units_dict = [] for i, this_par in enumerate(self._free_parameters.values()): parameter_paths.append(this_par.path) this_phys_q = self.get_variates(parameter_paths[-1]) values.append(this_phys_q.value) units_dict.append(this_par.unit) if error_type != "covariance": low_bound, hi_bound = errors_gatherer(this_phys_q, cl) negative_errors.append(low_bound - values[-1]) positive_errors.append(hi_bound - values[-1]) else: std_dev = np.sqrt(covariance[i, i]) if this_par.has_transformation(): best_fit_internal = this_par.transformation.forward( values[-1]) _, neg_error = this_par.internal_to_external_delta( best_fit_internal, -std_dev) negative_errors.append(neg_error) _, pos_error = this_par.internal_to_external_delta( best_fit_internal, std_dev) positive_errors.append(pos_error) else: negative_errors.append(-std_dev) positive_errors.append(std_dev) results_table = ResultsTable(parameter_paths, values, negative_errors, positive_errors, units_dict) return results_table