Esempio n. 1
0
def compare_gainControl(fitBase, normType, lossType, expInd, conSteps=11):
  ''' given a fitBase (str) with associated norm/loss indices, and experiment indices, get the RVC for all cells
  '''
  _, _, _, _, nCells, dir = hf.get_exp_params(expInd);

  if expInd == 1:
    dir = str(dir+'Structures/');
  else:
    dir = str(dir+'structures/');

  dataList = hf.np_smart_load(str(dir + 'dataList.npy'));

  fl_str = hf.fitList_name(fitBase, normType, lossType);
  fitList = hf.np_smart_load(str(dir + fl_str));

  rvcs = np.nan * np.zeros((nCells, conSteps));

  for i in range(nCells):
    print('cell %d' % i);
    cellStr = hf.np_smart_load(str(dir + dataList['unitName'][i] + '_sfm.npy'));
    currParams = fitList[i]['params'];

    rvcs[i, :] = evaluate_RVC(cellStr, currParams, normType=normType, expInd=expInd, conSteps=conSteps);

  if expInd == 1:
    np.save(str('rvcV1_' + fl_str), rvcs);
  elif expInd == 3:
    np.save(str('rvcLGN_' + fl_str), rvcs);

  return rvcs;
Esempio n. 2
0
def compute_diffs(lossType, expInd=1, baseStr='holdoutFits', date='181121'):

  _, nDisps, _, nCons, nCells, dataLoc = hf.get_exp_params(expInd);
  if expInd == 1: # main V1 branch
    dataLoc = dataLoc + 'Structures/';
  else:
    dataLoc = dataLoc + 'structures/';

  # then the loss type
  if lossType == 1:
    lossSuf = '_sqrt.npy';
  elif lossType == 2:
    lossSuf = '_poiss.npy';
  elif lossType == 3:
    lossSuf = '_modPoiss.npy';

  losses = np.nan * np.zeros((nCells, nDisps, nCons, nFits));
  preds = np.nan * np.zeros((nCells, nDisps, nCons, nFits));

  flat   = np.load(dataLoc + '%s_%s_flat%s' % (baseStr, date, lossSuf), encoding='latin1').item();
  weight = np.load(dataLoc + '%s_%s_wght%s' % (baseStr, date, lossSuf), encoding='latin1').item();

  for i in range(nCells):

    try:

      losses[i, :, :, 0] = np.mean(flat[i]['NLL'], 2)
      preds[i, :, :, 0] = np.mean(flat[i]['holdoutNLL'], 2)

      losses[i, :, :, 1] = np.mean(weight[i]['NLL'], 2)
      preds[i, :, :, 1] = np.mean(weight[i]['holdoutNLL'], 2)

    except:
      continue;  

  diffs_loss = losses[:, :, :, 0] - losses[:, :, :, 1];
  diffs_pred = preds[:, :, :, 0] - preds[:, :, :, 1];

  return diffs_loss, diffs_pred, losses, preds, flat, weight;
Esempio n. 3
0
            dispAx[d][i].set_title('D%02d - sf tuning %s' % (d, labels[i]))
            dispAx[d][i].legend()

    saveName = "/allCons_cell_%03d.pdf" % (cellNum)
    full_save = os.path.dirname(str(save_loc + 'byDisp%s/' % rvcFlag))
    if not os.path.exists(full_save):
        os.makedirs(full_save)
    pdfSv = pltSave.PdfPages(full_save + saveName)
    for f in fDisp:
        pdfSv.savefig(f)
        plt.close(f)
    pdfSv.close()

# #### Plot just sfMix contrasts

mixCons = hf.get_exp_params(expInd).nCons
minResp = np.min(np.min(np.min(respMean[~np.isnan(respMean)])))
maxResp = np.max(np.max(np.max(respMean[~np.isnan(respMean)])))

f, sfMixAx = plt.subplots(mixCons, nDisps, figsize=(25, 1.5 * 20))

sfs_plot = np.logspace(np.log10(all_sfs[0]), np.log10(all_sfs[-1]), 100)

for d in range(nDisps):
    v_cons = np.array(val_con_by_disp[d])
    n_v_cons = len(v_cons)
    v_cons = v_cons[np.arange(np.maximum(0, n_v_cons - mixCons), n_v_cons)]
    # max(1, .) for when there are fewer contrasts than 4
    n_v_cons = len(v_cons)

    for c in reversed(range(n_v_cons)):
Esempio n. 4
0
### Load datalist, cell, and "onset list"
expName = hf.get_datalist(expDir)
onsetName = 'onset_transients%s.npy' % onsStr

# auto...
path = '%sstructures/' % expDir
dataList = hf.np_smart_load(path + expName)
expName = 'sfBB_core'
unitNm = dataList['unitName'][cellNum - 1]
cell = hf.np_smart_load('%s%s_sfBB.npy' % (path, unitNm))
expInfo = cell[expName]
byTrial = expInfo['trial']

# we're enforcing expInd = -1 for now...
expInd = -1
stimDur = hf.get_exp_params(expInd).stimDur

msTenthToS = 1e-4
# the spike times are in 1/10th ms, so multiply by 1e-4 to convert to S
nonBlank = np.where(np.logical_or(byTrial['maskOn'], byTrial['maskOn']))[0]
nTrials = len(nonBlank)
# print('%d non-blank trials considered' % nTrials);
allSpikes = np.hstack(expInfo['spikeTimes'][nonBlank] * msTenthToS)

maskInd, baseInd = hf_sf.get_mask_base_inds()

