Example #1
0
def psychometric_function(trialsfile, plot=None, threshold=False, **kwargs):
    """
    Compute and plot the sychometric function.

    """
    # Load trials
    trials, ntrials = load_trials(trialsfile)

    #-------------------------------------------------------------------------------------
    # Compute psychometric function
    #-------------------------------------------------------------------------------------

    choice_by_coh = {}
    ntot = 0
    ncorrect = 0
    for trial in trials:
        info = trial['info']

        # Signed coherence
        coh = info['in_out'] * info['coh']

        # Choice
        choice = get_choice(trial, threshold)
        if choice is None:
            continue
        if coh != 0:
            ntot += 1
        if isinstance(choice, tuple):
            choice, _ = choice
        choice_by_coh.setdefault(coh, []).append(choice)

        # Correct
        if coh != 0 and choice == info['choice']:
            ncorrect += 1

    # Report overall performance
    pcorrect = 100 * ncorrect / ntot
    print("[ {}.psychometric_function ] {}/{} = {:.2f}% correct.".format(
        THIS, ncorrect, ntot, pcorrect))

    cohs = np.sort(np.array(choice_by_coh.keys()))
    p0 = np.zeros(len(cohs))
    for i, coh in enumerate(cohs):
        choices = np.array(choice_by_coh[coh])
        p0[i] = 1 - np.sum(choices) / len(choices)
    scaled_cohs = SCALE * cohs

    #-------------------------------------------------------------------------------------
    # Plot
    #-------------------------------------------------------------------------------------

    if plot is not None:
        # Data
        prop = {
            'ms': kwargs.get('ms', 6),
            'mfc': kwargs.get('mfc', '0.2'),
            'mew': kwargs.get('mew', 0),
            'zorder': 10
        }
        plot.plot(scaled_cohs, 100 * p0, 'o', **prop)

        # Fit
        prop = {
            'color': kwargs.get('mfc', '0.2'),
            'lw': kwargs.get('lw', 1),
            'zorder': 5
        }
        try:
            popt, func = fittools.fit_psychometric(scaled_cohs, p0)

            fit_cohs = np.linspace(min(scaled_cohs), max(scaled_cohs), 201)
            fit_p0 = func(fit_cohs, **popt)
            plot.plot(fit_cohs, 100 * fit_p0, **prop)
        except RuntimeError:
            print("[ {}.psychometric_function ]".format(THIS) +
                  " Unable to fit, drawing a line through the points.")
            plot.plot(scaled_cohs, 100 * p0, **prop)

        plot.ylim(0, 100)
        plot.yticks([0, 25, 50, 75, 100])
        plot.lim('x', scaled_cohs)
