def compute_precision(self, errors, remove_chance_level=True, correct_orientation=False, use_wrong_precision=True): ''' Compute the precision (1./circ_std**2). Remove the chance level if desired. ''' # if correct_orientation: # # Correct for the fact that bars are modelled in [0, pi] and not [0, 2pi] # errors = errors.copy()*2.0 # Circular standard deviation estimate error_std_dev_error = utils.angle_circular_std_dev(errors) # Precision if use_wrong_precision: precision = 1./error_std_dev_error else: precision = 1./error_std_dev_error**2. if remove_chance_level: # Remove the chance level precision -= utils.compute_precision_chance(errors.size) if correct_orientation: # The obtained precision is for half angles, correct it precision *= 2. return precision
def fit(responses, target_angle, nontarget_angles=np.array([[]]), kappa=None, initialisation_method='mixed', nb_initialisations=5, debug=False, force_random_less_than=None): ''' Return maximum likelihood values for a different mixture model, with: - 1 target Von Mises component - K nontarget Von Mises components - 1 circular uniform random component - Kappa fixed and set by Fisher Information. Should be provided Inputs in radian, in the -pi:pi range. - responses: Nx1 - target_angle: Nx1 - nontarget_angles NxK Modified from Bays et al 2009 ''' # Clean inputs not_nan_indices = ~np.isnan(responses) responses = responses[not_nan_indices] target_angle = target_angle[not_nan_indices] if nontarget_angles.size > 0: nontarget_angles = nontarget_angles[:, ~np.all(np.isnan(nontarget_angles), axis=0)] nontarget_angles = nontarget_angles[not_nan_indices] if kappa is None: # No Kappa provided, let's use the one from the Circular Std Dev. (Bound to fail for multiple items) kappa = utils.stddev_to_kappa_single(utils.angle_circular_std_dev(responses)) N = float(np.sum(~np.isnan(responses))) K = float(nontarget_angles.shape[1]) max_iter = 1000 epsilon = 1e-5 # Initial parameters initial_parameters_list = initialise_parameters(responses.size, K, initialisation_method, nb_initialisations) overall_LL = -np.inf LL = np.nan initial_i = 0 best_mixt_target, best_mixt_random, best_mixt_nontargets = (np.nan, np.nan, np.nan) for (mixt_target, mixt_random, mixt_nontargets, resp_ik) in initial_parameters_list: if debug: print "New initialisation point: ", (mixt_target, mixt_random, mixt_nontargets) old_LL = -np.inf i = 0 converged = False # Precompute some matrices error_to_target = wrap(target_angle - responses) error_to_nontargets = wrap(nontarget_angles - responses[:, np.newaxis]) # EM loop while i < max_iter and not converged: # E-step if debug: print "E", i, LL, kappa, mixt_target, mixt_nontargets, mixt_random resp_ik[:, 0] = mixt_target * vonmisespdf(error_to_target, 0.0, kappa) resp_r = mixt_random/(2.*np.pi) if K > 0: resp_ik[:, 1:] = mixt_nontargets*vonmisespdf(error_to_nontargets, 0.0, kappa) W = np.sum(resp_ik, axis=1) + resp_r # Compute likelihood LL = np.nansum(np.log(W)) dLL = LL - old_LL old_LL = LL if (np.abs(dLL) < epsilon): converged = True break # M-step mixt_target = np.nansum(resp_ik[:, 0]/W)/N mixt_nontargets = np.nansum(resp_ik[:, 1:]/W[:, np.newaxis], axis=0)/N mixt_random = np.nansum(resp_r/W)/N if force_random_less_than is not None: # Hacky, force mixt_random to be below this value mixt_random = np.min((mixt_random, force_random_less_than)) if debug: print "M", i, LL, mixt_target, mixt_nontargets, mixt_random i += 1 # if not converged: # if debug: # print "Warning, Em_circularmixture.fit() did not converge before ", max_iter, "iterations" # kappa = np.nan # mixt_target = np.nan # mixt_nontargets = np.nan # mixt_random = np.nan # rw = np.nan if LL > overall_LL and np.isfinite(LL): if debug: print "New best!", initial_i, overall_LL, LL overall_LL = LL (best_mixt_target, best_mixt_nontargets, best_mixt_random) = (mixt_target, mixt_nontargets, mixt_random) initial_i += 1 result_dict = dict(kappa=kappa, mixt_target=best_mixt_target, mixt_nontargets=best_mixt_nontargets, mixt_random=best_mixt_random, train_LL=overall_LL) # Compute BIC and AIC scores result_dict['bic'] = bic(result_dict, N) result_dict['aic'] = aic(result_dict) return result_dict