# what is the length (in mS) of one cycle (mask, base separately)
maskTf = np.unique(expInfo['trial']['tf'][maskInd, :])[0]
baseTf = np.unique(expInfo['trial']['tf'][baseInd, :])[0]
cycleDur_mask = 1e3 / maskTf  # guaranteed only one TF value
cycleDur_base = 1e3 / baseTf  # guaranteed only one TF value
Esempio n. 5
0
def phase_by_cond(which_cell,
                  data,
                  expInd,
                  disp,
                  con,
                  sf,
                  sv_loc=save_loc,
                  dir=-1,
                  cycle_fold=2,
                  n_bins_fold=8,
                  dp=dataPath,
                  expName=expName,
                  date_suffix=''):
    ''' Given a cell and the disp/con/sf indices, plot the spike raster for each trial, a folded PSTH,
      and finally the response phase - first relative to trial onset, and finally relative to the stimulus phase 
      
      dir = -1 or +1 (i.e. stimulus moving left or right?)
      cycle_fold = over how many stimulus cycles to fold when creating folded psth?
      n_bins_fold = how many bins per stimulus period when folding?
  '''
    dataList = hf.np_smart_load(str(dp + expName))
    save_base = sv_loc + 'phasePlots_%s/' % date_suffix

    val_trials, allDisps, allCons, allSfs = hf.get_valid_trials(
        data, disp, con, sf, expInd)

    if not np.any(
            val_trials[0]
    ):  # val_trials[0] will be the array of valid trial indices --> if it's empty, leave!
        warnings.warn('this condition is not valid')
        return

    ### SINGLE GRATINGS
    if disp == 0:

        # get the phase relative to the stimulus
        ph_rel_stim, stim_ph, resp_ph, all_tf = hf.get_true_phase(
            data, val_trials, expInd, dir)
        # compute the fourier amplitudes
        stimDur = hf.get_exp_params(expInd).stimDur
        psth_val, _ = hf.make_psth(data['spikeTimes'][val_trials],
                                   stimDur=stimDur)
        _, rel_amp, full_fourier = hf.spike_fft(psth_val, all_tf, stimDur)

        # now plot!
        f, ax = plt.subplots(3, 2, figsize=(20, 30))

        relSpikes = data['spikeTimes'][val_trials]
        colors = cm.rainbow(np.linspace(0, 1, len(val_trials[0])))

        #####
        # plot spike raster - trial-by-trial
        #####
        # only works for SINGLE GRATINGS
        # draw the beginning of each cycle for each trial
        ax = plt.subplot(3, 1, 1)
        for i in range(len(relSpikes)):
            ax.scatter(relSpikes[i],
                       i * np.ones_like(relSpikes[i]),
                       s=45,
                       color=colors[i])
            stimPh = stim_ph[i]
            stimPeriod = np.divide(1.0, all_tf[i])
            stimDur = hf.get_exp_params(expInd).stimDur
            # i.e. at what point during the trial (in s) does the stimulus component first begin a cycle?
            firstPh0 = hf.first_ph0(stimPh, all_tf[i])[1]

            for j in range(len(all_tf[i])):
                allPh0 = [
                    stimPeriod[j] * np.arange(-1, stimDur * all_tf[i][j]) +
                    firstPh0[j]
                ]
                allPh90 = allPh0 + stimPeriod[j] / 4
                allPh180 = allPh90 + stimPeriod[j] / 4
                allPh270 = allPh180 + stimPeriod[j] / 4
            ax.errorbar(allPh0[0],
                        i * np.ones_like(allPh0[0]),
                        0.25,
                        linestyle='none',
                        color='k',
                        linewidth=1)
            ax.errorbar(allPh90[0],
                        i * np.ones_like(allPh0[0]),
                        0.05,
                        linestyle='none',
                        color='k',
                        linewidth=1)
            ax.errorbar(allPh180[0],
                        i * np.ones_like(allPh0[0]),
                        0.05,
                        linestyle='none',
                        color='k',
                        linewidth=1)
            ax.errorbar(allPh270[0],
                        i * np.ones_like(allPh0[0]),
                        0.05,
                        linestyle='none',
                        color='k',
                        linewidth=1)
        ax.set_xlabel('time (s)')
        ax.set_ylabel('repetition #')
        ax.set_title('Spike rasters')

        #####
        # plot PSTH - per trial, but folded over N cycles
        #####
        # only works for SINGLE GRATINGS
        ax = plt.subplot(3, 1, 2)

        for i in range(len(relSpikes)):
            _, bin_edges, psth_norm = hf.fold_psth(relSpikes[i],
                                                   all_tf[i],
                                                   stim_ph[i],
                                                   cycle_fold,
                                                   n_bins_fold,
                                                   dir=dir)
            plt.plot(bin_edges[0:-1], i - 0.5 + psth_norm, color=colors[i])
            stimPeriod = np.divide(1.0, all_tf[i])
            for j in range(cycle_fold):
                cycStart = plt.axvline(j * stimPeriod[0])
                cycHalf = plt.axvline((j + 0.5) * stimPeriod[0],
                                      linestyle='--')
        ax.set_xlabel('time (s)')
        ax.set_ylabel('spike count (normalized by trial)')
        ax.set_title('PSTH folded')
        ax.set_xlim(
            [-stimPeriod[0] / 4.0, (cycle_fold + 0.25) * stimPeriod[0]])
        ax.legend((cycStart, cycHalf), ('ph = 0', 'ph = 180'))

        #####
        # plot response phase - without accounting for stimulus phase
        #####
        ax = plt.subplot(3, 2, 5, projection='polar')
        ax.scatter(np.radians(resp_ph),
                   rel_amp,
                   s=60,
                   color=colors,
                   clip_on=False)
        ax.set_title('Stimulus-blind')
        ax.set_ylim(auto='True')
        polar_ylim = ax.get_ylim()

        # now, compute the average amplitude/phase over all trials
        [avg_r, avg_ph, _, _] = hf.polar_vec_mean([rel_amp], [ph_rel_stim])
        avg_r = avg_r[0]
        # just get it out of the array!
        avg_ph = avg_ph[0]
        # just get it out of the array!

        #####
        # plot response phase - relative to stimulus phase
        #####
        ax = plt.subplot(3, 2, 6, projection='polar')
        ax.scatter(np.radians(ph_rel_stim),
                   rel_amp,
                   s=60,
                   color=colors,
                   clip_on=False)
        ax.plot([0, np.radians(avg_ph)], [0, avg_r],
                color='k',
                linestyle='--',
                clip_on=False)
        ax.set_ylim(polar_ylim)
        ax.set_title('Stimulus-accounted')

    ### MIXTURE STIMULI
    elif disp > 0:
        nComp = hf.get_exp_params(expInd).nStimComp
        # a useful function for swapping inside/outside of nested list...
        switch_inner_outer = lambda arr: [[x[i] for x in arr]
                                          for i in range(len(arr[0]))]

        ### first, gather the isolated component responses from the mixture
        val_trials, allDisp, allCons, allSfs = hf.get_valid_trials(
            data, disp, con, sf, expInd)
        # outer-most list (for ph_rel_stim, stim_phase, resp_phase) is trial/repetition, inner lists are by component
        ph_rel_stim, stim_phase, resp_phase, all_tf = hf.get_true_phase(
            data, val_trials, expInd, dir=dir)
        # f1all is nComp lists, each with nReps/nTrials values
        _, _, _, f1all, conByComp, sfByComp = hf.get_isolated_response(
            data, val_trials)
        # need to switch ph_rel_stim (and resp_phase) to be lists of phases by component (rather than list of phases by trial)
        ph_rel_stim = switch_inner_outer(ph_rel_stim)
        resp_phase = switch_inner_outer(resp_phase)
        # compute vector means
        r_comp, th_comp, _, _ = hf.polar_vec_mean(f1all, ph_rel_stim)

        nrow = 1 + nComp
        ncol = 2
        f, ax = plt.subplots(nrow, ncol, figsize=(ncol * 10, nrow * 10))

        # in-mixture response phase - NOT stimulus-aligned; just demonstrate for 1st component
        colors = cm.rainbow(np.linspace(0, 1, len(val_trials[0])))
        ax = plt.subplot(
            nrow, 1, 1, projection='polar'
        )  # pretend only one column, i.e. take up whole top row
        ax.scatter(np.radians(resp_phase[0]),
                   hf.flatten(f1all[0]),
                   s=45,
                   color=colors,
                   clip_on=False)
        ax.set_ylim([0, 1.1 * np.max(hf.flatten(f1all[0]))])
        ax.set_title('Stimulus-blind (compound; comp #1)')

        for i in range(nComp):

            # compute the isolated response
            # Then, pick one of the components and get the response (when presented in isolation)
            # phases/amplitudes and align relative to the stimulus phase
            isolConInd = np.where(allCons == conByComp[i])[0][0]
            # unwrap fully (will be only one value...)
            isolSfInd = np.where(allSfs == sfByComp[i])[0][0]
            val_trials_isol, _, _, _ = hf.get_valid_trials(data,
                                                           disp=0,
                                                           con=isolConInd,
                                                           sf=isolSfInd,
                                                           expInd=expInd)

            ph_rel_stim_isol, stim_phase_isol, resp_phase_isol, all_tf_isol = hf.get_true_phase(
                data, val_trials_isol, expInd, dir=dir)
            stimDur = hf.get_exp_params(expInd).stimDur
            psth_val, _ = hf.make_psth(data['spikeTimes'][val_trials_isol],
                                       stimDur=stimDur)
            _, rel_amp_isol, _ = hf.spike_fft(psth_val, all_tf_isol, stimDur)
            # and compute vector mean
            r_isol, th_isol, _, _ = hf.polar_vec_mean([rel_amp_isol],
                                                      [ph_rel_stim_isol])

            # isolated response phase - relative to stimulus phase
            colors = cm.rainbow(np.linspace(0, 1, len(val_trials[0])))
            ax = plt.subplot(nrow, ncol, 2 * (i + 1) + 1, projection='polar')
            ax.scatter(np.radians(ph_rel_stim_isol),
                       rel_amp_isol,
                       s=45,
                       color=colors,
                       clip_on=False)
            ax.plot([0, np.radians(th_isol[0])], [0, r_isol[0]],
                    ls='--',
                    color='k')
            ax.set_ylim([0, 1.1 * np.max(rel_amp_isol)])
            ax.set_title('isolated (r, phi) = (%.2f, %.2f)' %
                         (r_isol[0], th_isol[0]))

            # in-mixture response phase - relative to stimulus phase
            colors = cm.rainbow(np.linspace(0, 1, len(val_trials[0])))
            ax = plt.subplot(nrow, ncol, 2 * (i + 2), projection='polar')
            ax.scatter(np.radians(ph_rel_stim[i]),
                       hf.flatten(f1all[i]),
                       s=45,
                       color=colors,
                       clip_on=False)
            ax.plot([0, np.radians(th_comp[i])], [0, r_comp[i]],
                    ls='--',
                    color='k')
            ax.set_ylim([0, 1.1 * np.max(rel_amp_isol)])
            ax.set_title('compound (r, phi) = (%.2f, %.2f)' %
                         (r_comp[i], th_comp[i]))

    ## Common to all
    f.subplots_adjust(wspace=0.2, hspace=0.25)
    f1f0_ratio = hf.compute_f1f0(data,
                                 which_cell,
                                 expInd,
                                 dp,
                                 descrFitName_f0=descrFit_f0)[0]
    try:  # not always exists
        cell_label = dataList['unitType'][which_cell - 1]
    except:  # always works
        cell_label = dataList['unitArea'][which_cell - 1]
    f.suptitle('%s (f1f0: %.2f) #%d: disp %d, con %.2f, sf %.2f' %
               (cell_label, f1f0_ratio, which_cell, allDisps[disp],
                allCons[con], allSfs[sf]))

    saveName = "/cell_%03d_d%dsf%dcon%d_phase.pdf" % (which_cell, disp, sf,
                                                      con)
    save_loc = save_base + "cell_%03d/" % which_cell
    full_save = os.path.dirname(str(save_loc))
    if not os.path.exists(full_save):
        os.makedirs(full_save)
    pdfSv = pltSave.PdfPages(full_save + saveName)
    pdfSv.savefig(f)
    plt.close(f)
    pdfSv.close()