Example #2
0
def psychometric_function(trialsfile, plots=None, **kwargs):
    """
    Psychometric function.

    """
    # Load trials
    trials, ntrials = load_trials(trialsfile)

    #-------------------------------------------------------------------------------------
    # Compute psychometric function
    #-------------------------------------------------------------------------------------

    results = {cond: {} for cond in ['mm', 'mc', 'cm', 'cc']}
    ncorrect = 0
    for trial in trials:
        info = trial['info']
        coh_m = info['left_right_m'] * info['coh_m']
        coh_c = info['left_right_c'] * info['coh_c']
        choice = get_choice(trial)

        if choice == info['choice']:
            ncorrect += 1

        if info['context'] == 'm':
            results['mm'].setdefault(coh_m, []).append(choice)
            results['mc'].setdefault(coh_c, []).append(choice)
        else:
            results['cm'].setdefault(coh_m, []).append(choice)
            results['cc'].setdefault(coh_c, []).append(choice)
    print("[ {}.psychometric_function ] {:.2f}% correct.".format(
        THIS, 100 * ncorrect / ntrials))

    for cond in results:
        choice_by_coh = results[cond]

        cohs = np.sort(np.array(choice_by_coh.keys()))
        p0 = np.zeros(len(cohs))
        for i, coh in enumerate(cohs):
            choices = np.array(choice_by_coh[coh])
            p0[i] = 1 - np.sum(choices) / len(choices)
        scaled_cohs = SCALE * cohs

        results[cond] = (scaled_cohs, p0)

    #-------------------------------------------------------------------------------------
    # Plot
    #-------------------------------------------------------------------------------------

    if plots is not None:
        ms = kwargs.get('ms', 5)
        color_m = '0.2'
        color_c = Figure.colors('darkblue')

        for cond, result in results.items():
            # Context
            if cond[0] == 'm':
                color = color_m
                label = 'Motion context'
            else:
                color = color_c
                label = 'Color context'

            # Stimulus
            if cond[1] == 'm':
                plot = plots['m']
            else:
                plot = plots['c']

            # Result
            scaled_cohs, p0 = result

            # Data points
            plot.plot(scaled_cohs,
                      100 * p0,
                      'o',
                      ms=ms,
                      mew=0,
                      mfc=color,
                      zorder=10)

            # Fit
            try:
                popt, func = fittools.fit_psychometric(scaled_cohs, p0)

                fit_cohs = np.linspace(min(scaled_cohs), max(scaled_cohs), 201)
                fit_p0 = func(fit_cohs, **popt)
                plot.plot(fit_cohs,
                          100 * fit_p0,
                          color=color,
                          lw=1,
                          zorder=5,
                          label=label)
            except RuntimeError:
                print("[ {}.psychometric_function ]".format(THIS) +
                      " Unable to fit, drawing a line through the points.")
                plot.plot(scaled_cohs,
                          100 * p0,
                          color=color,
                          lw=1,
                          zorder=5,
                          label=label)

            plot.lim('x', scaled_cohs)
            plot.ylim(0, 100)

    #-------------------------------------------------------------------------------------

    return results
