def test_mle_fit_psycho(self): expected = { 'weibull50': (np.array([0.0034045, 3.9029162, .1119576]), -334.1149693046583), 'weibull': (np.array([0.00316341, 1.72552866, 0.1032307]), -261.235178611311), 'erf_psycho': (np.array([-9.78747259, 10., 0.15967605]), -193.0509031440323), 'erf_psycho_2gammas': (np.array([-11.45463779, 9.9999999, 0.24117732, 0.0270835]), -147.02380025592902) } for model in self.test_data.keys(): pars, L = psy.mle_fit_psycho(self.test_data[model], P_model=model, nfits=10) expected_pars, expected_L = expected[model] self.assertTrue(np.allclose(expected_pars, pars, atol=1e-3), f'unexpected pars for {model}') self.assertTrue(np.isclose(expected_L, L, atol=1e-3), f'unexpected likelihood for {model}') # Test on of the models with function pars params = { 'parmin': np.array([-5., 10., 0.]), 'parmax': np.array([5., 15., .1]), 'parstart': np.array([0., 11., 0.1]), 'nfits': 5 } model = 'erf_psycho' pars, L = psy.mle_fit_psycho(self.test_data[model], P_model=model, **params) expected = [-5, 15, 0.1] self.assertTrue(np.allclose(expected, pars, rtol=.01), f'unexpected pars for {model}') self.assertTrue(np.isclose(-195.55603, L, atol=1e-5), f'unexpected likelihood for {model}')
def fit_psychfunc(df): choicedat = df.groupby('signedContrast').agg({ 'trial': 'max', 'choice2': 'mean' }).reset_index() pars, L = psy.mle_fit_psycho( choicedat.values.transpose(), P_model='erf_psycho_2gammas', parstart=np.array( [choicedat['signedContrast'].mean(), 20., 0.05, 0.05]), parmin=np.array([choicedat['signedContrast'].min(), 0., 0., 0.]), parmax=np.array([choicedat['signedContrast'].max(), 100., 1, 1])) df2 = { 'bias': pars[0], 'threshold': pars[1], 'lapselow': pars[2], 'lapsehigh': pars[3] } return pd.DataFrame(df2, index=[0])
#bias = -10. #threshold = 20. #gamma = .1 # fake experimental data given those parameters #ratio = psychofit.erf_psycho([bias, threshold, gamma],edgeMid) # In[10]: cc = edgeMid # contrasts nn = histTact + histDCS # number of trials at each contrast #ntrials = 40 #nn = ntrials*np.ones((np.shape(ratio))) pp = ratio # proportion "rightward" pars, L = psychofit.mle_fit_psycho(np.vstack((cc, nn, pp)), 'erf_psycho', np.array([-100, 10., 0.5]), np.array([-200., 0.1, 0.]), np.array([20., 500., 1]), 50) plt.figure(figsize=(10, 10)) plt.plot(cc, pp, 'ko', mfc='k', markersize=15) plt.plot( np.arange(-550, 150, 0.1), psychofit.erf_psycho(pars, np.arange(-550, 150, 0.1)), '-b', linewidth=7, label=' threshold = {:2.0f} \n slope = {:2.0f} \n lapse = {:.01f}'.format( *pars)) plt.legend() #plt.xlim([-100,100]) plt.ylim([-0.1, 1.1]) plt.plot((0, 0), (0, 1), 'k:')
def plot_psychometric(df, color='black', ax=None, **kwargs): """ Plots psychometric data for a given DataFrame of behavioural trials If the data contains more than six different contrasts (or > three per side) the data are fit with an erf function. The x-axis is percent contrast and the y-axis is the proportion of 'rightward choices', i.e. trials where the subject turned the wheel clockwise to threshold. Example: df = alf.load_behaviour('2018-09-11_1_Mouse1', r'\\server\SubjectData') plot_psychometric(df) Args: df (DataFrame): DataFrame constructed from an ALF trials object. ax (Axes): Axes to plot to. If None, a new figure is created. Returns: ax (Axes): The plot axes """ if len(df['signedContrast'].unique()) > 4: df2 = df.groupby(['signedContrast']).agg({ 'choice': 'count', 'choice2': 'mean' }).reset_index() df2.rename(columns={ "choice2": "fraction", "choice": "ntrials" }, inplace=True) pars, L = psy.mle_fit_psycho( df2.transpose().values, # extract the data from the df P_model='erf_psycho_2gammas', parstart=np.array([df2['signedContrast'].mean(), 20., 0.05, 0.05]), parmin=np.array([df2['signedContrast'].min(), 0., 0., 0.]), parmax=np.array([df2['signedContrast'].max(), 100., 1, 1])) sns.lineplot(np.arange(-100, 100), psy.erf_psycho_2gammas(pars, np.arange(-100, 100)), color=color, ax=ax) # plot datapoints on top sns.lineplot(x='signedContrast', y='choice2', err_style="bars", linewidth=0, linestyle='None', mew=0.5, marker='.', ci=68, data=df, color=color, ax=ax) # Reduce the clutter ax.set_xticks([-100, -50, 0, 50, 100]) ax.set_xticklabels(['-100', '-50', '0', '50', '100']) ax.set_yticks([0, .5, 1]) # Set the limits ax.set_xlim([-110, 110]) ax.set_ylim([-0.03, 1.03]) ax.set_xlabel('Contrast (%)') return ax
def make(self, key): key_psy = key.copy() n_trials, n_correct_trials = (behavior.TrialSet & key).fetch1( 'n_trials', 'n_correct_trials') key_psy['performance'] = n_correct_trials / n_trials trials = behavior.TrialSet.Trial & key trials_rt = trials.proj(rt='trial_response_time-trial_stim_on_time') contrasts_left = np.unique( trials.fetch('trial_stim_contrast_left'))[1:] # discard contrast 0 contrasts_right = np.unique(trials.fetch('trial_stim_contrast_right'))[ 1:] # discard contrast 0 trials_no_contrast = trials & 'trial_stim_contrast_right=0' & 'trial_stim_contrast_left=0' n_right_trials_stim_left = [len(trials & 'trial_stim_contrast_left={}'.format(contrast) & 'trial_response_choice="CCW"') \ for contrast in contrasts_left] n_trials_stim_left = [len(trials & 'trial_stim_contrast_left={}'.format(contrast)) \ for contrast in contrasts_left] n_right_trials_stim_right = [len(trials & 'trial_stim_contrast_right={}'.format(contrast) & 'trial_response_choice="CCW"') \ for contrast in contrasts_right] n_trials_stim_right = [len(trials & 'trial_stim_contrast_right={}'.format(contrast)) \ for contrast in contrasts_right] p_right_stim_left = np.divide(n_right_trials_stim_left, n_trials_stim_left) p_right_stim_right = np.divide(n_right_trials_stim_right, n_trials_stim_right) rt_stim_left = [(trials_rt & (trials & 'trial_stim_contrast_left={}'.format(contrast))).fetch('rt').mean() \ for contrast in contrasts_left] rt_stim_right = [(trials_rt & (trials & 'trial_stim_contrast_right={}'.format(contrast))).fetch('rt').mean() \ for contrast in contrasts_right] trials_no_contrast = trials & 'trial_stim_contrast_right=0' & 'trial_stim_contrast_left=0' if trials_no_contrast: key_psy['contrasts'] = np.hstack( [np.negative(contrasts_left[::-1]), 0, contrasts_right]) * 100 p_right_no_contrast = len(trials_no_contrast & 'trial_response_choice="CCW"') / len( trials_no_contrast) key_psy['prob_choose_right'] = np.hstack([ p_right_stim_left[::-1], p_right_no_contrast, p_right_stim_right ]) n_total_trials = np.hstack([ n_trials_stim_left, len(trials_no_contrast), n_trials_stim_right ]) rt_no_contrast = (trials_rt & trials_no_contrast).fetch('rt').mean() key_psy['rt'] = np.hstack( [rt_stim_left, rt_no_contrast, rt_stim_right]) else: key_psy['contrasts'] = np.hstack( [np.negative(contrasts_left[::-1]), contrasts_right]) * 100 key_psy['prob_choose_right'] = np.hstack([ (p_right_stim_left[::-1]), p_right_stim_right ]) n_total_trials = np.hstack( [n_trials_stim_left, n_trials_stim_right]) key_psy['rt'] = np.hstack([rt_stim_left, rt_stim_right]) pars, L = psy.mle_fit_psycho(np.vstack([key_psy['contrasts'], n_total_trials, key_psy['prob_choose_right']]), \ P_model='erf_psycho_2gammas', \ parstart=np.array([key_psy['contrasts'].mean(), 20., 0.05, 0.05]), \ parmin=np.array([key_psy['contrasts'].mean(), 0., 0., 0.]), \ parmax=np.array([key_psy['contrasts'].mean(), 100., 1, 1])) print(pars) key_psy['bias'] = pars[0] key_psy['threshold'] = pars[1] key_psy['lapse_low'] = pars[2] key_psy['lapse_high'] = pars[3] self.insert1(key_psy)