Esempio n. 6
0
def plot_phase_advance(which_cell,
                       disp,
                       sv_loc=save_loc,
                       dir=-1,
                       dp=dataPath,
                       expName=expName,
                       phAdvStr=phAdvName,
                       rvcStr=rvcName,
                       date_suffix=''):
    ''' RVC, resp-X-phase, phase advance model split by SF within each cell/dispersion condition
      1. response-versus-contrast; shows original and adjusted response
      2. polar plot of response amplitude and phase with phase advance model fit
      3. response amplitude (x) vs. phase (y) with the linear phase advance model fit
  '''

    # basics
    dataList = hf.np_smart_load(str(dp + expName))
    cellName = dataList['unitName'][which_cell - 1]
    expInd = hf.get_exp_ind(dp, cellName)[0]
    cellStruct = hf.np_smart_load(str(dp + cellName + '_sfm.npy'))

    rvcFits = hf.np_smart_load(str(dp + hf.phase_fit_name(rvcStr, dir)))
    rvcFits = rvcFits[which_cell - 1]
    rvc_model = hf.get_rvc_model()

    phAdvFits = hf.np_smart_load(str(dp + hf.phase_fit_name(phAdvStr, dir)))
    phAdvFits = phAdvFits[which_cell - 1]
    phAdv_model = hf.get_phAdv_model()

    save_base = sv_loc + 'phasePlots_%s/' % date_suffix

    # gather/compute everything we need
    data = cellStruct['sfm']['exp']['trial']
    _, stimVals, val_con_by_disp, validByStimVal, _ = hf.tabulate_responses(
        data, expInd)

    valDisp = validByStimVal[0]
    valCon = validByStimVal[1]
    valSf = validByStimVal[2]

    allDisps = stimVals[0]
    allCons = stimVals[1]
    allSfs = stimVals[2]

    con_inds = val_con_by_disp[disp]

    # now get ready to plot
    fPhaseAdv = []

    # we will summarize for all spatial frequencies for a given cell!
    for j in range(len(allSfs)):
        # first, get the responses and phases that we need:
        amps = []
        phis = []
        sf = j
        for i in con_inds:
            val_trials = np.where(valDisp[disp] & valCon[i] & valSf[sf])

            # get the phase of the response relative to the stimulus (ph_rel_stim)
            ph_rel_stim, stim_ph, resp_ph, all_tf = hf.get_true_phase(
                data, val_trials, expInd, dir=dir)
            phis.append(ph_rel_stim)
            # get the relevant amplitudes (i.e. the amplitudes at the stimulus TF)
            stimDur = hf.get_exp_params(expInd).stimDur
            psth_val, _ = hf.make_psth(data['spikeTimes'][val_trials],
                                       stimDur=stimDur)
            _, rel_amp, _ = hf.spike_fft(psth_val, all_tf, stimDur=stimDur)
            amps.append(rel_amp)

        r, th, _, _ = hf.polar_vec_mean(amps, phis)
        # mean amp/phase (outputs 1/2); std/var for amp/phase (outputs 3/4)
        # get the models/fits that we need:
        con_values = allCons[con_inds]
        ## phase advance
        opt_params_phAdv = phAdvFits['params'][sf]
        ph_adv = phAdvFits['phAdv'][sf]
        ## rvc
        opt_params_rvc = rvcFits[disp]['params'][sf]
        con_gain = rvcFits[disp]['conGain'][sf]
        adj_means = rvcFits[disp]['adjMeans'][sf]
        if disp == 1:  # then sum adj_means (saved by component)
            adj_means = [np.sum(x, 1) if x else [] for x in adj_means]
        # (Above) remember that we have to project the amp/phase vector onto the "correct" phase for estimate of noiseless response
        ## now get ready to plot!
        f, ax = plt.subplots(2, 2, figsize=(20, 10))
        fPhaseAdv.append(f)

        n_conds = len(r)
        colors = cm.viridis(np.linspace(0, 0.95, n_conds))

        #####
        ## 1. now for plotting: first, response amplitude (with linear contrast)
        #####
        plot_cons = np.linspace(0, 1, 100)
        mod_fit = rvc_model(opt_params_rvc[0], opt_params_rvc[1],
                            opt_params_rvc[2], plot_cons)

        ax = plt.subplot(2, 2, 1)
        plot_amp = adj_means
        plt_measured = ax.scatter(allCons[con_inds],
                                  plot_amp,
                                  s=100,
                                  color=colors,
                                  label='ph. corr')
        plt_og = ax.plot(allCons[con_inds],
                         r,
                         linestyle='None',
                         marker='o',
                         markeredgecolor='k',
                         markerfacecolor='None',
                         alpha=0.5,
                         label='vec. mean')
        plt_fit = ax.plot(plot_cons,
                          mod_fit,
                          linestyle='--',
                          color='k',
                          label='rvc fit')
        ax.set_xlabel('contrast')
        ax.set_ylabel('response (f1)')
        ax.set_title('response versus contrast')
        ax.legend(loc='upper left')

        # also summarize the model fit on this plot
        ymax = np.maximum(np.max(r), np.max(mod_fit))
        plt.text(0.8,
                 0.30 * ymax,
                 'b: %.2f' % (opt_params_rvc[0]),
                 fontsize=12,
                 horizontalalignment='center',
                 verticalalignment='center')
        plt.text(0.8,
                 0.20 * ymax,
                 'slope:%.2f' % (opt_params_rvc[1]),
                 fontsize=12,
                 horizontalalignment='center',
                 verticalalignment='center')
        plt.text(0.8,
                 0.10 * ymax,
                 'c0: %.2f' % (opt_params_rvc[2]),
                 fontsize=12,
                 horizontalalignment='center',
                 verticalalignment='center')
        plt.text(0.8,
                 0.0 * ymax,
                 'con gain: %.2f' % (con_gain),
                 fontsize=12,
                 horizontalalignment='center',
                 verticalalignment='center')

        #####
        ## 3. then the fit/plot of phase as a function of ampltude
        #####
        plot_amps = np.linspace(0, np.max(r), 100)
        mod_fit = phAdv_model(opt_params_phAdv[0], opt_params_phAdv[1],
                              plot_amps)

        ax = plt.subplot(2, 1, 2)
        plt_measured = ax.scatter(r,
                                  th,
                                  s=100,
                                  color=colors,
                                  clip_on=False,
                                  label='vec. mean')
        plt_fit = ax.plot(plot_amps,
                          mod_fit,
                          linestyle='--',
                          color='k',
                          clip_on=False,
                          label='phAdv model')
        ax.set_xlabel('response amplitude')
        if phAdv_set_ylim:
            ax.set_ylim([0, 360])
        ax.set_ylabel('response phase')
        ax.set_title('phase advance with amplitude')
        ax.legend(loc='upper left')

        ## and again, summarize the model fit on the plot
        xmax = np.maximum(np.max(r), np.max(plot_amps))
        ymin = np.minimum(np.min(th), np.min(mod_fit))
        ymax = np.maximum(np.max(th), np.max(mod_fit))
        yrange = ymax - ymin
        if phAdv_set_ylim:
            if mod_fit[-1] > 260:  # then start from ymin and go dwn
                start, sign = mod_fit[-1] - 30, -1
            else:
                start, sign = mod_fit[-1] + 30, 1
            plt.text(0.9 * xmax,
                     start + 1 * 30 * sign,
                     'phi0: %.2f' % (opt_params_phAdv[0]),
                     fontsize=12,
                     horizontalalignment='center',
                     verticalalignment='center')
            plt.text(0.9 * xmax,
                     start + 2 * 30 * sign,
                     'slope:%.2f' % (opt_params_phAdv[1]),
                     fontsize=12,
                     horizontalalignment='center',
                     verticalalignment='center')
            plt.text(0.9 * xmax,
                     start + 3 * 30 * sign,
                     'phase advance: %.2f ms' % (ph_adv),
                     fontsize=12,
                     horizontalalignment='center',
                     verticalalignment='center')
        else:
            plt.text(0.8 * xmax,
                     ymin + 0.25 * yrange,
                     'phi0: %.2f' % (opt_params_phAdv[0]),
                     fontsize=12,
                     horizontalalignment='center',
                     verticalalignment='center')
            plt.text(0.8 * xmax,
                     ymin + 0.15 * yrange,
                     'slope:%.2f' % (opt_params_phAdv[1]),
                     fontsize=12,
                     horizontalalignment='center',
                     verticalalignment='center')
            plt.text(0.8 * xmax,
                     ymin + 0.05 * yrange,
                     'phase advance: %.2f ms' % (ph_adv),
                     fontsize=12,
                     horizontalalignment='center',
                     verticalalignment='center')

        #center_phi = lambda ph1, ph2: np.arcsin(np.sin(np.deg2rad(ph1) - np.deg2rad(ph2)));

        #####
        ## 2. now the polar plot of resp/phase together
        #####
        ax = plt.subplot(2, 2, 2, projection='polar')
        th_center = np.rad2deg(np.radians(-90) + np.radians(th[np.argmax(r)]))
        # "anchor" to the phase at the highest amplitude response
        #data_centered = center_phi(th, th_center);
        #model_centered = center_phi(mod_fit, th_center);
        #ax.scatter(data_centered, r, s=50, color=colors);
        #ax.plot(model_centered, plot_amps, linestyle='--', color='k');
        data_centered = np.mod(th - th_center, 360)
        model_centered = np.mod(mod_fit - th_center, 360)
        ax.scatter(np.deg2rad(data_centered), r, s=50, color=colors)
        ax.plot(np.deg2rad(model_centered),
                plot_amps,
                linestyle='--',
                color='k')
        ax.set_ylim(0, 1.25 * np.max(r))
        ax.set_title('phase advance')

        # overall title
        f.subplots_adjust(wspace=0.2, hspace=0.25)
        f1f0_ratio = hf.compute_f1f0(data,
                                     which_cell,
                                     expInd,
                                     dp,
                                     descrFitName_f0=descrFit_f0)[0]
        try:
            f.suptitle('%s (%.2f) #%d: disp %d, sf %.2f cpd' %
                       (dataList['unitType'][which_cell - 1], f1f0_ratio,
                        which_cell, allDisps[disp], allSfs[sf]))
        except:
            f.suptitle('%s (%.2f) #%d: disp %d, sf %.2f cpd' %
                       (dataList['unitArea'][which_cell - 1], f1f0_ratio,
                        which_cell, allDisps[disp], allSfs[sf]))

    saveName = "/cell_%03d_d%d_phaseAdv.pdf" % (which_cell, disp)
    save_loc = save_base + 'summary/'
    full_save = os.path.dirname(str(save_loc))
    if not os.path.exists(full_save):
        os.makedirs(full_save)
    pdfSv = pltSave.PdfPages(full_save + saveName)
    for f in fPhaseAdv:
        pdfSv.savefig(f)
        plt.close(f)
    pdfSv.close()