def psychometric_function(trialsfile, plot=None, **kwargs):
    """
    Compute and plot the sychometric function.

    """
    # Load trials
    with open(trialsfile) as f:
        trials = pickle.load(f)
    ntrials = len(trials)

    # Get modalities
    mods = list(set([trial['info']['modality'] for trial in trials]))

    #-------------------------------------------------------------------------------------
    # Compute psychometric function
    #-------------------------------------------------------------------------------------

    results         = {mod: {} for mod in mods}
    ncorrect_by_mod = {mod: 0  for mod in mods}
    ntrials_by_mod  = {mod: 0  for mod in mods}
    for trial in trials:
        # Condition
        info = trial['info']
        mod  = info['modality']
        freq = info['freq']
        ntrials_by_mod[mod] += 1

        # Choice
        choice = get_choice(trial)
        results[mod].setdefault(freq, []).append(choice)

        # Correct
        if choice == info['choice']:
            ncorrect_by_mod[mod] += 1

    # Report overall performance
    pcorrect_by_mod = {mod: 100*ncorrect_by_mod[mod]/ntrials_by_mod[mod] for mod in mods}
    print("[ {}.psychometric_function ]".format(THIS))
    print("  v  {:.2f}% correct.".format(pcorrect_by_mod['v']))
    print("   a {:.2f}% correct.".format(pcorrect_by_mod['a']))
    print("  va {:.2f}% correct.".format(pcorrect_by_mod['va']))

    # Psychometric function
    for mod in mods:
        choice_by_freq = results[mod]

        freqs = np.sort(np.array(choice_by_freq.keys()))
        p0    = np.zeros(len(freqs))
        for i, freq in enumerate(freqs):
            choices = np.array(choice_by_freq[freq])
            p0[i]   = 1 - np.sum(choices)/len(choices)

        results[mod] = (freqs, p0)

    #-------------------------------------------------------------------------------------
    # Plot
    #-------------------------------------------------------------------------------------

    if plot is not None:
        sigmas = {}
        for mod in ['v', 'a', 'va']:
            freqs, p0 = results[mod]

            # Data
            prop = {'ms':     kwargs.get('ms',  6),
                    'mfc':    kwargs.get('mfc', colors[mod]),
                    'mew':    kwargs.get('mew', 0),
                    'zorder': 10}
            plot.plot(freqs, 100*p0, 'o', **prop)

            if mod == 'v':
                label = 'Visual'
            elif mod == 'a':
                label = 'Auditory'
            elif mod == 'va':
                label = 'Multisensory'
            else:
                raise ValueError(" [ {}.psychometric_function ] Unknown modality.".
                                 format(THIS))

            # Fit
            prop = {'color':  kwargs.get('mfc', colors[mod]),
                    'lw':     kwargs.get('lw',  1),
                    'label':  label,
                    'zorder': 5}
            try:
                popt, func = fittools.fit_psychometric(freqs, p0)

                fit_freqs = np.linspace(min(freqs), max(freqs), 201)
                fit_p0    = func(fit_freqs, **popt)
                plot.plot(fit_freqs, 100*fit_p0, **prop)

                sigmas[mod] = popt['sigma']
            except RuntimeError:
                print("[ {}.psychometric_function ]".format(THIS)
                      + " Unable to fit, drawing a line through the points.")
                plot.plot(freqs, 100*p0, **prop)

        plot.ylim(0, 100)
        plot.yticks([0, 25, 50, 75, 100])
        plot.lim('x', freqs)

        # Is it optimal?
        print("")
        print("  Optimality test")
        print("  ---------------")
        for mod in ['v', 'a', 'va']:
            print("  sigma_{:<2} = {:.6f}".format(mod, sigmas[mod]))
        print("  1/sigma_v**2 + 1/sigma_a**2 = {:.6f}"
              .format(1/sigmas['v']**2 + 1/sigmas['a']**2))
        print("  1/sigma_va**2               = {:.6f}".format(1/sigmas['va']**2))
