def plot_prd_spike_count(spk_cnt, prd, sps, fig): """Plot spike count results for period.""" # Init axes. ax_total, ax_per_tr, ax_mean = putil.sps_add_axes(fig, sps, 1, 3) # Plot total spike count histogram. plot_spike_count_hist(spk_cnt.unstack(), prd + ', all trials', ax_total) # Per trial. cols = sns.color_palette('Spectral', len(spk_cnt.index)) for (itr, nspk), col in zip(spk_cnt.iterrows(), cols): plot_spike_count_hist(nspk, prd + ', per trial', ax_per_tr, False, 1.0, col, 1) # Mean over time. x = spk_cnt.index y = spk_cnt.median(1) pplot.lines(x, y, xlim=[x.min(), x.max()], xlab='trial index', ylab='mean spike counts', title='median over time', ax=ax_mean) # Format figures. for ax in (ax_total, ax_per_tr): putil.set_limits(ax, xlim=[-0.5, None], ylim=[0, None]) return ax_total, ax_per_tr, ax_mean
def plot_tuning(xfit, yfit, vals=None, meanr=None, semr=None, color='b', baseline=None, xticks=None, xlim=None, ylim=None, xlab=None, ylab=None, title=None, ffig=None, ax=None, **kwargs): """Plot tuning curve, optionally with data samples.""" ax = putil.axes(ax) # Plot baseline. if baseline is not None: putil.add_baseline(baseline, ax=ax) # Plot fitted curve. pplot.lines(xfit, yfit, color=color, ax=ax) # Plot data samples. if meanr is not None and semr is not None: pplot.errorbar(vals, meanr, yerr=semr, fmt='o', color=color, ax=ax, **kwargs) # Set x axis ticks. if xticks is not None: putil.set_xtick_labels(ax, xticks) elif vals is not None: putil.set_xtick_labels(ax, vals) # Format plot. putil.format_plot(ax, xlim, ylim, xlab, ylab, title) # Save and return plot. putil.save_fig(ffig) return ax
def plot_auc_over_time(auc, tvec, prds=None, evts=None, xlim=None, ylim=None, xlab='time', ylab='AUC', title=None, ax=None): """Plot AROC values over time.""" # Init params. ax = putil.axes(ax) if xlim is None: xlim = [min(tvec), max(tvec)] # Plot periods first. putil.plot_periods(prds, ax=ax) # Plot AUC over time. pplot.lines(tvec, auc, ylim, xlim, xlab, ylab, title, color='green', ax=ax) # Add chance level line. putil.add_chance_level(ax=ax) # # Set minimum y axis scale. # ymin, ymax = ax.get_ylim() # ymin, ymax = min(ymin, 0.3), max(ymax, 0.7) # ax.set_ylim([ymin, ymax]) # Set y tick labels. if ylim is not None and ylim[0] == 0 and ylim[1] == 1: tck_marks = np.linspace(0, 1, 5) tck_lbls = np.array(tck_marks, dtype=str) tck_lbls[1::2] = '' putil.set_ytick_labels(ax, tck_marks, tck_lbls) putil.set_max_n_ticks(ax, 5, 'y') # Plot event markers. putil.plot_event_markers(evts, ax=ax) return ax
def plot_qm(u, bs_stats, stab_prd_res, prd_inc, tr_inc, spk_inc, add_lbls=False, ftempl=None, fig=None, sps=None): """Plot quality metrics related figures.""" # Init values. waveforms = np.array(u.Waveforms) wavetime = u.Waveforms.columns * us spk_times = np.array(u.SpikeParams['time'], dtype=float) base_rate = u.QualityMetrics['baseline'] # Minimum and maximum gain. gmin = u.SessParams['minV'] gmax = u.SessParams['maxV'] # %% Init plots. # Disable inline plotting to prevent memory leak. putil.inline_off() # Init figure and gridspec. fig = putil.figure(fig) if sps is None: sps = putil.gridspec(1, 1)[0] ogsp = putil.embed_gsp(sps, 2, 1, height_ratios=[0.02, 1]) info_sps, qm_sps = ogsp[0], ogsp[1] # Info header. info_ax = fig.add_subplot(info_sps) putil.hide_axes(info_ax) title = putil.get_unit_info_title(u) putil.set_labels(ax=info_ax, title=title, ytitle=0.80) # Create axes. gsp = putil.embed_gsp(qm_sps, 3, 2, wspace=0.3, hspace=0.4) ax_wf_inc, ax_wf_exc = [fig.add_subplot(gsp[0, i]) for i in (0, 1)] ax_wf_amp, ax_wf_dur = [fig.add_subplot(gsp[1, i]) for i in (0, 1)] ax_amp_dur, ax_rate = [fig.add_subplot(gsp[2, i]) for i in (0, 1)] # Trial markers. trial_starts, trial_stops = u.TrData.TrialStart, u.TrData.TrialStop tr_markers = pd.DataFrame({'time': trial_starts[9::10]}) tr_markers['label'] = [ str(itr + 1) if i % 2 else '' for i, itr in enumerate(tr_markers.index) ] # Common variables, limits and labels. WF_T_START = test_sorting.WF_T_START spk_t = u.SessParams.sampl_prd * (np.arange(waveforms.shape[1]) - WF_T_START) ses_t_lim = test_sorting.get_start_stop_times(spk_times, trial_starts, trial_stops) ss, sa = 1.0, 0.8 # marker size and alpha on scatter plot # Color spikes by their occurance over session time. my_cmap = putil.get_cmap('jet') spk_cols = np.tile(np.array([.25, .25, .25, .25]), (len(spk_times), 1)) if np.any(spk_inc): # check if there is any spike included spk_t_inc = np.array(spk_times[spk_inc]) tmin, tmax = float(spk_times.min()), float(spk_times.max()) spk_cols[spk_inc, :] = my_cmap((spk_t_inc - tmin) / (tmax - tmin)) # Put excluded trials to the front, and randomise order of included trials # so later spikes don't systematically cover earlier ones. spk_order = np.hstack((np.where(np.invert(spk_inc))[0], np.random.permutation(np.where(spk_inc)[0]))) # Common labels for plots ses_t_lab = 'Recording time (s)' # %% Waveform shape analysis. # Plot included and excluded waveforms on different axes. # Color included by occurance in session time to help detect drifts. s_waveforms, s_spk_cols = waveforms[spk_order, :], spk_cols[spk_order] wf_t_lim, glim = [min(spk_t), max(spk_t)], [gmin, gmax] wf_t_lab, volt_lab = 'WF time ($\mu$s)', 'Voltage' for st in ('Included', 'Excluded'): ax = ax_wf_inc if st == 'Included' else ax_wf_exc spk_idx = spk_inc if st == 'Included' else np.invert(spk_inc) tr_idx = tr_inc if st == 'Included' else np.invert(tr_inc) nspsk, ntrs = sum(spk_idx), sum(tr_idx) title = '{} WFs, {} spikes, {} trials'.format(st, nspsk, ntrs) # Select waveforms and colors. rand_spk_idx = spk_idx[spk_order] wfs = s_waveforms[rand_spk_idx, :] cols = s_spk_cols[rand_spk_idx] # Plot waveforms. xlab, ylab = (wf_t_lab, volt_lab) if add_lbls else (None, None) pwaveform.plot_wfs(wfs, spk_t, cols=cols, lw=0.1, alpha=0.05, xlim=wf_t_lim, ylim=glim, title=title, xlab=xlab, ylab=ylab, ax=ax) # %% Waveform summary metrics. # Init data. wf_amp_all = u.SpikeParams['amplitude'] wf_amp_inc = wf_amp_all[spk_inc] wf_dur_all = u.SpikeParams['duration'] wf_dur_inc = wf_dur_all[spk_inc] # Set common limits and labels. dur_lim = [0, wavetime[-2] - wavetime[WF_T_START]] # same across units glim = max(wf_amp_all.max(), gmax - gmin) amp_lim = [0, glim] amp_lab = 'Amplitude' dur_lab = 'Duration ($\mu$s)' # Waveform amplitude across session time. m_amp, sd_amp = wf_amp_inc.mean(), wf_amp_inc.std() title = 'WF amplitude: {:.1f} $\pm$ {:.1f}'.format(m_amp, sd_amp) xlab, ylab = (ses_t_lab, amp_lab) if add_lbls else (None, None) pplot.scatter(spk_times, wf_amp_all, spk_inc, c='m', bc='grey', s=ss, xlab=xlab, ylab=ylab, xlim=ses_t_lim, ylim=amp_lim, edgecolors='', alpha=sa, id_line=False, title=title, ax=ax_wf_amp) # Waveform duration across session time. mdur, sdur = wf_dur_inc.mean(), wf_dur_inc.std() title = 'WF duration: {:.1f} $\pm$ {:.1f} $\mu$s'.format(mdur, sdur) xlab, ylab = (ses_t_lab, dur_lab) if add_lbls else (None, None) pplot.scatter(spk_times, wf_dur_all, spk_inc, c='c', bc='grey', s=ss, xlab=xlab, ylab=ylab, xlim=ses_t_lim, ylim=dur_lim, edgecolors='', alpha=sa, id_line=False, title=title, ax=ax_wf_dur) # Waveform duration against amplitude. title = 'WF duration - amplitude' xlab, ylab = (dur_lab, amp_lab) if add_lbls else (None, None) pplot.scatter(wf_dur_all[spk_order], wf_amp_all[spk_order], c=spk_cols[spk_order], s=ss, xlab=xlab, ylab=ylab, xlim=dur_lim, ylim=amp_lim, edgecolors='', alpha=sa, id_line=False, title=title, ax=ax_amp_dur) # %% Firing rate. tmean = np.array(bs_stats['tmean']) rmean = util.remove_dim_from_series(bs_stats['rate']) prd_tstart, prd_tstop = stab_prd_res['tstart'], stab_prd_res['tstop'] # Color segments depending on whether they are included / excluded. def plot_periods(v, color, ax): # Plot line segments. for i in range(len(prd_inc[:-1])): col = color if prd_inc[i] and prd_inc[i + 1] else 'grey' x, y = [(tmean[i], tmean[i + 1]), (v[i], v[i + 1])] ax.plot(x, y, color=col) # Plot line points. for i in range(len(prd_inc)): col = color if prd_inc[i] else 'grey' x, y = [tmean[i], v[i]] ax.plot(x, y, color=col, marker='o', markersize=3, markeredgecolor=col) # Firing rate over session time. title = 'Baseline rate: {:.1f} spike/s'.format(float(base_rate)) xlab, ylab = (ses_t_lab, putil.FR_lbl) if add_lbls else (None, None) ylim = [0, 1.25 * np.max(rmean)] plot_periods(rmean, 'b', ax_rate) pplot.lines([], [], c='b', xlim=ses_t_lim, ylim=ylim, title=title, xlab=xlab, ylab=ylab, ax=ax_rate) # Trial markers. putil.plot_events(tr_markers, lw=0.5, ls='--', alpha=0.35, y_lbl=0.92, ax=ax_rate) # Excluded periods. excl_prds = [] tstart, tstop = ses_t_lim if tstart != prd_tstart: excl_prds.append(('beg', tstart, prd_tstart)) if tstop != prd_tstop: excl_prds.append(('end', prd_tstop, tstop)) putil.plot_periods(excl_prds, ymax=0.92, ax=ax_rate) # %% Post-formatting. # Maximize number of ticks on recording time axes to prevent covering. for ax in (ax_wf_amp, ax_wf_dur, ax_rate): putil.set_max_n_ticks(ax, 6, 'x') # %% Save figure. if ftempl is not None: fname = ftempl.format(u.name_to_fname()) putil.save_fig(fname, fig, title, rect_height=0.92) putil.inline_on() return [ax_wf_inc, ax_wf_exc], ax_wf_amp, ax_wf_dur, ax_amp_dur, ax_rate
def plot_DR(dirs, resp, DSI=None, PD=None, baseline=None, plot_type='line', complete_missing_dirs=False, color='b', title=None, ffig=None, ax=None): """ Plot response to each directions on polar plot, with a vector pointing to preferred direction (PD) with length DSI. Use plot_type to change between sector ('bar') and connected ('line') plot types. """ ax = putil.axes(ax, polar=True) # Plot baseline. if baseline is not None: putil.add_baseline(baseline, ax=ax) # Remove NaNs. all_dirs = dirs not_nan = np.array(~pd.isnull(dirs) & ~pd.isnull(resp)) dirs, resp = dirs[not_nan], np.array(resp[not_nan]) # Prepare data. # Complete missing directions with 0 response. if complete_missing_dirs: for i, d in enumerate(all_dirs): if d not in dirs: dirs = np.insert(dirs, i, d) * dirs.units resp = np.insert(resp, i, 0) * 1 / s rad_dirs = dirs.rescale(rad) # Plot response to each directions on polar plot. if plot_type == 'bar': # sector plot ndirs = all_dirs.size left_rad_dirs = rad_dirs - np.pi / ndirs # no need for this in MPL 2.0? w = 2 * np.pi / ndirs # same with edgecolor and else? pplot.bars(left_rad_dirs, resp, width=w, alpha=0.50, color=color, lw=1, edgecolor='w', title=title, ytitle=1.08, ax=ax) else: # line plot rad_dirs, resp = [np.append(v, [v[0]]) for v in (rad_dirs, resp)] pplot.lines(rad_dirs, resp, color=color, marker='o', lw=1, ms=4, mew=0, title=title, ytitle=1.08, ax=ax) ax.fill(rad_dirs, resp, color=color, alpha=0.15) # Add arrow representing PD and weighted DSI. if DSI is not None and PD is not None: rho = np.max(resp) * DSI xy = (float(PD.rescale(rad)), rho) arr_props = dict(facecolor=color, edgecolor='k', shrink=0.0, alpha=0.5) ax.annotate('', xy, xytext=(0, 0), arrowprops=arr_props) # Remove spines and tick marks, maximize tick labels. putil.set_spines(ax, False, False) putil.hide_tick_marks(ax) # Save and return plot. putil.save_fig(ffig) return ax
def rec_stability_test(UA, fname=None, periods=None): """Check stability of recording session across tasks.""" # Init. if periods is None: periods = ['whole trial', 'fixation'] # Init figure. fig, gsp, axs = putil.get_gs_subplots(nrow=len(periods), ncol=1, subw=10, subh=2.5, create_axes=True, as_array=False) for prd, ax in zip(periods, axs): # Calculate and plot firing rate during given period in each trial # across session for all units. colors = putil.get_colors() task_stats = pd.DataFrame(columns=['t_start', 't_stops', 'label']) for task, color in zip(UA.tasks(), colors): # Get activity of all units in task. tr_rates = [] for u in UA.iter_thru([task]): rates = u.get_prd_rates(prd, tr_time_idx=True) tr_rates.append(util.remove_dim_from_series(rates)) tr_rates = pd.DataFrame(tr_rates) # Not (non-empty and included) unit during task. if not len(tr_rates.index): continue # Plot each rate in task. tr_times = tr_rates.columns pplot.lines(tr_times, tr_rates.T, zorder=1, alpha=0.5, color=color, ax=ax) # Plot mean +- sem rate. tr_time = tr_rates.columns mean_rate, sem_rate = tr_rates.mean(), tr_rates.std() lower, upper = mean_rate-sem_rate, mean_rate+sem_rate lower[lower < 0] = 0 # remove negative values ax.fill_between(tr_time, lower, upper, zorder=2, alpha=.5, facecolor='grey', edgecolor='grey') pplot.lines(tr_time, mean_rate, lw=2, color='k', ax=ax) # Add task stats. task_lbl = '{}, {} units'.format(task, len(tr_rates.index)) # Add grand mean FR. task_lbl += '\nFR: {:.1f} sp/s'.format(tr_rates.mean().mean()) # Calculate linear trend to test gradual drift. slope, _, _, p_value, _ = sp.stats.linregress(tr_times, mean_rate) slope = 3600*slope # convert to change in spike per hour pval = util.format_pvalue(p_value, max_digit=3) task_lbl += '\n$\delta$FR: {:.1f} sp/s/h'.format(slope) task_lbl += '\n{}'.format(pval) task_stats.loc[task] = (tr_times.min(), tr_times.max(), task_lbl) # Set axes limits. tmin, tmax = task_stats.t_start.min(), task_stats.t_stops.max() putil.set_limits(ax, xlim=(tmin, tmax)) # Add task labels after all tasks have been plotted. putil.plot_events(task_stats[['t_start', 'label']], y_lbl=0.75, lbl_ha='left', lbl_rotation=0, ax=ax) # Format plot. xlab = 'Recording time (s)' if prd == periods[-1] else None putil.set_labels(ax, xlab=xlab, ylab=prd) putil.set_spines(ax, left=False) # Save figure. title = 'Recording stability of ' + UA.Name putil.save_fig(fname, fig, title)