Esempio n. 7
0
def makeStimulus(stimFamily, conLevel, sf_c, expInd, template):

    # returns [Or, Tf, Co, Ph, Sf, trial_used]

    # 1/23/16 - This function is used to make arbitrary stimuli for use with
    # the Robbe V1 model. Rather than fitting the model responses at the actual
    # experimental stimuli, we instead can simulate from the model at any
    # arbitrary spatial frequency in order to determine peak SF
    # response/bandwidth/etc

    # If argument 'template' is given, then orientation, phase, and tf_center will be
    # taken from the template, which will be actual stimuli from that cell

    # Fixed parameters
    warnings.warn(
        'makeStimulus is not set to work for experiments besides expInd=1; take caution when making own stimulus for simulation'
    )
    exper = hf.get_exp_params(expInd)
    num_families = exper.nFamilies
    num_gratings = exper.nStimComp

    spreadVec = numpy.logspace(math.log10(.125), math.log10(1.25),
                               num_families)
    octSeries = numpy.linspace(1.5, -1.5, num_gratings)

    # set contrast and spatial frequency
    if conLevel == 1:
        total_contrast = 1
    elif conLevel == 2:
        total_contrast = 1 / 3
    elif conLevel >= 0 and conLevel <= 1:
        total_contrast = conLevel
    else:
        #warning('Contrast should be given as 1 [full] or 2 [low/one-third]; setting contrast to 1 (full)');
        total_contrast = 1
        # default to that

    spread = spreadVec[stimFamily - 1]
    profTemp = norm.pdf(octSeries, 0, spread)
    profile = profTemp / sum(profTemp)

    if stimFamily == 1:  # do this for consistency with actual experiment - for stimFamily 1, only one grating is non-zero; round gives us this
        profile = round(profile)

    Sf = numpy.power(2, octSeries + numpy.log2(sf_c))
    # final spatial frequency
    Co = numpy.dot(profile, total_contrast)
    # final contrast

    # The others

    # get orientation - IN RADIANS
    trial = template.get('sfm').get('exp').get('trial')
    OriVal = mode(trial.get('ori')[0]).mode * numpy.pi / 180
    # pick arbitrary grating, mode for this is cell's pref Ori for experiment
    Or = numpy.matlib.repmat(OriVal, 1, num_gratings)[0]
    # weird tuple [see below], just get the array we care about...

    if template.get('trial_used') is not None:  #use specified trial
        trial_to_copy = template.get('trial_used')
    else:  # get random trial for phase, TF
        # we'll draw from a random trial with the same stimulus family/contrast
        valid_blockIDs = numpy.arange(
            (stimFamily - 1) * (13 * 2) + 1 + (conLevel - 1),
            ((stimFamily) * (13 * 2) - 5) + (conLevel - 1), 2)
        # above from Robbe's plotSfMix
        num_blockIDs = len(valid_blockIDs)
        # for phase and TF
        valid_trials = trial.get('blockID') == valid_blockIDs[random.randint(
            0, num_blockIDs - 1)]  # pick a random block ID
        valid_trials = numpy.where(valid_trials)[0]
        # 0 is to get array...weird "tuple" return type...
        trial_to_copy = valid_trials[random.randint(0,
                                                    len(valid_trials) - 1)]
        # pick a random trial from within this

    trial_used = trial_to_copy

    # grab Tf and Phase [IN RADIANS] from each grating for the given trial
    Tf = numpy.asarray([i[trial_to_copy] for i in trial.get('tf')])
    Ph = numpy.asarray(
        [i[trial_to_copy] * math.pi / 180 for i in trial.get('ph')])

    return {
        'Ori': Or,
        'Tf': Tf,
        'Con': Co,
        'Ph': Ph,
        'Sf': Sf,
        'trial_used': trial_used
    }
Esempio n. 8
0
def measure_chiSq(lossType, expInd=1, date='181121'):

  _, nDisps, _, nCons, nCells, dataLoc = hf.get_exp_params(expInd);
  if expInd == 1: # main V1 branch
    dataLoc = dataLoc + 'Structures/';
  else:
    dataLoc = dataLoc + 'structures/';
  dataList = hf.np_smart_load(str(dataLoc + 'dataList.npy'));

  # then the loss type
  if lossType == 1:
    lossSuf = '_sqrt.npy';
  elif lossType == 2:
    lossSuf = '_poiss.npy';
  elif lossType == 3:
    lossSuf = '_modPoiss.npy';

  chi_wght = np.nan * np.zeros((nCells, ));
  chi_flat = np.nan * np.zeros((nCells, ));

  flat   = hf.np_smart_load(dataLoc + 'fitList_%s_flat%s' % (date, lossSuf));
  weight = hf.np_smart_load(dataLoc + 'fitList_%s_wght%s' % (date, lossSuf));

  for i in range(nCells):
    print('cell %d' % i);
    S = hf.np_smart_load(str(dataLoc + dataList['unitName'][i] + '_sfm.npy'));

    # first, the data
    _, _, sfmixExpResp, allSfMixExp = hf.organize_modResp(S['sfm']['exp']['trial']['spikeCount'], S['sfm']['exp']['trial'])    
    exp_responses = [sfmixExpResp, np.nanvar(allSfMixExp, axis=3)];

    ## then, the model
    # flat normalization
    if i in flat:
      flat_params = flat[i]['params'];
      ignore, modResp = SFMGiveBof(flat_params, S, normType=1, lossType=lossType, expInd=expInd);
      _, _, sfmixModResp, allSfMixMod = hf.organize_modResp(modResp, S['sfm']['exp']['trial'])
      mod_responses = [sfmixModResp, np.nanvar(allSfMixMod)];

      chi_flat[i] = hf.chiSq(exp_responses, mod_responses);
      print('chi: %.1f' % chi_flat[i]);

    # weighted normalization
    if i in weight:
      wght_params = weight[i]['params'];
      ignore, modResp = SFMGiveBof(wght_params, S, normType=2, lossType=lossType, expInd=expInd);
      _, _, sfmixModResp, allSfMixMod = hf.organize_modResp(modResp, S['sfm']['exp']['trial'])
      mod_responses = [sfmixModResp, np.nanvar(allSfMixMod)];

      chi_wght[i] = hf.chiSq(exp_responses, mod_responses);
      print('\tchi: %.1f' % chi_wght[i]);

  n_flat_params = len(flat[0]['params']);
  n_wght_params = len(weight[0]['params']);    

  chiNorm_flat = np.divide(chi_flat, n_flat_params);
  chiNorm_wght = np.divide(chi_flat, n_wght_params);

  chiAnalysis = dict();
  chiAnalysis['flat_norm'] = chiNorm_flat;
  chiAnalysis['wght_norm'] = chiNorm_wght;
  chiAnalysis['flat'] = chi_flat;
  chiAnalysis['wght'] = chi_wght;

  np.save('chiAnalysis', chiAnalysis);

  return chiNorm_flat, chiNorm_wght, chi_flat, chi_wght;
Esempio n. 9
0
                                      rvcFits=rvcFits,
                                      expInd=expInd)
    spikes = np.array([np.sum(x) for x in spikes_byComp])
    rates = True if vecF1 == 0 else False
    # when we get the spikes from rvcFits, they've already been converted into rates (in hf.get_all_fft)
    baseline = None
    # f1 has no "DC", yadig?
else:  # otherwise, if it's complex, just get F0
    respMeasure = 0
    spikes = hf.get_spikes(expData, get_f0=1, rvcFits=None, expInd=expInd)
    rates = False
    # get_spikes without rvcFits is directly from spikeCount, which is counts, not rates!
    baseline = hf.blankResp(expData, expInd)[0]
    # we'll plot the spontaneous rate
    # why mult by stimDur? well, spikes are not rates but baseline is, so we convert baseline to count (i.e. not rate, too)
    spikes = spikes - baseline * hf.get_exp_params(expInd).stimDur

#print('###\nGetting spikes (data): rates? %d\n###' % rates);
_, _, _, respAll = hf.organize_resp(spikes, expData, expInd, respsAsRate=rates)
# only using respAll to get variance measures
resps_data, stimVals, val_con_by_disp, _, _ = hf.tabulate_responses(
    expData,
    expInd,
    overwriteSpikes=spikes,
    respsAsRates=rates,
    modsAsRate=rates)

if fitList is None:
    resps = resps_data
    # otherwise, we'll still keep resps_data for reference