Example #4
0
def psychometric_function(trialsfile, plot=None, **kwargs):
    """
    Compute and plot the sychometric function.

    """
    # Load trials
    with open(trialsfile) as f:
        trials = pickle.load(f)
    ntrials = len(trials)

    # Get modalities
    mods = list(set([trial['info']['modality'] for trial in trials]))

    #-------------------------------------------------------------------------------------
    # Compute psychometric function
    #-------------------------------------------------------------------------------------

    results = {mod: {} for mod in mods}
    ncorrect_by_mod = {mod: 0 for mod in mods}
    ntrials_by_mod = {mod: 0 for mod in mods}
    for trial in trials:
        # Condition
        info = trial['info']
        mod = info['modality']
        freq = info['freq']
        ntrials_by_mod[mod] += 1

        # Choice
        choice = get_choice(trial)
        results[mod].setdefault(freq, []).append(choice)

        # Correct
        if choice == info['choice']:
            ncorrect_by_mod[mod] += 1

    # Report overall performance
    pcorrect_by_mod = {
        mod: 100 * ncorrect_by_mod[mod] / ntrials_by_mod[mod]
        for mod in mods
    }
    print("[ {}.psychometric_function ]".format(THIS))
    print("  v  {:.2f}% correct.".format(pcorrect_by_mod['v']))
    print("   a {:.2f}% correct.".format(pcorrect_by_mod['a']))
    print("  va {:.2f}% correct.".format(pcorrect_by_mod['va']))

    # Psychometric function
    for mod in mods:
        choice_by_freq = results[mod]

        freqs = np.sort(np.array(choice_by_freq.keys()))
        p0 = np.zeros(len(freqs))
        for i, freq in enumerate(freqs):
            choices = np.array(choice_by_freq[freq])
            p0[i] = 1 - np.sum(choices) / len(choices)

        results[mod] = (freqs, p0)

    #-------------------------------------------------------------------------------------
    # Plot
    #-------------------------------------------------------------------------------------

    if plot is not None:
        sigmas = {}
        for mod in ['v', 'a', 'va']:
            freqs, p0 = results[mod]

            # Data
            prop = {
                'ms': kwargs.get('ms', 6),
                'mfc': kwargs.get('mfc', colors[mod]),
                'mew': kwargs.get('mew', 0),
                'zorder': 10
            }
            plot.plot(freqs, 100 * p0, 'o', **prop)

            if mod == 'v':
                label = 'Visual'
            elif mod == 'a':
                label = 'Auditory'
            elif mod == 'va':
                label = 'Multisensory'
            else:
                raise ValueError(
                    " [ {}.psychometric_function ] Unknown modality.".format(
                        THIS))

            # Fit
            prop = {
                'color': kwargs.get('mfc', colors[mod]),
                'lw': kwargs.get('lw', 1),
                'label': label,
                'zorder': 5
            }
            try:
                popt, func = fittools.fit_psychometric(freqs, p0)

                fit_freqs = np.linspace(min(freqs), max(freqs), 201)
                fit_p0 = func(fit_freqs, **popt)
                plot.plot(fit_freqs, 100 * fit_p0, **prop)

                sigmas[mod] = popt['sigma']
            except RuntimeError:
                print("[ {}.psychometric_function ]".format(THIS) +
                      " Unable to fit, drawing a line through the points.")
                plot.plot(freqs, 100 * p0, **prop)

        plot.ylim(0, 100)
        plot.yticks([0, 25, 50, 75, 100])
        plot.lim('x', freqs)

        # Is it optimal?
        print("")
        print("  Optimality test")
        print("  ---------------")
        for mod in ['v', 'a', 'va']:
            print("  sigma_{:<2} = {:.6f}".format(mod, sigmas[mod]))
        print("  1/sigma_v**2 + 1/sigma_a**2 = {:.6f}".format(
            1 / sigmas['v']**2 + 1 / sigmas['a']**2))
        print("  1/sigma_va**2               = {:.6f}".format(1 /
                                                              sigmas['va']**2))
def psychometric_function(trialsfile, plot=None, threshold=False, **kwargs):
    """
    Compute and plot the sychometric function.

    """
    # Load trials
    trials, ntrials = load_trials(trialsfile)

    #-------------------------------------------------------------------------------------
    # Compute psychometric function
    #-------------------------------------------------------------------------------------

    choice_by_coh = {}
    ntot     = 0
    ncorrect = 0
    for trial in trials:
        info = trial['info']

        # Signed coherence
        coh = info['in_out']*info['coh']

        # Choice
        choice = get_choice(trial, threshold)
        if choice is None:
            continue
        if coh != 0:
            ntot += 1
        if isinstance(choice, tuple):
            choice, _ = choice
        choice_by_coh.setdefault(coh, []).append(choice)

        # Correct
        if coh != 0 and choice == info['choice']:
            ncorrect += 1

    # Report overall performance
    pcorrect = 100*ncorrect/ntot
    print("[ {}.psychometric_function ] {}/{} = {:.2f}% correct."
          .format(THIS, ncorrect, ntot, pcorrect))

    cohs = np.sort(np.array(choice_by_coh.keys()))
    p0   = np.zeros(len(cohs))
    for i, coh in enumerate(cohs):
        choices = np.array(choice_by_coh[coh])
        p0[i]   = 1 - np.sum(choices)/len(choices)
    scaled_cohs = SCALE*cohs

    #-------------------------------------------------------------------------------------
    # Plot
    #-------------------------------------------------------------------------------------

    if plot is not None:
        # Data
        prop = {'ms':     kwargs.get('ms',  6),
                'mfc':    kwargs.get('mfc', '0.2'),
                'mew':    kwargs.get('mew', 0),
                'zorder': 10}
        plot.plot(scaled_cohs, 100*p0, 'o', **prop)

        # Fit
        prop = {'color':  kwargs.get('mfc', '0.2'),
                'lw':     kwargs.get('lw',  1),
                'zorder': 5}
        try:
            popt, func = fittools.fit_psychometric(scaled_cohs, p0)

            fit_cohs = np.linspace(min(scaled_cohs), max(scaled_cohs), 201)
            fit_p0   = func(fit_cohs, **popt)
            plot.plot(fit_cohs, 100*fit_p0, **prop)
        except RuntimeError:
            print("[ {}.psychometric_function ]".format(THIS)
                  + " Unable to fit, drawing a line through the points.")
            plot.plot(scaled_cohs, 100*p0, **prop)

        plot.ylim(0, 100)
        plot.yticks([0, 25, 50, 75, 100])
        plot.lim('x', scaled_cohs)
