def compute_biasshift(x): # print(x.describe()) shift = x.loc[x['probabilityLeft'] == 80, 'bias'].item() - x.loc[x['probabilityLeft'] == 20, 'bias'].item() xax = np.arange(-100, 100) y_80 = psy.erf_psycho_2gammas([ x.loc[x['probabilityLeft'] == 80, 'bias'].item(), x.loc[x['probabilityLeft'] == 80, 'threshold'].item(), x.loc[x['probabilityLeft'] == 80, 'lapselow'].item(), x.loc[x['probabilityLeft'] == 80, 'lapsehigh'].item() ], xax) y_20 = psy.erf_psycho_2gammas([ x.loc[x['probabilityLeft'] == 20, 'bias'].item(), x.loc[x['probabilityLeft'] == 20, 'threshold'].item(), x.loc[x['probabilityLeft'] == 20, 'lapselow'].item(), x.loc[x['probabilityLeft'] == 20, 'lapsehigh'].item() ], xax) yshift = 100 * (y_20[xax == 0] - y_80[xax == 0]) yshift = 0.5 + (y_20[xax == 0] - y_80[xax == 0]) / 2 # from Nick Roy, 23 April return yshift
def plot_psychometric(trials, ax, **kwargs): from ibl_pipeline.utils import psychofit as psy if trials['signed_contrast'].max() <= 1: trials['signed_contrast'] = trials['signed_contrast'] * 100 stim_levels = np.sort(trials['signed_contrast'].unique()) pars = fit_psychfunc( stim_levels, trials.groupby('signed_contrast').size(), trials.groupby('signed_contrast').mean()['right_choice']) # plot psychfunc sns.lineplot(x=np.arange(-27, 27), y=psy.erf_psycho_2gammas(pars, np.arange(-27, 27)), ax=ax, **kwargs) # plot psychfunc: -100, +100 sns.lineplot(x=np.arange(-36, -31), y=psy.erf_psycho_2gammas(pars, np.arange(-103, -98)), ax=ax, **kwargs) sns.lineplot(x=np.arange(31, 36), y=psy.erf_psycho_2gammas(pars, np.arange(98, 103)), ax=ax, **kwargs) # now break the x-axis trials['signed_contrast'].replace(-100, -35) trials['signed_contrast'].replace(100, 35) # plot datapoints with errorbars on top sns.lineplot(x=trials['signed_contrast'], y=trials['right_choice'], ax=ax, **{ **{ 'err_style': "bars", 'linewidth': 0, 'linestyle': 'None', 'mew': 0.5, 'marker': 'o', 'ci': 68 }, **kwargs }) ax.set(xticks=[-35, -25, -12.5, 0, 12.5, 25, 35], xlim=[-40, 40], ylim=[0, 1.02], yticks=[0, 0.25, 0.5, 0.75, 1], yticklabels=['0', '25', '50', '75', '100'], ylabel='Right choices', xlabel='Contrast (%)') ax.set_xticklabels(['-100', '-25', '-12.5', '0', '12.5', '25', '100'], size='small') break_xaxis()
def pars2shift(pars, choicevar, outcomevar): pars2 = pd.DataFrame([]) xvec = behav.signed_contrast.unique() for index, group in pars.groupby(['institution_code', 'subject_nickname', 'task', choicevar, outcomevar]): # expand yvec = psy.erf_psycho_2gammas([group.bias.item(), group.threshold.item(), group.lapselow.item(), group.lapsehigh.item()], xvec) group2 = group.loc[group.index.repeat( len(yvec))].reset_index(drop=True).copy() group2['signed_contrast'] = xvec group2['choice'] = 100 * yvec # add this pars2 = pars2.append(group2) # only pick psychometric functions that were fit on a reasonable number of trials... pars2 = pars2[(pars2.ntrials > 50) & (pars2.signed_contrast == 0)] # compute history-dependent bias shift pars3 = pd.pivot_table(pars2, values='choice', index=['institution_code', 'subject_nickname', 'task', outcomevar], columns=[choicevar]).reset_index() pars3['bias_shift'] = pars3.right - pars3.left pars4 = pd.pivot_table(pars3, values='bias_shift', index=['institution_code', 'subject_nickname', 'task'], columns=[outcomevar]).reset_index() print(pars4.describe()) return pars4
def pars2choicefract(group): group['choicefract'] = psy.erf_psycho_2gammas([ group.bias.item(), group.threshold.item(), group.lapselow.item(), group.lapsehigh.item() ], 0) * 100 return group
def compute_biasshift_postcorrect(x): xax = np.arange(-100, 100) x_right = x.loc[np.isclose(x['previous_choice'], 1) & np.isclose(x['previous_outcome'], 1)] x_left = x.loc[np.isclose(x['previous_choice'], -1) & np.isclose(x['previous_outcome'], 1)] y_right = psy.erf_psycho_2gammas([ x_right['bias'].item(), x_right['threshold'].item(), x_right['lapselow'].item(), x_right['lapsehigh'].item() ], xax) y_left = psy.erf_psycho_2gammas([ x_left['bias'].item(), x_left['threshold'].item(), x_left['lapselow'].item(), x_left['lapsehigh'].item() ], xax) shift_postcorrect = (y_right[xax == 0] - y_left[xax == 0]) return shift_postcorrect
def compute_biasshift(x): # shift in the intercept parameter, along x-axis shift = x.loc[x['probabilityLeft'] == 80, 'bias'].item() - x.loc[x['probabilityLeft'] == 20, 'bias'].item() # also read out the y-shift, in choice probability xax = np.arange(-100, 100) y_80 = psy.erf_psycho_2gammas([x.loc[x['probabilityLeft'] == 80, 'bias'].item(), x.loc[x['probabilityLeft'] == 80, 'threshold'].item(), x.loc[x['probabilityLeft'] == 80, 'lapselow'].item(), x.loc[x['probabilityLeft'] == 80, 'lapsehigh'].item()], xax) y_20 = psy.erf_psycho_2gammas([x.loc[x['probabilityLeft'] == 20, 'bias'].item(), x.loc[x['probabilityLeft'] == 20, 'threshold'].item(), x.loc[x['probabilityLeft'] == 20, 'lapselow'].item(), x.loc[x['probabilityLeft'] == 20, 'lapsehigh'].item()], xax) print(y_20[xax == 0]) print(y_80[xax == 0]) yshift = 0.5 + (y_20[xax == 0] - y_80[xax == 0]) / 2 # from Nick Roy, 23 April return yshift
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 plot_psychometric(x, y, subj, **kwargs): # summary stats - average psychfunc over observers df = pd.DataFrame({'signed_contrast': x, 'choice': y, 'choice2': y, 'subject_nickname': subj}) df2 = df.groupby(['signed_contrast', 'subject_nickname']).agg( {'choice2': 'count', 'choice': 'mean'}).reset_index() df2.rename(columns={"choice2": "ntrials", "choice": "fraction"}, inplace=True) df2 = df2.groupby(['signed_contrast']).mean().reset_index() df2 = df2[['signed_contrast', 'ntrials', 'fraction']] # only 'break' the x-axis and remove 50% contrast when 0% is present # print(df2.signed_contrast.unique()) if 0. in df2.signed_contrast.values: brokenXaxis = True else: brokenXaxis = False # fit psychfunc pars, L = psy.mle_fit_psycho(df2.transpose().values, # extract the data from the df P_model='erf_psycho_2gammas', parstart=np.array( [0, 20., 0.05, 0.05]), parmin=np.array( [df2['signed_contrast'].min(), 5, 0., 0.]), parmax=np.array([df2['signed_contrast'].max(), 40., 1, 1])) if brokenXaxis: # plot psychfunc g = sns.lineplot(np.arange(-29, 29), psy.erf_psycho_2gammas(pars, np.arange(-29, 29)), **kwargs) # plot psychfunc: -100, +100 sns.lineplot(np.arange(-37, -32), psy.erf_psycho_2gammas(pars, np.arange(-103, -98)), **kwargs) sns.lineplot(np.arange(32, 37), psy.erf_psycho_2gammas(pars, np.arange(98, 103)), **kwargs) # now break the x-axis # if 100 in df.signed_contrast.values and not 50 in # df.signed_contrast.values: df['signed_contrast'] = df['signed_contrast'].replace(-100, -35) df['signed_contrast'] = df['signed_contrast'].replace(100, 35) else: # plot psychfunc g = sns.lineplot(np.arange(-103, 103), psy.erf_psycho_2gammas(pars, np.arange(-103, 103)), **kwargs) df3 = df.groupby(['signed_contrast', 'subject_nickname']).agg( {'choice2': 'count', 'choice': 'mean'}).reset_index() # plot datapoints with errorbars on top if df['subject_nickname'].nunique() > 1: sns.lineplot(df3['signed_contrast'], df3['choice'], err_style="bars", linewidth=0, linestyle='None', mew=0.5, marker='o', ci=68, **kwargs) if brokenXaxis: g.set_xticks([-35, -25, -12.5, 0, 12.5, 25, 35]) g.set_xticklabels(['-100', '-25', '-12.5', '0', '12.5', '25', '100'], size='small', rotation=60) g.set_xlim([-40, 40]) else: g.set_xticks([-100, -50, 0, 50, 100]) g.set_xticklabels(['-100', '-50', '0', '50', '100'], size='small', rotation=60) g.set_xlim([-110, 110]) g.set_ylim([0, 1.02]) g.set_yticks([0, 0.25, 0.5, 0.75, 1]) g.set_yticklabels(['0', '25', '50', '75', '100'])
def plot_from_spots(spotlist, psychoSpotData, spots, color, ax, plotType='psycho', bs=False): """ Function to plot three psychometrics on a single plot, with the dots that they were generated with. usually I'll use this with a group of left spots, a group of right spots and a group of control spots. Inputs: spotlists: an array containing the spots that you want to group together to plot the psychometric for. a n by 2 numpy array that gives [[x1,y1],[x2,y2],[xn,yn]] psychoSpotData: a list of len(numSpots) each with a list lenght 4 containing 1, an array of signed contrasts, 2, a list of number of presentations for that contrast, 3, a list of arrays containing a bool for if choice was CCW (list len num contrast, array len num presentations) 4, a list of the same form as above with the reaction times spots: a dataframe of length num spots, column 0: laserPosX, column 1: laserPosY, column3, count color: the color for the line you want to plot, string eg 'b' ax: the matplotlib axis to plot onto plotType: defualt is psycho for plotting psychometric curves, other option is 'chrono' """ if plotType == 'psycho': psycho = [[], [0 for i in range(len(psychoSpotData[0][1]))], [np.array([])] * len(psychoSpotData[0][1])] controlSpotPsycho = [[], [ 0 for i in range(len(psychoSpotData[0][1])) ], [np.nan for i in range(len(psychoSpotData[0][1]))]] tempPsych = [np.array([])] * len(psychoSpotData[0][1]) for i in range(len(spots)): spotX = spots.iloc[i, [0]][0] spotY = spots.iloc[i, [1]][0] for spot in range(len(spotlist)): if spotX == spotlist[spot][0] and spotY == spotlist[spot][1]: psycho[0] = psychoSpotData[i][0] psycho[1] = [ temp + j for temp, j in zip(psychoSpotData[i][1], psycho[1]) ] for contrast in range(len(psycho[0])): con = int(contrast) tempPsych[con] = np.append(tempPsych[con], psychoSpotData[i][2][con]) sems = [] for c in range(len(tempPsych)): psycho[2][c] = np.nanmean(tempPsych[c]) sems.append(stats.sem(tempPsych[c])) if bs: ## Bootstrap confidence intervals nboots = 10 bootFits = pd.DataFrame( columns=['threshold', 'slope', 'gamma', 'lambda'], index=range(nboots)) bootData = [[], [0 for i in range(len(psychoSpotData[0][1]))], [np.array([])] * len(psychoSpotData[0][1])] bootData[0] = psycho[0] cnt = 0 print('bootstrapping errorbars...', sep=' ', end='') for i in range(nboots): if not (cnt % 5): print(int(cnt / nboots * 100), sep=' ', end='%,', flush=True) for j in range(len(tempPsych)): bootData[2][j] = np.random.choice( tempPsych[j], size=int(len(tempPsych[j]) / 1.25), replace=True) bootData[1][j] = len(bootData[2][j]) bootData[2][j] = np.mean(bootData[2][j]) fitParams = [] fitLikes = [] for repeat in range(5): parStart = np.array([ -5 + np.random.rand() * 10, 0 + np.random.rand() * 100, 0 + np.random.rand(), 0 + np.random.rand() ]) pars, L = mle_fit_psycho(bootData, P_model='erf_psycho_2gammas', parstart=np.array([0, 50, .5, .5]), parmin=np.array([-5, 0., 0., 0.]), parmax=np.array([5, 100., 1, 1]), nfits=2) fitParams.append(pars) fitLikes.append(L) cnt += 1 bootFits.iloc[i] = fitParams[np.where(min(fitLikes))[0][0]] a = .05 CIs = [] for i in bootFits.columns: CIs.append([ np.percentile(bootFits[i], 100 - a / 2), np.percentile( bootFits[i], a / 2, ) ]) else: CIs = None ## plotting psychometrics for different cortical groups lines = [] fitParams = [] fitLikes = [] for repeat in range(10): parStart = np.array([ -5 + np.random.rand() * 10, 0 + np.random.rand() * 100, 0 + np.random.rand(), 0 + np.random.rand() ]) params, L = psychofit.mle_fit_psycho( psycho, P_model='erf_psycho_2gammas', parstart=parStart, parmin=np.array([-5, 0., 0., 0.]), parmax=np.array([5, 100., 1, 1]), nfits=25) fitParams.append(params) fitLikes.append(L) # find the best params (with the lowest neg likelihood) params = fitParams[np.where(min(fitLikes))[0][0]] spotFits.append(params) #plot the psychometrics fitx = np.linspace(-1, 1, 100) fity = psychofit.erf_psycho_2gammas(params, fitx) line = ax.plot(fitx, fity, color=color) lines.append(line) ax.errorbar(psycho[0], np.array(psycho[2]), yerr=sems, color=color, marker='.', ms=4, ls='') plt.ylim(-0.1, 1.1) plt.xlim(-1.1, 1.1) elif plotType == 'chrono': chrono = [[], [0 for i in range(len(psychoSpotData[0][1]))], [np.array([])] * len(psychoSpotData[0][1])] tempChrono = [np.array([])] * len(psychoSpotData[0][1]) for i in range(len(spots)): spotX = spots.iloc[i, [0]][0] spotY = spots.iloc[i, [1]][0] for spot in range(len(spotlist)): if spotX == spotlist[spot][0] and spotY == spotlist[spot][1]: chrono[0] = psychoSpotData[i][0] chrono[1] = [ temp + j for temp, j in zip(psychoSpotData[i][1], chrono[1]) ] for contrast in range(len(chrono[0])): con = int(contrast) tempChrono[con] = np.append(tempChrono[con], psychoSpotData[i][3][con]) sems = [] for c in range(len(tempChrono)): chrono[2][c] = np.nanmedian(tempChrono[c]) sems.append(stats.sem(tempChrono[c])) ax.errorbar(chrono[0], np.array(chrono[2]), yerr=sems, color=color, marker='.', ms=4) ax.set_ylim(.15, .5) # the params I use here are RT at 0 contrast, 'RT bias' which is pairwise RT left- RT right, and # peakiness which is the ratio of max RT to the average of the two 100% RTs p2 = (chrono[2][0] - chrono[2][-1]) + (chrono[2][1] - chrono[2][-2] + (chrono[2][2] - chrono[2][-3]) + (chrono[2][3] - chrono[2][-4])) params = [ chrono[2][4], p2, max(chrono[2]) / np.mean([chrono[2][0], chrono[2][-1]]), chrono[2] ] CIs = None else: raise Exception( "This is not a supported plot type, choose 'psycho' or 'chrono'") return params, CIs
def create_psych_curve_plot(sessions): data_mean = [] data_errorbar = [] data_fit = [] for session in sessions.fetch('KEY'): contrasts, prob_right, prob_left, \ threshold, bias, lapse_low, lapse_high, \ n_trials, n_trials_right = \ (sessions & session).fetch1( 'signed_contrasts', 'prob_choose_right', 'prob_left', 'threshold', 'bias', 'lapse_low', 'lapse_high', 'n_trials_stim', 'n_trials_stim_right') pars = [bias, threshold, lapse_low, lapse_high] contrasts = contrasts * 100 contrasts_fit = np.arange(-100, 100) prob_right_fit = psy.erf_psycho_2gammas(pars, contrasts_fit) ci = smp.proportion_confint( n_trials_right, n_trials, alpha=0.032, method='normal') - prob_right curve_color, error_color = get_color(prob_left, 0.3) behavior_data = go.Scatter( x=contrasts.tolist(), y=prob_right.tolist(), marker=dict(size=6, color=curve_color, line=dict(color='white', width=1)), mode='markers', name=f'p_left = {prob_left}, data with 68% CI') behavior_errorbar = go.Scatter(x=contrasts.tolist(), y=prob_right.tolist(), error_y=dict(type='data', array=ci[0].tolist(), arrayminus=np.negative( ci[1]).tolist(), visible=True, color=error_color), marker=dict(size=6, ), mode='none', showlegend=False) behavior_fit = go.Scatter(x=contrasts_fit.tolist(), y=prob_right_fit.tolist(), name=f'p_left = {prob_left} model fits', marker=dict(color=curve_color)) data_mean.append(behavior_data) data_errorbar.append(behavior_errorbar) data_fit.append(behavior_fit) layout = go.Layout(width=630, height=350, title=dict(text='Psychometric Curve', x=0.25, y=0.85), xaxis=dict(title='Contrast (%)'), yaxis=dict(title='Probability choosing right', range=[-0.05, 1.05]), template=dict(layout=dict(plot_bgcolor="white"))) data = data_errorbar for element in data_fit: data.append(element) for element in data_mean: data.append(element) return go.Figure(data=data, layout=layout)
def plot_psychometric(x, y, subj, **kwargs): # summary stats - average psychfunc over observers df = pd.DataFrame({ 'signed_contrast': x, 'choice': y, 'choice2': y, 'subject_nickname': subj }) df2 = df.groupby(['signed_contrast', 'subject_nickname']).agg({ 'choice2': 'count', 'choice': 'mean' }).reset_index() df2.rename(columns={ "choice2": "ntrials", "choice": "fraction" }, inplace=True) df2 = df2.groupby(['signed_contrast']).mean().reset_index() df2 = df2[['signed_contrast', 'ntrials', 'fraction']] # fit psychfunc pars, L = psy.mle_fit_psycho( df2.transpose().values, # extract the data from the df P_model='erf_psycho_2gammas', parstart=np.array([df2['signed_contrast'].mean(), 20., 0.05, 0.05]), parmin=np.array([df2['signed_contrast'].min(), 0., 0., 0.]), parmax=np.array([df2['signed_contrast'].max(), 100., 0.5, 0.5])) # plot psychfunc sns.lineplot(np.arange(-29, 29), psy.erf_psycho_2gammas(pars, np.arange(-29, 29)), **kwargs) # plot psychfunc: -100 sns.lineplot(np.arange(-37, -32), psy.erf_psycho_2gammas(pars, np.arange(-103, -98)), **kwargs) sns.lineplot(np.arange(32, 37), psy.erf_psycho_2gammas(pars, np.arange(98, 103)), **kwargs) # now break the x-axis # if 100 in df.signed_contrast.values and not 50 in df.signed_contrast.values: df['signed_contrast'] = df['signed_contrast'].replace(-100, -35) df['signed_contrast'] = df['signed_contrast'].replace(100, 35) df3 = df.groupby(['signed_contrast', 'subject_nickname']).agg({ 'choice2': 'count', 'choice': 'mean' }).reset_index() # plot datapoints with errorbars on top g = sns.lineplot(df3['signed_contrast'], df3['choice'], err_style="bars", linewidth=0, linestyle='None', mew=0.5, marker='o', ci=68, **kwargs) g.set_yticks([0, 0.25, 0.5, 0.75, 1]) # # ADD TEXT WITH THE PSYCHOMETRIC FUNCTION PARAMETERS # if len(df['subject_nickname'].unique()) == 1: # try: # # add text with parameters into the plot # if kwargs['label'] == '50': # ypos = 0.5 # # ADD PSYCHOMETRIC FUNCTION PARAMS # plt.text(-35, ypos, r'$\mu\/ %.2f,\/ \sigma\/ %.2f,$'%(pars[0], pars[1]) + '\n' + r'$\gamma \/%.2f,\/ \lambda\/ %.2f$'%(pars[2], pars[3]), # fontweight='normal', fontsize=5, color=kwargs['color']) # elif kwargs['label'] == '20': # ypos = 0.3 # # ADD PSYCHOMETRIC FUNCTION PARAMS # plt.text(-35, ypos, r'$\mu\/ %.2f,\/ \sigma\/ %.2f,$'%(pars[0], pars[1]) + '\n' + r'$\gamma \/%.2f,\/ \lambda\/ %.2f$'%(pars[2], pars[3]), # fontweight='normal', fontsize=5, color=kwargs['color']) # elif kwargs['label'] == '80': # ypos = 0.7 # # ADD PSYCHOMETRIC FUNCTION PARAMS # plt.text(-35, ypos, r'$\mu\/ %.2f,\/ \sigma\/ %.2f,$'%(pars[0], pars[1]) + '\n' + r'$\gamma \/%.2f,\/ \lambda\/ %.2f$'%(pars[2], pars[3]), # fontweight='normal', fontsize=5, color=kwargs['color']) # except: # when there is no label # # pass # ypos = 0.5 # # ADD PSYCHOMETRIC FUNCTION PARAMS # plt.text(-35, ypos, r'$\mu\/ %.2f,\/ \sigma\/ %.2f,$'%(pars[0], pars[1]) + '\n' + r'$\gamma \/%.2f,\/ \lambda\/ %.2f$'%(pars[2], pars[3]), # fontweight='normal', fontsize=8, color=kwargs['color']) # # plt.text(12, 0.1, '1 mouse', fontsize=10, color='k') # print the number of mice if df['subject_nickname'].nunique() == 1: plt.text(12, 0.1, '1 mouse', fontsize=10, color='k') else: plt.text(12, 0.1, '%d mice' % (df['subject_nickname'].nunique()), fontsize=10, color='k') #if brokenXaxis: g.set_xticks([-35, -25, -12.5, 0, 12.5, 25, 35]) g.set_xticklabels(['-100', '-25', '-12.5', '0', '12.5', '25', '100'], size='small', rotation=45) g.set_xlim([-40, 40]) g.set_ylim([0, 1]) g.set_yticks([0, 0.25, 0.5, 0.75, 1]) g.set_yticklabels(['0', '25', '50', '75', '100'])
def model_psychometric_history(behav): select = behav.copy() select['t-1'] = select['trial_feedback_type'].shift(periods=1).to_numpy() select.loc[select['choice'] == -1, 'choice'] = 0 select = select.iloc[1:,:] #select['t-1'].fillna(0, inplace=True) select['t-1'] = select['t-1'].astype(int) plot_psychometric(select.loc[select['signed_contrast'], select.loc[select['probabilityLeft'] ==i, 'signed_contrast'], palette = ['red', 'green'], ci = 68) sns.lineplot(data = select_50, hue = 't-1', x = select_50['signed_contrast'], y = select_50['simulation_prob'], palette = ['red', 'green'], ci = 68) ## Functions def run_glm(behav, example, correction = True, bias = False, cross_validation = True): for i, nickname in enumerate(np.unique(behav['subject_nickname'])): if np.mod(i+1, 10) == 0: print('Loading data of subject %d of %d' % (i+1, len( np.unique(behav['subject_nickname'])))) # Get the trials of the sessions around criterion trials = behav.loc[behav['subject_nickname'] == nickname].copy() if bias == True: neutral_n = fit_psychfunc(behav[(behav['subject_nickname'] == nickname) & (behav['probabilityLeft'] == 50)]) left_fit = fit_psychfunc(behav[(behav['subject_nickname'] == nickname) & (behav['probabilityLeft'] == 80)]) right_fit = fit_psychfunc(behav[(behav['subject_nickname'] == nickname) & (behav['probabilityLeft'] == 20)]) behav.loc[behav['subject_nickname'] == nickname, 'bias_n'] = \ neutral_n.loc[0, 'bias'] behav.loc[behav['subject_nickname'] == nickname, 'bias_r'] = \ right_fit.loc[0, 'bias'] behav.loc[behav['subject_nickname'] == nickname, 'bias_l'] = \ left_fit.loc[0, 'bias'] else: fit_df = dj2pandas(trials.copy()) fit_result = fit_psychfunc(fit_df) behav.loc[behav['subject_nickname'] == nickname, 'threshold'] = \ fit_result.loc[0, 'threshold'] ## GLM #make separate datafrme data = trials[['index', 'trial_feedback_type', 'signed_contrast', 'choice', 'probabilityLeft']].copy() #drop trials with odd probabilities of left data.drop( data['probabilityLeft'][~data['probabilityLeft'].isin([50,20,80])].index, inplace=True) # Rewardeded choices: data.loc[(data['choice'] == 0) & (data['trial_feedback_type'].isnull()), 'rchoice'] = 0 # NoGo trials data.loc[(data['choice'] == -1) & (data['trial_feedback_type'] == -1), 'rchoice'] = 0 data.loc[(data['choice'] == -1) & (data['trial_feedback_type'] == 1), 'rchoice'] = -1 data.loc[(data['choice'] == 1) & (data['trial_feedback_type'] == 1), 'rchoice'] = 1 data.loc[(data['choice'] == 0) & (data['trial_feedback_type'].isnull()) , 'rchoice'] = 0 # NoGo trials data.loc[(data['choice'] == 1) & (data['trial_feedback_type'] == -1), 'rchoice'] = 0 # Unrewarded choices: data.loc[(data['choice'] == 0) & (data['trial_feedback_type'].isnull()), 'uchoice'] = 0 # NoGo trials data.loc[(data['choice'] == -1) & (data['trial_feedback_type'] == -1), 'uchoice'] = -1 data.loc[(data['choice'] == -1) & (data['trial_feedback_type'] == 1), 'uchoice'] = 0 data.loc[(data['choice'] == 1) & (data['trial_feedback_type'] == 1), 'uchoice'] = 0 data.loc[(data['choice'] == 0) & (data['trial_feedback_type'].isnull()) , 'uchoice'] = 0 # NoGo trials data.loc[(data['choice'] == 1) & (data['trial_feedback_type'] == -1) , 'uchoice'] = 1 # Apply correction if correction == True: data['rchoice+1'] = \ data['rchoice'].shift(periods=-1).to_numpy() data['uchoice+1'] = \ data['uchoice'].shift(periods=-1).to_numpy() # Shift rewarded and unrewarded predictors by one data.loc[:, ['rchoice', 'uchoice']] = \ data[['rchoice', 'uchoice']].shift(periods=1).to_numpy() # Drop any nan trials data.dropna(inplace=True) # Make sensory predictors (no 0 predictor) contrasts = [ 25, 100, 12.5, 6.25] for i in contrasts: data.loc[(data['signed_contrast'].abs() == i), i] = \ np.sign(data.loc[(data['signed_contrast'].abs() == i), 'signed_contrast'].to_numpy()) data_con = data[i].copy() data[i] = data_con.fillna(0) # If contrast missing break for i in contrasts: if np.sum(data[i]) == 0: print('missing contrast') missing_contrast = True else: missing_contrast = False if missing_contrast == True: continue # Make block identity (across predictors right is positive, hence logic below) if bias == True: data.loc[(data['probabilityLeft'] == 50), 'block'] = 0 data.loc[(data['probabilityLeft'] == 20), 'block'] = 1 data.loc[(data['probabilityLeft'] == 80), 'block'] = -1 # Make choice in between 0 and 1 -> 1 for right and 0 for left data.loc[data['choice'] == -1, 'choice'] = 0 # Store index index = data['index'].copy() # Create predictor matrix endog = data['choice'].copy() exog = data.copy() exog.drop(columns=['trial_feedback_type', 'signed_contrast', 'choice', 'probabilityLeft'], inplace=True) exog = sm.add_constant(exog) if cross_validation == False: X_train = exog.copy() X_test = exog.copy() y_train = endog.copy() y_test = endog.copy() else: X_train = exog.iloc[:int(len(exog)*0.70),:].copy() X_test = exog.iloc[int(len(endog)*0.70):,:].copy() y_train = endog.iloc[:int(len(endog)*0.70)].copy() y_test = endog.iloc[int(len(endog)*0.70):].copy() # Store index index = X_test['index'].to_numpy() X_train.drop(columns=['index'], inplace=True) X_test.drop(columns=['index'], inplace=True) # Fit model try: logit_model = sm.Logit(y_train, X_train) result = logit_model.fit_regularized() # print(result.summary2()) # Store model weights behav.loc[behav['subject_nickname'] == nickname, 'intercept'] = result.params['const'].copy() behav.loc[behav['subject_nickname'] == nickname, 'rchoice'] = result.params['rchoice'].copy() behav.loc[behav['subject_nickname'] == nickname, 'uchoice'] = result.params['uchoice'].copy() mask = result.params.index.get_level_values(0) behav.loc[behav['subject_nickname'] == nickname, '25'] = result.params[25].copy() behav.loc[behav['subject_nickname'] == nickname, '6'] = result.params.loc[mask == 6.25][0] behav.loc[behav['subject_nickname'] == nickname, '100'] = result.params[100].copy() behav.loc[behav['subject_nickname'] == nickname, '12'] = result.params.loc[mask == 12.5][0] if bias == True: behav.loc[behav['subject_nickname'] == nickname, 'block'] = result.params['block'].copy() if correction == True: behav.loc[behav['subject_nickname'] == nickname, 'rchoice+1'] = result.params['rchoice+1'].copy() behav.loc[behav['subject_nickname'] == nickname, 'uchoice+1'] = result.params['uchoice+1'].copy() # Probabilities on test data prob = result.predict(X_test).to_numpy() if nickname == example: example_model = result # Propagate to storing dataframe behav.loc[behav['index'].isin(index), 'simulation_prob'] = prob except: print('singular matrix') return behav, example_model def data_2_X_test (behav, correction = True, bias = True): data = behav[['index','trial_feedback_type', 'signed_contrast', 'choice', 'probabilityLeft']].copy() #drop trials with odd probabilities of left data.drop( data['probabilityLeft'][~data['probabilityLeft'].isin([50,20,80])].index, inplace=True) # Rewardeded choices: data.loc[(data['choice'] == 0) & (data['trial_feedback_type'].isnull()), 'rchoice'] = 0 # NoGo trials data.loc[(data['choice'] == -1) & (data['trial_feedback_type'] == -1), 'rchoice'] = 0 data.loc[(data['choice'] == -1) & (data['trial_feedback_type'] == 1), 'rchoice'] = -1 data.loc[(data['choice'] == 1) & (data['trial_feedback_type'] == 1), 'rchoice'] = 1 data.loc[(data['choice'] == 0) & (data['trial_feedback_type'].isnull()) , 'rchoice'] = 0 # NoGo trials data.loc[(data['choice'] == 1) & (data['trial_feedback_type'] == -1), 'rchoice'] = 0 # Unrewarded choices: data.loc[(data['choice'] == 0) & (data['trial_feedback_type'].isnull()), 'uchoice'] = 0 # NoGo trials data.loc[(data['choice'] == -1) & (data['trial_feedback_type'] == -1), 'uchoice'] = -1 data.loc[(data['choice'] == -1) & (data['trial_feedback_type'] == 1), 'uchoice'] = 0 data.loc[(data['choice'] == 1) & (data['trial_feedback_type'] == 1), 'uchoice'] = 0 data.loc[(data['choice'] == 0) & (data['trial_feedback_type'].isnull()) , 'uchoice'] = 0 # NoGo trials data.loc[(data['choice'] == 1) & (data['trial_feedback_type'] == -1) , 'uchoice'] = 1 # Apply correction if correction == True: data['rchoice+1'] = \ data['rchoice'].shift(periods=-1).to_numpy() data['uchoice+1'] = \ data['uchoice'].shift(periods=-1).to_numpy() # Shift rewarded and unrewarded predictors by one data.loc[:, ['rchoice', 'uchoice']] = \ data[['rchoice', 'uchoice']].shift(periods=1).to_numpy() # Drop any nan trials data.dropna(inplace=True) # Make sensory predictors (no 0 predictor) contrasts = [ 25, 100, 12.5, 6.25] for i in contrasts: data.loc[(data['signed_contrast'].abs() == i), i] = \ np.sign(data.loc[(data['signed_contrast'].abs() == i), 'signed_contrast'].to_numpy()) data_con = data[i].copy() data[i] = data_con.fillna(0) # Make block identity (across predictors right is positive, hence logic below) if bias == True: data.loc[(data['probabilityLeft'] == 50), 'block'] = 0 data.loc[(data['probabilityLeft'] == 20), 'block'] = 1 data.loc[(data['probabilityLeft'] == 80), 'block'] = -1 # Make choice in between 0 and 1 -> 1 for right and 0 for left data.loc[data['choice'] == -1, 'choice'] = 0 index = data['index'].copy() # Create predictor matrix endog = data['choice'].copy() exog = data.copy() exog.drop(columns=['index', 'trial_feedback_type', 'signed_contrast', 'choice', 'probabilityLeft'], inplace=True) exog = sm.add_constant(exog) return exog, index def plot_psychometric(x, y, col, point = False, mark = 'o', al =1): # summary stats - average psychfunc over observers df = pd.DataFrame({'signed_contrast': x, 'choice': y, 'choice2': y}) df2 = df.groupby(['signed_contrast']).agg( {'choice2': 'count', 'choice': 'mean'}).reset_index() df2.rename(columns={"choice2": "ntrials", "choice": "fraction"}, inplace=True) df2 = df2.groupby(['signed_contrast']).mean().reset_index() df2 = df2[['signed_contrast', 'ntrials', 'fraction']] # fit psychfunc pars, L = psy.mle_fit_psycho(df2.transpose().values, # extract the data from the df P_model='erf_psycho_2gammas', parstart=np.array( [df2['signed_contrast'].mean(), 20., 0.05, 0.05]), parmin=np.array( [df2['signed_contrast'].min(), 5, 0., 0.]), parmax=np.array([df2['signed_contrast'].max(), 100., 1, 1])) # plot psychfunc g = sns.lineplot(np.arange(-29, 29), psy.erf_psycho_2gammas(pars, np.arange(-29, 29)), color = col, alpha = al) # plot psychfunc: -100, +100 sns.lineplot(np.arange(-37, -32), psy.erf_psycho_2gammas(pars, np.arange(-103, -98)), color = col, alpha = al) sns.lineplot(np.arange(32, 37), psy.erf_psycho_2gammas(pars, np.arange(98, 103)), color = col, alpha = al) # now break the x-axis # if 100 in df.signed_contrast.values and not 50 in # df.signed_contrast.values: df['signed_contrast'] = df['signed_contrast'].replace(-100, -35) df['signed_contrast'] = df['signed_contrast'].replace(100, 35) if point == True: sns.lineplot(df['signed_contrast'], df['choice'], err_style="bars", linewidth=0, linestyle='None', mew=0.5, marker=mark, ci=68, color = col, alpha = al) g.set_xticks([-35, -25, -12.5, 0, 12.5, 25, 35]) g.set_xticklabels(['-100', '-25', '-12.5', '0', '12.5', '25', '100'], size='small', rotation=45) g.set_xlim([-40, 40]) g.set_ylim([0, 1]) g.set_yticks([0, 0.25, 0.5, 0.75, 1]) g.set_yticklabels(['0', '25', '50', '75', '100']) # FUNCTION UNDER DEVELOPMENT def updating: select = behav.copy() select['signed_contrast-1'] = select['signed_contrast'].shift(periods=1).to_numpy() select['signed_contrast+1'] = select['signed_contrast'].shift(periods=-1).to_numpy() select['t-1'] = select['trial_feedback_type'].shift(periods=1).to_numpy() select['t+1'] = select['trial_feedback_type'].shift(periods=-1).to_numpy() select = select.iloc[1:-1,:] # First and last trial will have nan for history select['t-1'] = select['t-1'].astype(int) select['simulation_prob'] = select['simulation_prob']*100 #select = select.loc[select['signed_contrast-1'] >= 0] for mouse in select['subject_nickname'].unique(): for c in select['signed_contrast-1'].unique(): for r in select['t-1'].unique(): sub_select = select.loc[(select['signed_contrast-1'] == c) & (select['t-1'] == r) & (select['subject_nickname'] == mouse)] fit_result = fit_psychfunc(sub_select) select.loc[select['subject_nickname'] == nickname, 'updating'] = \ fit_result['bias'][0] for c in select['signed_contrast+1'].unique(): for r in select['t+1'].unique(): sub_select = select.loc[(select['signed_contrast+1'] == c) & (select['t+1'] == r) & (select['subject_nickname'] == mouse)] fit_result = fit_psychfunc(sub_select) select.loc[select['subject_nickname'] == nickname, 'updating_correction'] = \ fit_result['bias'][0] sns.lineplot(data = select, hue = 't-1', x = select['signed_contrast-1'], y = select['simulation_prob'], ci = 68) sns.lineplot(data = select, hue = 't-1', x = select['signed_contrast-1'], y = select[select['probabilityLeft'] == 80],'choice'] - select.loc[select['probabilityLeft'] == 20 ,'choice'])
def plot_psychometric(x, y, col, point=False, line=True, mark='.', al=1, ax=None, **kwargs): if not ax: ax = plt.sca(ax[0]) # summary stats - average psychfunc over observers df = pd.DataFrame({'signed_contrast': x, 'choice': y, 'choice2': y}) df2 = df.groupby(['signed_contrast']).agg({ 'choice2': 'count', 'choice': 'mean' }).reset_index() df2.rename(columns={ "choice2": "ntrials", "choice": "fraction" }, inplace=True) df2 = df2.groupby(['signed_contrast']).mean().reset_index() df2 = df2[['signed_contrast', 'ntrials', 'fraction']] # fit psychfunc pars, L = psy.mle_fit_psycho( df2.transpose().values, # extract the data from the df P_model='erf_psycho_2gammas', parstart=np.array([df2['signed_contrast'].mean(), 20., 0.05, 0.05]), parmin=np.array([df2['signed_contrast'].min(), 5, 0., 0.]), parmax=np.array([df2['signed_contrast'].max(), 100., 1, 1])) if line: # plot psychfunc sns.lineplot(np.arange(-29, 29), psy.erf_psycho_2gammas(pars, np.arange(-29, 29)), color=col, alpha=al, ax=ax) # plot psychfunc: -100, +100 sns.lineplot(np.arange(-37, -32), psy.erf_psycho_2gammas(pars, np.arange(-103, -98)), color=col, alpha=al, ax=ax) sns.lineplot(np.arange(32, 37), psy.erf_psycho_2gammas(pars, np.arange(98, 103)), color=col, alpha=al, ax=ax) # now break the x-axis # if 100 in df.signed_contrast.values and not 50 in # df.signed_contrast.values: df['signed_contrast'] = df['signed_contrast'].replace(-100, -35) df['signed_contrast'] = df['signed_contrast'].replace(100, 35) # PLOT DATAPOINTS if point == True: sns.lineplot(df['signed_contrast'], df['choice'], err_style="bars", linewidth=0, linestyle='None', mew=0.5, marker=mark, ci=95, color=col, alpha=al, markersize=3, ax=ax) ax.set_xticks([-35, -25, -12.5, 0, 12.5, 25, 35]) ax.set_xticklabels(['-100', ' ', ' ', '0', ' ', ' ', '100'], size='small', rotation=45) ax.set_xlim([-40, 40]) ax.set_ylim([0, 1]) ax.set_yticks([0, 0.25, 0.5, 0.75, 1]) ax.set_yticklabels(['0', ' ', '50', ' ', '100'])
parmin=np.array([-5, 0., 0., 0.]), parmax=np.array([5, 100., 1, 1]), nfits=50) fitParams.append(params) fitLikes.append(L) # find the best params (with the lowest neg likelihood) params = fitParams[np.where(min(fitLikes))[0][0]] spotBias[i][subIdx] = params[0] spotSlope[i][subIdx] = params[1] spotLapseLow[i][subIdx] = params[2] spotLapseHigh[i][subIdx] = params[3] spotFits.append(params) #plot the psychometrics fitx = np.linspace(-1, 1, 100) fity = psychofit.erf_psycho_2gammas(params, fitx) line = axs[0,0].plot(fitx, fity, lineColors[plotCount]) lines.append(line) axs[0,0].plot(psychoPlot[0], np.array(psychoPlot[2]), dotColors[plotCount]) plotCount+=1 ## Formatting the psychometric figure LvisLine = mpl.lines.Line2D([],[],color='blue',marker='.',label='L Off') RvisLine = mpl.lines.Line2D([],[],color='red',marker='.',label='R Off') cLine = mpl.lines.Line2D([],[],color='green',marker='.',label='Control Spots') ax.legend(handles=[LvisLine,RvisLine,cLine],loc='upper left') ax.set_title('Visual Cortex') ax.set_ylabel('Proportion CW') if len(np.unique(subject['subject'])) == 1: fig.suptitle(np.unique(subject['subject'])[0]) else:
# FOR EACH ANIMAL + for each lab (in 'lab color') # ================================================================== # print('fitting psychometric functions...') pars = behav.groupby( ['institution_code', 'subject_nickname', 'probabilityLeft']).apply(fit_psychfunc).reset_index() # now read these out at the presented levels of signed contrast behav2 = pd.DataFrame([]) xvec = behav.signed_contrast.unique() for index, group in pars.groupby( ['institution_code', 'subject_nickname', 'probabilityLeft']): # expand yvec = psy.erf_psycho_2gammas([ group.bias.item(), group.threshold.item(), group.lapselow.item(), group.lapsehigh.item() ], xvec) group2 = group.loc[group.index.repeat( len(yvec))].reset_index(drop=True).copy() group2['signed_contrast'] = xvec group2['choice'] = 100 * yvec # add this behav2 = behav2.append(group2) # now subtract these to compute a bias shift behav3 = pd.pivot_table( behav2, values='choice', index=['institution_code', 'subject_nickname', 'signed_contrast'],
def plot_psych_block(psy_df, block_variable, blocks): """Plots psychometric using ibl_psychometric INPUT: Dataframe where index = trial and block variable = hue block_variable = name of block struct column (e.g rewprobabilityLeft) blocks = blovks that I want plotted (np.array) (e.g np.array([1, 0.7])) OUTPUT: Average of all sessions, Average of Last three sessions, Last 5 sessions""" #First get fits for each block #Set frame for plots block_summary, axes = plt.subplots(1, 2, sharex=True) block_summary.set_figheight(7) block_summary.set_figwidth(15) plt.sca(axes[0]) colors = ['blue', 'green'] sns.set() #First get fits for each block for j, i in enumerate(blocks): psy_df_block = psy_df.loc[psy_df[block_variable] == i] pars, L = ibl_psychometric(psy_df_block) sns.lineplot(np.arange(-100, 100), psy.erf_psycho_2gammas(pars, np.arange(-100, 100)), color=colors[j]) sns.lineplot(x='signed_contrasts', y='right_choices', err_style="bars", linewidth=0, linestyle='None', mew=0.5, marker='.', color=colors[j], ci=68, data=psy_df_block) #Get sns.lineplot for raw data for each contrast per session axes[0].set_xlabel('Signed contrast (%)') axes[0].set_ylabel('% Right') axes[0].set_title('All sessions') #plot average last three session dates = sorted(psy_df['ses'].unique()) psy_df_last3 = psy_df.loc[(psy_df['ses'] == dates[-1]) | (psy_df['ses'] == dates[-2]) | (psy_df['ses'] == dates[-3])] plt.sca(axes[1]) for j, i in enumerate(blocks): psy_df_block = psy_df_last3.loc[psy_df_last3[block_variable] == i] pars, L = ibl_psychometric(psy_df_block) sns.lineplot(np.arange(-100, 100), psy.erf_psycho_2gammas(pars, np.arange(-100, 100)), color=colors[j]) sns.lineplot(x='signed_contrasts', y='right_choices', err_style="bars", linewidth=0, linestyle='None', mew=0.5, marker='.', color=colors[j], ci=68, data=psy_df_block) axes[1].set_xlabel('Signed contrast (%)') axes[1].set_title('Last 3 sessions') axes[1].set_ylabel('') #Now plot last 5 sessions plots = len(dates) rows = int(np.ceil(plots / 3)) cols = int(np.ceil(plots / rows)) all_sessions = plt.figure(figsize=(12, 10)) sns.set() for j, date in enumerate(dates): ax = all_sessions.add_subplot(rows, cols, 1 + j) ax.set_title(date) ax.label_outer() for l, i in enumerate(blocks): psy_df_block = psy_df.loc[psy_df[block_variable] == i] psy_df_block_date = psy_df_block.loc[psy_df_block['ses'] == date] pars, L = ibl_psychometric(psy_df_block_date, ax) sns.lineplot(np.arange(-100, 100), psy.erf_psycho_2gammas(pars, np.arange(-100, 100)), color=colors[l]) sns.lineplot(x='signed_contrasts', y='right_choices', err_style="bars", linewidth=0, linestyle='None', mew=0.5, marker='.', color=colors[l], ci=68, data=psy_df_block_date) ax.set_label("block %s" % i) ax.set_xlabel('Signed contrast (%)') ax.set_ylabel('Right choices (%)') blue_patch = mpatches.Patch(color='blue', label='P(rew|Left): 1 P(rew|Right): 0.7') orange_patch = mpatches.Patch(color='green', label='P(rew|Left): 0.7 P(rew|Right): 1') plt.legend(handles=[blue_patch, orange_patch], loc='lower right') ax.set_xlabel('Signed contrast (%)') return block_summary, all_sessions
def plot_psychos_from_spots(spotlists, psychoSpotData, spots, labels): """ Function to plot three psychometrics on a single plot, with the dots that they were generated with. usually I'll use this with a group of left spots, a group of right spots and a group of control spots. Inputs: spotlists: a list of length 3 that has the spots """ Psycho1 = [[],[0 for i in range(len(psychoSpotData[0][1]))],[np.nan for i in range(len(psychoSpotData[0][1]))]] Psycho2 = [[],[0 for i in range(len(psychoSpotData[0][1]))],[np.nan for i in range(len(psychoSpotData[0][1]))]] controlSpotPsycho = [[],[0 for i in range(len(psychoSpotData[0][1]))],[np.nan for i in range(len(psychoSpotData[0][1]))]] for i in range(len(spots)): spotX = spots.iloc[i, [0]][0] spotY = spots.iloc[i, [1]][0] for leftSpots in range(len(visLeftSpots)): if spotX == visLeftSpots[leftSpots][0] and spotY == visLeftSpots[leftSpots][1]: Psycho1[0] = psychoSpotData[i][0] Psycho1[1] = [i+j for i,j in zip(psychoSpotData[i][1],Psycho1[1])] Psycho1[2] = [np.nanmean([i,j]) for i,j in zip(psychoSpotData[i][2],Psycho1[2])] for rightSpots in range(len(visRightSpots)): if spotX == visRightSpots[rightSpots][0] and spotY == visRightSpots[rightSpots][1]: Psycho2[0] = psychoSpotData[i][0] Psycho2[1] = [i+j for i,j in zip(psychoSpotData[i][1],Psycho2[1])] Psycho2[2] = [np.nanmean([i,j]) for i,j in zip(psychoSpotData[i][2],Psycho2[2])] for ii in controlSpots: if spotX == spots.iloc[ii][0] and spotY == spots.iloc[ii][1]: controlSpotPsycho[0] = psychoSpotData[ii][0] controlSpotPsycho[1] = [i+j for i,j in zip(psychoSpotData[ii][1],controlSpotPsycho[1])] controlSpotPsycho[2] = [np.nanmean([i,j]) for i,j in zip(psychoSpotData[ii][2],controlSpotPsycho[2])] ## plotting psychometrics for different cortical groups lines = [] print('Fitting psychometrics, {}/{} Done'.format(subIdx,len(data))) fig = plt.figure() dotColors = ['.b','.r','.g'] lineColors = ['-b','-r','-g'] plotCount = 0 for psychoPlot in [Psycho1, Psycho2, controlSpotPsycho]: fitParams = [] fitLikes = [] for repeat in range(10): params, L = psychofit.mle_fit_psycho(psychoPlot, P_model='erf_psycho_2gammas', parstart=np.array([0,20,.05,.05]), parmin=np.array([-5, 0., 0., 0.]), parmax=np.array([5, 100., 1, 1]), nfits=50) fitParams.append(params) fitLikes.append(L) # find the best params (with the lowest neg likelihood) params = fitParams[np.where(min(fitLikes))[0][0]] spotBias[i][subIdx] = params[0] spotSlope[i][subIdx] = params[1] spotLapseLow[i][subIdx] = params[2] spotLapseHigh[i][subIdx] = params[3] spotFits.append(params) #plot the psychometrics fitx = np.linspace(-1, 1, 100) fity = psychofit.erf_psycho_2gammas(params, fitx) line = plt.plot(fitx, fity, lineColors[plotCount]) lines.append(line) plt.plot(psychoPlot[0], np.array(psychoPlot[2]), dotColors[plotCount]) plotCount+=1 ## Formatting the psychometric figure LvisLine = mpl.lines.Line2D([],[],color='blue',marker='.',label=labels[0]) RvisLine = mpl.lines.Line2D([],[],color='red',marker='.',label=labels[1]) cLine = mpl.lines.Line2D([],[],color='green',marker='.',label=labels[2]) plt.legend(handles=[LvisLine,RvisLine,cLine])