elif fitList is not None:  # OVERWRITE the data with the model spikes!
def plot_save_superposition(which_cell, expDir, use_mod_resp=0, fitType=2, excType=1, useHPCfit=1, conType=None, lgnFrontEnd=None, force_full=1, f1_expCutoff=2, to_save=1):

  if use_mod_resp == 2:
    rvcAdj   = -1; # this means vec corrected F1, not phase adjustment F1...
    _applyLGNtoNorm = 0; # don't apply the LGN front-end to the gain control weights
    recenter_norm = 1;
    newMethod = 1; # yes, use the "new" method for mrpt (not that new anymore, as of 21.03)
    lossType = 1; # sqrt
    _sigmoidSigma = 5;

  basePath = os.getcwd() + '/'
  if 'pl1465' in basePath or useHPCfit:
    loc_str = 'HPC';
  else:
    loc_str = '';

  rvcName = 'rvcFits%s_220531' % loc_str if expDir=='LGN/' else 'rvcFits%s_220609' % loc_str
  rvcFits = None; # pre-define this as None; will be overwritten if available/needed
  if expDir == 'altExp/': # we don't adjust responses there...
    rvcName = None;
  dFits_base = 'descrFits%s_220609' % loc_str if expDir=='LGN/' else 'descrFits%s_220631' % loc_str
  if use_mod_resp == 1:
    rvcName = None; # Use NONE if getting model responses, only
    if excType == 1:
      fitBase = 'fitList_200417';
    elif excType == 2:
      fitBase = 'fitList_200507';
    lossType = 1; # sqrt
    fitList_nm = hf.fitList_name(fitBase, fitType, lossType=lossType);
  elif use_mod_resp == 2:
    rvcName = None; # Use NONE if getting model responses, only
    if excType == 1:
      fitBase = 'fitList%s_210308_dG' % loc_str
      if recenter_norm:
        #fitBase = 'fitList%s_pyt_210312_dG' % loc_str
        fitBase = 'fitList%s_pyt_210331_dG' % loc_str
    elif excType == 2:
      fitBase = 'fitList%s_pyt_210310' % loc_str
      if recenter_norm:
        #fitBase = 'fitList%s_pyt_210312' % loc_str
        fitBase = 'fitList%s_pyt_210331' % loc_str
    fitList_nm = hf.fitList_name(fitBase, fitType, lossType=lossType, lgnType=lgnFrontEnd, lgnConType=conType, vecCorrected=-rvcAdj);

  # ^^^ EDIT rvc/descrFits/fitList names here; 

  ############
  # Before any plotting, fix plotting paramaters
  ############
  plt.style.use('https://raw.githubusercontent.com/paul-levy/SF_diversity/master/paul_plt_style.mplstyle');
  from matplotlib import rcParams
  rcParams['font.size'] = 20;
  rcParams['pdf.fonttype'] = 42 # should be 42, but there are kerning issues
  rcParams['ps.fonttype'] = 42 # should be 42, but there are kerning issues
  rcParams['lines.linewidth'] = 2.5;
  rcParams['axes.linewidth'] = 1.5;
  rcParams['lines.markersize'] = 8; # this is in style sheet, just being explicit
  rcParams['lines.markeredgewidth'] = 0; # no edge, since weird tings happen then

  rcParams['xtick.major.size'] = 15
  rcParams['xtick.minor.size'] = 5; # no minor ticks
  rcParams['ytick.major.size'] = 15
  rcParams['ytick.minor.size'] = 0; # no minor ticks

  rcParams['xtick.major.width'] = 2
  rcParams['xtick.minor.width'] = 2;
  rcParams['ytick.major.width'] = 2
  rcParams['ytick.minor.width'] = 0

  rcParams['font.style'] = 'oblique';
  rcParams['font.size'] = 20;

  ############
  # load everything
  ############
  dataListNm = hf.get_datalist(expDir, force_full=force_full);
  descrFits_f0 = None;
  dLoss_num = 2; # see hf.descrFit_name/descrMod_name/etc for details
  if expDir == 'LGN/':
    rvcMod = 0; 
    dMod_num = 1;
    rvcDir = 1;
    vecF1 = -1;
  else:
    rvcMod = 1; # i.e. Naka-rushton (1)
    dMod_num = 3; # d-dog-s
    rvcDir = None; # None if we're doing vec-corrected
    if expDir == 'altExp/':
      vecF1 = 0;
    else:
      vecF1 = 1;

  dFits_mod = hf.descrMod_name(dMod_num)
  descrFits_name = hf.descrFit_name(lossType=dLoss_num, descrBase=dFits_base, modelName=dFits_mod, phAdj=1 if vecF1==-1 else None);

  ## now, let it run
  dataPath = basePath + expDir + 'structures/'
  save_loc = basePath + expDir + 'figures/'
  save_locSuper = save_loc + 'superposition_220713/'
  if use_mod_resp == 1:
    save_locSuper = save_locSuper + '%s/' % fitBase

  dataList = hf.np_smart_load(dataPath + dataListNm);
  print('Trying to load descrFits at: %s' % (dataPath + descrFits_name));
  descrFits = hf.np_smart_load(dataPath + descrFits_name);
  if use_mod_resp == 1 or use_mod_resp == 2:
    fitList = hf.np_smart_load(dataPath + fitList_nm);
  else:
    fitList = None;

  if not os.path.exists(save_locSuper):
    os.makedirs(save_locSuper)

  cells = np.arange(1, 1+len(dataList['unitName']))

  zr_rm = lambda x: x[x>0];
  # more flexible - only get values where x AND z are greater than some value "gt" (e.g. 0, 1, 0.4, ...)
  zr_rm_pair = lambda x, z, gt: [x[np.logical_and(x>gt, z>gt)], z[np.logical_and(x>gt, z>gt)]];
  # zr_rm_pair = lambda x, z: [x[np.logical_and(x>0, z>0)], z[np.logical_and(x>0, z>0)]] if np.logical_and(x!=[], z!=[])==True else [], [];

  # here, we'll save measures we are going use for analysis purpose - e.g. supperssion index, c50
  curr_suppr = dict();

  ############
  ### Establish the plot, load cell-specific measures
  ############
  nRows, nCols = 6, 2;
  cellName = dataList['unitName'][which_cell-1];
  expInd = hf.get_exp_ind(dataPath, cellName)[0]
  S = hf.np_smart_load(dataPath + cellName + '_sfm.npy')
  expData = S['sfm']['exp']['trial'];

  # 0th, let's load the basic tuning characterizations AND the descriptive fit
  try:
    dfit_curr = descrFits[which_cell-1]['params'][0,-1,:]; # single grating, highest contrast
  except:
    dfit_curr = None;
  # - then the basics
  try:
    basic_names, basic_order = dataList['basicProgName'][which_cell-1], dataList['basicProgOrder']
    basics = hf.get_basic_tunings(basic_names, basic_order);
  except:
    try:
      # we've already put the basics in the data structure... (i.e. post-sorting 2021 data)
      basic_names = ['','','','',''];
      basic_order = ['rf', 'sf', 'tf', 'rvc', 'ori']; # order doesn't matter if they are already loaded
      basics = hf.get_basic_tunings(basic_names, basic_order, preProc=S, reducedSave=True)
    except:
      basics = None;

  ### TEMPORARY: save the "basics" in curr_suppr; should live on its own, though; TODO
  curr_suppr['basics'] = basics;

  try:
    oriBW, oriCV = basics['ori']['bw'], basics['ori']['cv'];
  except:
    oriBW, oriCV = np.nan, np.nan;
  try:
    tfBW = basics['tf']['tfBW_oct'];
  except:
    tfBW = np.nan;
  try:
    suprMod = basics['rfsize']['suprInd_model'];
  except:
    suprMod = np.nan;
  try:
    suprDat = basics['rfsize']['suprInd_data'];
  except:
    suprDat = np.nan;

  try:
    cellType = dataList['unitType'][which_cell-1];
  except:
    # TODO: note, this is dangerous; thus far, only V1 cells don't have 'unitType' field in dataList, so we can safely do this
    cellType = 'V1';


  ############
  ### compute f1f0 ratio, and load the corresponding F0 or F1 responses
  ############
  f1f0_rat = hf.compute_f1f0(expData, which_cell, expInd, dataPath, descrFitName_f0=descrFits_f0)[0];
  curr_suppr['f1f0'] = f1f0_rat;
  respMeasure = 1 if f1f0_rat > 1 else 0;

  if vecF1 == 1:
    # get the correct, adjusted F1 response
    if expInd > f1_expCutoff and respMeasure == 1:
      respOverwrite = hf.adjust_f1_byTrial(expData, expInd);
    else:
      respOverwrite = None;

  if (respMeasure == 1 or expDir == 'LGN/') and expDir != 'altExp/' : # i.e. if we're looking at a simple cell, then let's get F1
    if vecF1 == 1:
      spikes_byComp = respOverwrite
      # then, sum up the valid components per stimulus component
      allCons = np.vstack(expData['con']).transpose();
      blanks = np.where(allCons==0);
      spikes_byComp[blanks] = 0; # just set it to 0 if that component was blank during the trial
    else:
      if rvcName is not None:
        try:
          rvcFits = hf.get_rvc_fits(dataPath, expInd, which_cell, rvcName=rvcName, rvcMod=rvcMod, direc=rvcDir, vecF1=vecF1);
        except:
          rvcFits = None;
      else:
        rvcFits = None
      spikes_byComp = hf.get_spikes(expData, get_f0=0, rvcFits=rvcFits, expInd=expInd);
    spikes = np.array([np.sum(x) for x in spikes_byComp]);
    rates = True if vecF1 == 0 else False; # when we get the spikes from rvcFits, they've already been converted into rates (in hf.get_all_fft)
    baseline = None; # f1 has no "DC", yadig?
  else: # otherwise, if it's complex, just get F0
    respMeasure = 0;
    spikes = hf.get_spikes(expData, get_f0=1, rvcFits=None, expInd=expInd);
    rates = False; # get_spikes without rvcFits is directly from spikeCount, which is counts, not rates!
    baseline = hf.blankResp(expData, expInd)[0]; # we'll plot the spontaneous rate
    # why mult by stimDur? well, spikes are not rates but baseline is, so we convert baseline to count (i.e. not rate, too)
    spikes = spikes - baseline*hf.get_exp_params(expInd).stimDur; 

  #print('###\nGetting spikes (data): rates? %d\n###' % rates);
  _, _, _, respAll = hf.organize_resp(spikes, expData, expInd, respsAsRate=rates); # only using respAll to get variance measures
  resps_data, stimVals, val_con_by_disp, _, _ = hf.tabulate_responses(expData, expInd, overwriteSpikes=spikes, respsAsRates=rates, modsAsRate=rates);

  if fitList is None:
    resps = resps_data; # otherwise, we'll still keep resps_data for reference
  elif fitList is not None: # OVERWRITE the data with the model spikes!
    if use_mod_resp == 1:
      curr_fit = fitList[which_cell-1]['params'];
      modResp = mod_resp.SFMGiveBof(curr_fit, S, normType=fitType, lossType=lossType, expInd=expInd, cellNum=which_cell, excType=excType)[1];
      if f1f0_rat < 1: # then subtract baseline..
        modResp = modResp - baseline*hf.get_exp_params(expInd).stimDur; 
      # now organize the responses
      resps, stimVals, val_con_by_disp, _, _ = hf.tabulate_responses(expData, expInd, overwriteSpikes=modResp, respsAsRates=False, modsAsRate=False);
    elif use_mod_resp == 2: # then pytorch model!
      resp_str = hf_sf.get_resp_str(respMeasure)
      curr_fit = fitList[which_cell-1][resp_str]['params'];
      model = mrpt.sfNormMod(curr_fit, expInd=expInd, excType=excType, normType=fitType, lossType=lossType, lgnFrontEnd=lgnFrontEnd, newMethod=newMethod, lgnConType=conType, applyLGNtoNorm=_applyLGNtoNorm)
      ### get the vec-corrected responses, if applicable
      if expInd > f1_expCutoff and respMeasure == 1:
        respOverwrite = hf.adjust_f1_byTrial(expData, expInd);
      else:
        respOverwrite = None;

      dw = mrpt.dataWrapper(expData, respMeasure=respMeasure, expInd=expInd, respOverwrite=respOverwrite); # respOverwrite defined above (None if DC or if expInd=-1)
      modResp = model.forward(dw.trInf, respMeasure=respMeasure, sigmoidSigma=_sigmoidSigma, recenter_norm=recenter_norm).detach().numpy();

      if respMeasure == 1: # make sure the blank components have a zero response (we'll do the same with the measured responses)
        blanks = np.where(dw.trInf['con']==0);
        modResp[blanks] = 0;
        # next, sum up across components
        modResp = np.sum(modResp, axis=1);
      # finally, make sure this fills out a vector of all responses (just have nan for non-modelled trials)
      nTrialsFull = len(expData['num']);
      modResp_full = np.nan * np.zeros((nTrialsFull, ));
      modResp_full[dw.trInf['num']] = modResp;

      if respMeasure == 0: # if DC, then subtract baseline..., as determined from data (why not model? we aren't yet calc. response to no stim, though it can be done)
        modResp_full = modResp_full - baseline*hf.get_exp_params(expInd).stimDur;

      # TODO: This is a work around for which measures are in rates vs. counts (DC vs F1, model vs data...)
      stimDur = hf.get_exp_params(expInd).stimDur;
      asRates = False;
      #divFactor = stimDur if asRates == 0 else 1;
      #modResp_full = np.divide(modResp_full, divFactor);
      # now organize the responses
      resps, stimVals, val_con_by_disp, _, _ = hf.tabulate_responses(expData, expInd, overwriteSpikes=modResp_full, respsAsRates=asRates, modsAsRate=asRates);

  predResps = resps[2];

  respMean = resps[0]; # equivalent to resps[0];
  respStd = np.nanstd(respAll, -1); # take std of all responses for a given condition
  # compute SEM, too
  findNaN = np.isnan(respAll);
  nonNaN  = np.sum(findNaN == False, axis=-1);
  respSem = np.nanstd(respAll, -1) / np.sqrt(nonNaN);

  ############
  ### first, fit a smooth function to the overall pred V measured responses
  ### --- from this, we can measure how each example superposition deviates from a central tendency
  ### --- i.e. the residual relative to the "standard" input:output relationship
  ############
  all_resps = respMean[1:, :, :].flatten() # all disp>0
  all_preds = predResps[1:, :, :].flatten() # all disp>0
  # a model which allows negative fits
  #         myFit = lambda x, t0, t1, t2: t0 + t1*x + t2*x*x;
  #         non_nan = np.where(~np.isnan(all_preds)); # cannot fit negative values with naka-rushton...
  #         fitz, _ = opt.curve_fit(myFit, all_preds[non_nan], all_resps[non_nan], p0=[-5, 10, 5], maxfev=5000)
  # naka rushton
  myFit = lambda x, g, expon, c50: hf.naka_rushton(x, [0, g, expon, c50]) 
  non_neg = np.where(all_preds>0) # cannot fit negative values with naka-rushton...
  try:
    if use_mod_resp == 1: # the reference will ALWAYS be the data -- redo the above analysis for data
      predResps_data = resps_data[2];
      respMean_data = resps_data[0];
      all_resps_data = respMean_data[1:, :, :].flatten() # all disp>0
      all_preds_data = predResps_data[1:, :, :].flatten() # all disp>0
      non_neg_data = np.where(all_preds_data>0) # cannot fit negative values with naka-rushton...
      fitz, _ = opt.curve_fit(myFit, all_preds_data[non_neg_data], all_resps_data[non_neg_data], p0=[100, 2, 25], maxfev=5000)
    else:
      fitz, _ = opt.curve_fit(myFit, all_preds[non_neg], all_resps[non_neg], p0=[100, 2, 25], maxfev=5000)
    rel_c50 = np.divide(fitz[-1], np.max(all_preds[non_neg]));
  except:
    fitz = None;
    rel_c50 = -99;

  ############
  ### organize stimulus information
  ############
  all_disps = stimVals[0];
  all_cons = stimVals[1];
  all_sfs = stimVals[2];

  nCons = len(all_cons);
  nSfs = len(all_sfs);
  nDisps = len(all_disps);

  maxResp = np.maximum(np.nanmax(respMean), np.nanmax(predResps));
  # by disp
  clrs_d = cm.viridis(np.linspace(0,0.75,nDisps-1));
  lbls_d = ['disp: %s' % str(x) for x in range(nDisps)];
  # by sf
  val_sfs = hf.get_valid_sfs(S, disp=1, con=val_con_by_disp[1][0], expInd=expInd) # pick 
  clrs_sf = cm.viridis(np.linspace(0,.75,len(val_sfs)));
  lbls_sf = ['sf: %.2f' % all_sfs[x] for x in val_sfs];
  # by con
  val_con = all_cons;
  clrs_con = cm.viridis(np.linspace(0,.75,len(val_con)));
  lbls_con = ['con: %.2f' % x for x in val_con];

  ############
  ### create the figure
  ############
  fSuper, ax = plt.subplots(nRows, nCols, figsize=(10*nCols, 8*nRows))
  sns.despine(fig=fSuper, offset=10)

  allMix = [];
  allSum = [];

  ### plot reference tuning [row 1 (i.e. 2nd row)]
  ## on the right, SF tuning (high contrast)
  sfRef = hf.nan_rm(respMean[0, :, -1]); # high contrast tuning
  ax[1, 1].plot(all_sfs, sfRef, 'k-', marker='o', label='ref. tuning (d0, high con)', clip_on=False)
  ax[1, 1].set_xscale('log')
  ax[1, 1].set_xlim((0.1, 10));
  ax[1, 1].set_xlabel('sf (c/deg)')
  ax[1, 1].set_ylabel('response (spikes/s)')
  ax[1, 1].set_ylim((-5, 1.1*np.nanmax(sfRef)));
  ax[1, 1].legend(fontsize='x-small');

  #####
  ## then on the left, RVC (peak SF)
  #####
  sfPeak = np.argmax(sfRef); # stupid/simple, but just get the rvc for the max response
  v_cons_single = val_con_by_disp[0]
  rvcRef = hf.nan_rm(respMean[0, sfPeak, v_cons_single]);
  # now, if possible, let's also plot the RVC fit
  if rvcFits is not None:
    rvcFits = hf.get_rvc_fits(dataPath, expInd, which_cell, rvcName=rvcName, rvcMod=rvcMod);
    rel_rvc = rvcFits[0]['params'][sfPeak]; # we get 0 dispersion, peak SF
    plt_cons = np.geomspace(all_cons[0], all_cons[-1], 50);
    c50, pk = hf.get_c50(rvcMod, rel_rvc), rvcFits[0]['conGain'][sfPeak];
    c50_emp, c50_eval = hf.c50_empirical(rvcMod, rel_rvc); # determine c50 by optimization, numerical approx.
    if rvcMod == 0:
      rvc_mod = hf.get_rvc_model();
      rvcmodResp = rvc_mod(*rel_rvc, plt_cons);
    else: # i.e. mod=1 or mod=2
      rvcmodResp = hf.naka_rushton(plt_cons, rel_rvc);
    if baseline is not None:
      rvcmodResp = rvcmodResp - baseline; 
    ax[1, 0].plot(plt_cons, rvcmodResp, 'k--', label='rvc fit (c50=%.2f, gain=%0f)' %(c50, pk))
    # and save it
    curr_suppr['c50'] = c50; curr_suppr['conGain'] = pk;
    curr_suppr['c50_emp'] = c50_emp; curr_suppr['c50_emp_eval'] = c50_eval
  else:
    curr_suppr['c50'] = np.nan; curr_suppr['conGain'] = np.nan;
    curr_suppr['c50_emp'] = np.nan; curr_suppr['c50_emp_eval'] = np.nan;

  ax[1, 0].plot(all_cons[v_cons_single], rvcRef, 'k-', marker='o', label='ref. tuning (d0, peak SF)', clip_on=False)
  #         ax[1, 0].set_xscale('log')
  ax[1, 0].set_xlabel('contrast (%)');
  ax[1, 0].set_ylabel('response (spikes/s)')
  ax[1, 0].set_ylim((-5, 1.1*np.nanmax(rvcRef)));
  ax[1, 0].legend(fontsize='x-small');

  # plot the fitted model on each axis
  pred_plt = np.linspace(0, np.nanmax(all_preds), 100);
  if fitz is not None:
    ax[0, 0].plot(pred_plt, myFit(pred_plt, *fitz), 'r--', label='fit')
    ax[0, 1].plot(pred_plt, myFit(pred_plt, *fitz), 'r--', label='fit')

  for d in range(nDisps):
    if d == 0: # we don't care about single gratings!
      dispRats = [];
      continue; 
    v_cons = np.array(val_con_by_disp[d]);
    n_v_cons = len(v_cons);

    # plot split out by each contrast [0,1]
    for c in reversed(range(n_v_cons)):
      v_sfs = hf.get_valid_sfs(S, d, v_cons[c], expInd)
      for s in v_sfs:
        mixResp = respMean[d, s, v_cons[c]];
        allMix.append(mixResp);
        sumResp = predResps[d, s, v_cons[c]];
        allSum.append(sumResp);
  #      print('condition: d(%d), c(%d), sf(%d):: pred(%.2f)|real(%.2f)' % (d, v_cons[c], s, sumResp, mixResp))
        # PLOT in by-disp panel
        if c == 0 and s == v_sfs[0]:
          ax[0, 0].plot(sumResp, mixResp, 'o', color=clrs_d[d-1], label=lbls_d[d], clip_on=False)
        else:
          ax[0, 0].plot(sumResp, mixResp, 'o', color=clrs_d[d-1], clip_on=False)
        # PLOT in by-sf panel
        sfInd = np.where(np.array(v_sfs) == s)[0][0]; # will only be one entry, so just "unpack"
        try:
          if d == 1 and c == 0:
            ax[0, 1].plot(sumResp, mixResp, 'o', color=clrs_sf[sfInd], label=lbls_sf[sfInd], clip_on=False);
          else:
            ax[0, 1].plot(sumResp, mixResp, 'o', color=clrs_sf[sfInd], clip_on=False);
        except:
          pass;
          #pdb.set_trace();
        # plot baseline, if f0...
  #       if baseline is not None:
  #         [ax[0, i].axhline(baseline, linestyle='--', color='k', label='spon. rate') for i in range(2)];


    # plot averaged across all cons/sfs (i.e. average for the whole dispersion) [1,0]
    mixDisp = respMean[d, :, :].flatten();
    sumDisp = predResps[d, :, :].flatten();
    mixDisp, sumDisp = zr_rm_pair(mixDisp, sumDisp, 0.5);
    curr_rats = np.divide(mixDisp, sumDisp)
    curr_mn = geomean(curr_rats); curr_std = np.std(np.log10(curr_rats));
  #  curr_rat = geomean(np.divide(mixDisp, sumDisp));
    ax[2, 0].bar(d, curr_mn, yerr=curr_std, color=clrs_d[d-1]);
    ax[2, 0].set_yscale('log')
    ax[2, 0].set_ylim(0.1, 10);
  #  ax[2, 0].yaxis.set_ticks(minorticks)
    dispRats.append(curr_mn);
  #  ax[2, 0].bar(d, np.mean(np.divide(mixDisp, sumDisp)), color=clrs_d[d-1]);

    # also, let's plot the (signed) error relative to the fit
    if fitz is not None:
      errs = mixDisp - myFit(sumDisp, *fitz);
      ax[3, 0].bar(d, np.mean(errs), yerr=np.std(errs), color=clrs_d[d-1])
      # -- and normalized by the prediction output response
      errs_norm = np.divide(mixDisp - myFit(sumDisp, *fitz), myFit(sumDisp, *fitz));
      ax[4, 0].bar(d, np.mean(errs_norm), yerr=np.std(errs_norm), color=clrs_d[d-1])

    # and set some labels/lines, as needed
    if d == 1:
        ax[2, 0].set_xlabel('dispersion');
        ax[2, 0].set_ylabel('suppression ratio (linear)')
        ax[2, 0].axhline(1, ls='--', color='k')
        ax[3, 0].set_xlabel('dispersion');
        ax[3, 0].set_ylabel('mean (signed) error')
        ax[3, 0].axhline(0, ls='--', color='k')
        ax[4, 0].set_xlabel('dispersion');
        ax[4, 0].set_ylabel('mean (signed) error -- as frac. of fit prediction')
        ax[4, 0].axhline(0, ls='--', color='k')

    curr_suppr['supr_disp'] = dispRats;

  ### plot averaged across all cons/disps
  sfInds = []; sfRats = []; sfRatStd = []; 
  sfErrs = []; sfErrsStd = []; sfErrsInd = []; sfErrsIndStd = []; sfErrsRat = []; sfErrsRatStd = [];
  curr_errNormFactor = [];
  for s in range(len(val_sfs)):
    try: # not all sfs will have legitmate values;
      # only get mixtures (i.e. ignore single gratings)
      mixSf = respMean[1:, val_sfs[s], :].flatten();
      sumSf = predResps[1:, val_sfs[s], :].flatten();
      mixSf, sumSf = zr_rm_pair(mixSf, sumSf, 0.5);
      rats_curr = np.divide(mixSf, sumSf); 
      sfInds.append(s); sfRats.append(geomean(rats_curr)); sfRatStd.append(np.std(np.log10(rats_curr)));

      if fitz is not None:
        #curr_NR = myFit(sumSf, *fitz); # unvarnished
        curr_NR = np.maximum(myFit(sumSf, *fitz), 0.5); # thresholded at 0.5...

        curr_err = mixSf - curr_NR;
        sfErrs.append(np.mean(curr_err));
        sfErrsStd.append(np.std(curr_err))

        curr_errNorm = np.divide(mixSf - curr_NR, mixSf + curr_NR);
        sfErrsInd.append(np.mean(curr_errNorm));
        sfErrsIndStd.append(np.std(curr_errNorm))

        curr_errRat = np.divide(mixSf, curr_NR);
        sfErrsRat.append(np.mean(curr_errRat));
        sfErrsRatStd.append(np.std(curr_errRat));

        curr_normFactors = np.array(curr_NR)
        curr_errNormFactor.append(geomean(curr_normFactors[curr_normFactors>0]));
      else:
        sfErrs.append([]);
        sfErrsStd.append([]);
        sfErrsInd.append([]);
        sfErrsIndStd.append([]);
        sfErrsRat.append([]);
        sfErrsRatStd.append([]);
        curr_errNormFactor.append([]);
    except:
      pass

  # get the offset/scale of the ratio so that we can plot a rescaled/flipped version of
  # the high con/single grat tuning for reference...does the suppression match the response?
  offset, scale = np.nanmax(sfRats), np.nanmax(sfRats) - np.nanmin(sfRats);
  sfRef = hf.nan_rm(respMean[0, val_sfs, -1]); # high contrast tuning
  sfRefShift = offset - scale * (sfRef/np.nanmax(sfRef))
  ax[2,1].scatter(all_sfs[val_sfs][sfInds], sfRats, color=clrs_sf[sfInds], clip_on=False)
  ax[2,1].errorbar(all_sfs[val_sfs][sfInds], sfRats, sfRatStd, color='k', linestyle='-', clip_on=False, label='suppression tuning')
  #         ax[2,1].plot(all_sfs[val_sfs][sfInds], sfRats, 'k-', clip_on=False, label='suppression tuning')
  ax[2,1].plot(all_sfs[val_sfs], sfRefShift, 'k--', label='ref. tuning', clip_on=False)
  ax[2,1].axhline(1, ls='--', color='k')
  ax[2,1].set_xlabel('sf (cpd)')
  ax[2,1].set_xscale('log')
  ax[2,1].set_xlim((0.1, 10));
  #ax[2,1].set_xlim((np.min(all_sfs), np.max(all_sfs)));
  ax[2,1].set_ylabel('suppression ratio');
  ax[2,1].set_yscale('log')
  #ax[2,1].yaxis.set_ticks(minorticks)
  ax[2,1].set_ylim(0.1, 10);        
  ax[2,1].legend(fontsize='x-small');
  curr_suppr['supr_sf'] = sfRats;

  ### residuals from fit of suppression
  if fitz is not None:
    # mean signed error: and labels/plots for the error as f'n of SF
    ax[3,1].axhline(0, ls='--', color='k')
    ax[3,1].set_xlabel('sf (cpd)')
    ax[3,1].set_xscale('log')
    ax[3,1].set_xlim((0.1, 10));
    #ax[3,1].set_xlim((np.min(all_sfs), np.max(all_sfs)));
    ax[3,1].set_ylabel('mean (signed) error');
    ax[3,1].errorbar(all_sfs[val_sfs][sfInds], sfErrs, sfErrsStd, color='k', marker='o', linestyle='-', clip_on=False)
    # -- and normalized by the prediction output response + output respeonse
    val_errs = np.logical_and(~np.isnan(sfErrsRat), np.logical_and(np.array(sfErrsIndStd)>0, np.array(sfErrsIndStd) < 2));
    norm_subset = np.array(sfErrsInd)[val_errs];
    normStd_subset = np.array(sfErrsIndStd)[val_errs];
    ax[4,1].axhline(0, ls='--', color='k')
    ax[4,1].set_xlabel('sf (cpd)')
    ax[4,1].set_xscale('log')
    ax[4,1].set_xlim((0.1, 10));
    #ax[4,1].set_xlim((np.min(all_sfs), np.max(all_sfs)));
    ax[4,1].set_ylim((-1, 1));
    ax[4,1].set_ylabel('error index');
    ax[4,1].errorbar(all_sfs[val_sfs][sfInds][val_errs], norm_subset, normStd_subset, color='k', marker='o', linestyle='-', clip_on=False)
    # -- AND simply the ratio between the mixture response and the mean expected mix response (i.e. Naka-Rushton)
    # --- equivalent to the suppression ratio, but relative to the NR fit rather than perfect linear summation
    val_errs = np.logical_and(~np.isnan(sfErrsRat), np.logical_and(np.array(sfErrsRatStd)>0, np.array(sfErrsRatStd) < 2));
    rat_subset = np.array(sfErrsRat)[val_errs];
    ratStd_subset = np.array(sfErrsRatStd)[val_errs];
    #ratStd_subset = (1/np.log(2))*np.divide(np.array(sfErrsRatStd)[val_errs], rat_subset);
    ax[5,1].scatter(all_sfs[val_sfs][sfInds][val_errs], rat_subset, color=clrs_sf[sfInds][val_errs], clip_on=False)
    ax[5,1].errorbar(all_sfs[val_sfs][sfInds][val_errs], rat_subset, ratStd_subset, color='k', linestyle='-', clip_on=False, label='suppression tuning')
    ax[5,1].axhline(1, ls='--', color='k')
    ax[5,1].set_xlabel('sf (cpd)')
    ax[5,1].set_xscale('log')
    ax[5,1].set_xlim((0.1, 10));
    ax[5,1].set_ylabel('suppression ratio (wrt NR)');
    ax[5,1].set_yscale('log', basey=2)
  #         ax[2,1].yaxis.set_ticks(minorticks)
    ax[5,1].set_ylim(np.power(2.0, -2), np.power(2.0, 2));
    ax[5,1].legend(fontsize='x-small');
    # - compute the variance - and put that value on the plot
    errsRatVar = np.var(np.log2(sfErrsRat)[val_errs]);
    curr_suppr['sfRat_VAR'] = errsRatVar;
    ax[5,1].text(0.1, 2, 'var=%.2f' % errsRatVar);

    # compute the unsigned "area under curve" for the sfErrsInd, and normalize by the octave span of SF values considered
    val_errs = np.logical_and(~np.isnan(sfErrsRat), np.logical_and(np.array(sfErrsIndStd)>0, np.array(sfErrsIndStd) < 2));
    val_x = all_sfs[val_sfs][sfInds][val_errs];
    ind_var = np.var(np.array(sfErrsInd)[val_errs]);
    curr_suppr['sfErrsInd_VAR'] = ind_var;
    # - and put that value on the plot
    ax[4,1].text(0.1, -0.25, 'var=%.3f' % ind_var);
  else:
    curr_suppr['sfErrsInd_VAR'] = np.nan
    curr_suppr['sfRat_VAR'] = np.nan

  #########
  ### NOW, let's evaluate the derivative of the SF tuning curve and get the correlation with the errors
  #########
  mod_sfs = np.geomspace(all_sfs[0], all_sfs[-1], 1000);
  mod_resp = hf.get_descrResp(dfit_curr, mod_sfs, DoGmodel=dMod_num);
  deriv = np.divide(np.diff(mod_resp), np.diff(np.log10(mod_sfs)))
  deriv_norm = np.divide(deriv, np.maximum(np.nanmax(deriv), np.abs(np.nanmin(deriv)))); # make the maximum response 1 (or -1)
  # - then, what indices to evaluate for comparing with sfErr?
  errSfs = all_sfs[val_sfs][sfInds];
  mod_inds = [np.argmin(np.square(mod_sfs-x)) for x in errSfs];
  deriv_norm_eval = deriv_norm[mod_inds];
  # -- plot on [1, 1] (i.e. where the data is)
  ax[1,1].plot(mod_sfs, mod_resp, 'k--', label='fit (g)')
  ax[1,1].legend();
  # Duplicate "twin" the axis to create a second y-axis
  ax2 = ax[1,1].twinx();
  ax2.set_xscale('log'); # have to re-inforce log-scale?
  ax2.set_ylim([-1, 1]); # since the g' is normalized
  # make a plot with different y-axis using second axis object
  ax2.plot(mod_sfs[1:], deriv_norm, '--', color="red", label='g\'');
  ax2.set_ylabel("deriv. (normalized)",color="red")
  ax2.legend();
  sns.despine(ax=ax2, offset=10, right=False);
  # -- and let's plot rescaled and shifted version in [2,1]
  offset, scale = np.nanmax(sfRats), np.nanmax(sfRats) - np.nanmin(sfRats);
  derivShift = offset - scale * (deriv_norm/np.nanmax(deriv_norm));
  ax[2,1].plot(mod_sfs[1:], derivShift, 'r--', label='deriv(ref. tuning)', clip_on=False)
  ax[2,1].legend(fontsize='x-small');
  # - then, normalize the sfErrs/sfErrsInd and compute the correlation coefficient
  if fitz is not None:
    norm_sfErr = np.divide(sfErrs, np.nanmax(np.abs(sfErrs)));
    norm_sfErrInd = np.divide(sfErrsInd, np.nanmax(np.abs(sfErrsInd))); # remember, sfErrsInd is normalized per condition; this is overall
    non_nan = np.logical_and(~np.isnan(norm_sfErr), ~np.isnan(deriv_norm_eval))
    corr_nsf, corr_nsfN = np.corrcoef(deriv_norm_eval[non_nan], norm_sfErr[non_nan])[0,1], np.corrcoef(deriv_norm_eval[non_nan], norm_sfErrInd[non_nan])[0,1]
    curr_suppr['corr_derivWithErr'] = corr_nsf;
    curr_suppr['corr_derivWithErrsInd'] = corr_nsfN;
    ax[3,1].text(0.1, 0.25*np.nanmax(sfErrs), 'corr w/g\' = %.2f' % corr_nsf)
    ax[4,1].text(0.1, 0.25, 'corr w/g\' = %.2f' % corr_nsfN)
  else:
    curr_suppr['corr_derivWithErr'] = np.nan;
    curr_suppr['corr_derivWithErrsInd'] = np.nan;

  # make a polynomial fit
  try:
    hmm = np.polyfit(allSum, allMix, deg=1) # returns [a, b] in ax + b 
  except:
    hmm = [np.nan];
  curr_suppr['supr_index'] = hmm[0];

  for j in range(1):
    for jj in range(nCols):
      ax[j, jj].axis('square')
      ax[j, jj].set_xlabel('prediction: sum(components) (imp/s)');
      ax[j, jj].set_ylabel('mixture response (imp/s)');
      ax[j, jj].plot([0, 1*maxResp], [0, 1*maxResp], 'k--')
      ax[j, jj].set_xlim((-5, maxResp));
      ax[j, jj].set_ylim((-5, 1.1*maxResp));
      ax[j, jj].set_title('Suppression index: %.2f|%.2f' % (hmm[0], rel_c50))
      ax[j, jj].legend(fontsize='x-small');

  fSuper.suptitle('Superposition: %s #%d [%s; f1f0 %.2f; szSupr[dt/md] %.2f/%.2f; oriBW|CV %.2f|%.2f; tfBW %.2f]' % (cellType, which_cell, cellName, f1f0_rat, suprDat, suprMod, oriBW, oriCV, tfBW))

  if fitList is None:
    save_name = 'cell_%03d.pdf' % which_cell
  else:
    save_name = 'cell_%03d_mod%s.pdf' % (which_cell, hf.fitType_suffix(fitType))
  pdfSv = pltSave.PdfPages(str(save_locSuper + save_name));
  pdfSv.savefig(fSuper)
  pdfSv.close();

  #########
  ### Finally, add this "superposition" to the newest 
  #########

  if to_save:

    if fitList is None:
      from datetime import datetime
      suffix = datetime.today().strftime('%y%m%d')
      super_name = 'superposition_analysis_%s.npy' % suffix;
    else:
      super_name = 'superposition_analysis_mod%s.npy' % hf.fitType_suffix(fitType);

    pause_tm = 5*np.random.rand();
    print('sleeping for %d secs (#%d)' % (pause_tm, which_cell));
    time.sleep(pause_tm);

    if os.path.exists(dataPath + super_name):
      suppr_all = hf.np_smart_load(dataPath + super_name);
    else:
      suppr_all = dict();
    suppr_all[which_cell-1] = curr_suppr;
    np.save(dataPath + super_name, suppr_all);
  
  return curr_suppr;