def psychometric_function(trialsfile, plots=None, **kwargs):
    """
    Psychometric function.

    """
    # Load trials
    trials, ntrials = load_trials(trialsfile)

    #-------------------------------------------------------------------------------------
    # Compute psychometric function
    #-------------------------------------------------------------------------------------

    results  = {cond: {} for cond in ['mm', 'mc', 'cm', 'cc']}
    ncorrect = 0
    for trial in trials:
        info   = trial['info']
        coh_m  = info['left_right_m']*info['coh_m']
        coh_c  = info['left_right_c']*info['coh_c']
        choice = get_choice(trial)

        if choice == info['choice']:
            ncorrect += 1

        if info['context'] == 'm':
            results['mm'].setdefault(coh_m, []).append(choice)
            results['mc'].setdefault(coh_c, []).append(choice)
        else:
            results['cm'].setdefault(coh_m, []).append(choice)
            results['cc'].setdefault(coh_c, []).append(choice)
    print("[ {}.psychometric_function ] {:.2f}% correct."
          .format(THIS, 100*ncorrect/ntrials))

    for cond in results:
        choice_by_coh = results[cond]

        cohs = np.sort(np.array(choice_by_coh.keys()))
        p0   = np.zeros(len(cohs))
        for i, coh in enumerate(cohs):
            choices = np.array(choice_by_coh[coh])
            p0[i]   = 1 - np.sum(choices)/len(choices)
        scaled_cohs = SCALE*cohs

        results[cond] = (scaled_cohs, p0)

    #-------------------------------------------------------------------------------------
    # Plot
    #-------------------------------------------------------------------------------------

    if plots is not None:
        ms      = kwargs.get('ms', 5)
        color_m = '0.2'
        color_c = Figure.colors('darkblue')

        for cond, result in results.items():
            # Context
            if cond[0] == 'm':
                color = color_m
                label = 'Motion context'
            else:
                color = color_c
                label = 'Color context'

            # Stimulus
            if cond[1] == 'm':
                plot = plots['m']
            else:
                plot = plots['c']

            # Result
            scaled_cohs, p0 = result

            # Data points
            plot.plot(scaled_cohs, 100*p0, 'o', ms=ms, mew=0, mfc=color, zorder=10)

            # Fit
            try:
                popt, func = fittools.fit_psychometric(scaled_cohs, p0)

                fit_cohs = np.linspace(min(scaled_cohs), max(scaled_cohs), 201)
                fit_p0   = func(fit_cohs, **popt)
                plot.plot(fit_cohs, 100*fit_p0, color=color, lw=1, zorder=5, label=label)
            except RuntimeError:
                print("[ {}.psychometric_function ]".format(THIS)
                      + " Unable to fit, drawing a line through the points.")
                plot.plot(scaled_cohs, 100*p0, color=color, lw=1, zorder=5, label=label)

            plot.lim('x', scaled_cohs)
            plot.ylim(0, 100)

    #-------------------------------------------------------------------------------------

    return results