예제 #1
0
def fit_psychfunc(df):
    choicedat = df.groupby('signed_contrast').agg({
        'choice': 'count',
        'choice2': 'mean'
    }).reset_index()
    pars, L = psy.mle_fit_psycho(
        choicedat.values.transpose(),
        P_model='erf_psycho_2gammas',
        parstart=np.array(
            [choicedat['signed_contrast'].mean(), 20., 0.05, 0.05]),
        parmin=np.array([choicedat['signed_contrast'].min(), 0., 0., 0.]),
        parmax=np.array([choicedat['signed_contrast'].max(), 100., 1, 1]))
    df2 = {
        'bias': pars[0],
        'threshold': pars[1],
        'lapselow': pars[2],
        'lapsehigh': pars[3]
    }
    df2 = pd.DataFrame(df2, index=[0])

    # # add some stuff
    # df2['easy_correct'] = df.loc[np.abs(
    #     df['signed_contrast'] > 50), 'correct'].mean(skipna=True)
    # df2['zero_contrast'] = df.loc[np.abs(
    #     df['signed_contrast'] == 0), 'choice2'].mean(skipna=True)
    # df2['median_rt'] = df['rt'].median(skipna=True)
    # df2['mean_rt'] = df['rt'].mean(skipna=True)

    # # number of trials per day
    # df4 = df.groupby(['session_start_time'])['correct'].count().reset_index()
    # df2['ntrials_perday'] = [df4['correct'].values]
    df2['ntrials'] = df['choice'].count()

    return df2
예제 #2
0
def ibl_psychometric(psy_df, ax=None, **kwargs):
    """Calculates and plot psychometic curve from dataframe
    datajoint independent
    assumes that there is not signed contrast in dataframe
    INPUTS: Dataframe where index is the trial number
    OUTPUTS:  psychometic fit using IBL function from Miles and fit parameters"""

    #1st calculate some useful data...
    psy_df.loc[:, 'contrastRight'] = psy_df['contrastRight'].fillna(0)
    psy_df.loc[:, 'contrastLeft'] = psy_df['contrastLeft'].fillna(0)
    psy_df.loc[:, 'signed_contrasts'] = (psy_df['contrastRight'] -
                                         psy_df['contrastLeft']) * 100
    unique_signed_contrasts = sorted(psy_df['signed_contrasts'].unique())

    right_choices = psy_df['choice'] == -1
    psy_df.loc[:, 'right_choices'] = right_choices
    total_trials = []
    right_trials = []

    for cont in unique_signed_contrasts:
        matching = (psy_df['signed_contrasts'] == cont)
        total_trials.append(np.sum(matching))
        right_trials.append(np.sum(right_choices[matching]))

    prop_right_trials = np.divide(right_trials, total_trials)

    pars, L = psy.mle_fit_psycho(
        np.vstack([unique_signed_contrasts, total_trials, prop_right_trials]),
        P_model='erf_psycho_2gammas',
        parstart=np.array([np.mean(unique_signed_contrasts), 20., 0.05, 0.05]),
        parmin=np.array([np.min(unique_signed_contrasts), 0., 0., 0.]),
        parmax=np.array([np.max(unique_signed_contrasts), 100., 1, 1]))

    return pars, L
예제 #3
0
def fit_psychfunc(df):
    choicedat = df.groupby('signedContrast').agg({'trial':'count', '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])
예제 #4
0
def fit_psychfunc(stim_levels, n_trials, proportion):
    # Fit a psychometric function with two lapse rates
    #
    # Returns vector pars with [bias, threshold, lapselow, lapsehigh]
    from ibl_pipeline.utils import psychofit as psy
    assert (stim_levels.shape == n_trials.shape == proportion.shape)
    if stim_levels.max() <= 1:
        stim_levels = stim_levels * 100

    pars, _ = psy.mle_fit_psycho(np.vstack(
        (stim_levels, n_trials, proportion)),
                                 P_model='erf_psycho_2gammas',
                                 parstart=np.array([0, 20, 0.05, 0.05]),
                                 parmin=np.array([-100, 5, 0, 0]),
                                 parmax=np.array([100, 100, 1, 1]))
    return pars
예제 #5
0
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_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
예제 #7
0
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'])
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])
        ## plotting psychometrics for different cortical groups
    lines = []
    print('Fitting psychometrics, {}/{} Done'.format(subIdx,len(data)))
    fig,axs = plt.subplots(nrows=2,ncols=2)
    ax=axs[0,0]
    dotColors = ['.r','.b','.g']
    lineColors = ['-r','-b','-g']
    plotCount = 0
    for psychoPlot in [visLeftPsycho, visRightPsycho, 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)
예제 #10
0
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'])
                
예제 #11
0
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'])
예제 #12
0
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'])