Esempio n. 11
0
                                      get_f0=0,
                                      rvcFits=rvcFits,
                                      expInd=expInd)
        spikes = np.array([np.sum(x) for x in spikes_byComp])
        rates = True
        # when we get the spikes from rvcFits, they've already been converted into rates (in hf.get_all_fft)
        baseline_sfMix = None
        # f1 has no "DC", yadig?
    else:  # otherwise, if it's complex, just get F0
        spikes = hf.get_spikes(expData, get_f0=1, rvcFits=None, expInd=expInd)
        rates = False
        # get_spikes without rvcFits is directly from spikeCount, which is counts, not rates!
        baseline_sfMix = hf.blankResp(expData, expInd)[0]
        # we'll plot the spontaneous rate
        # why mult by stimDur? well, spikes are not rates but baseline is, so we convert baseline to count (i.e. not rate, too)
        spikes = spikes - baseline_sfMix * hf.get_exp_params(expInd).stimDur

    _, _, respOrg, respAll = hf.organize_resp(spikes, expData, expInd)
    resps, stimVals, val_con_by_disp, _, _ = hf.tabulate_responses(
        expData, expInd, overwriteSpikes=spikes, respsAsRates=rates)
    predResps = resps[2]

    respMean = resps[0]
    # equivalent to resps[0];
    respStd = np.nanstd(respAll, -1)
    # take std of all responses for a given condition
    # compute SEM, too
    findNaN = np.isnan(respAll)
    nonNaN = np.sum(findNaN == False, axis=-1)
    respSem = np.nanstd(respAll, -1) / np.sqrt(nonNaN)