def init_res_fig(n_subplots, max_sess=None, modif=False):
    """
    init_res_fig(n_subplots)

    Initializes a figure in which to plot summary results.

    Required args:
        - n_subplots (int): number of subplots
        
    Optional args:
        - max_sess (int): maximum number of sessions plotted
                          default: None
        - modif (bool)  : if True, plots are made in a modified (simplified 
                          way)
                          default: False

    Returns:
        - fig (plt Fig): figure
        - ax (plt Axis): axis
    """

    subplot_hei = 14
    subplot_wid = 7.5
    if max_sess is not None:
        subplot_wid *= max_sess/4.0

    if modif:
        sess_plot_util.update_plt_linpla()
        figpar_init = sess_plot_util.fig_init_linpla(sharey=True)["init"]
        fig, ax = plot_util.init_fig(n_subplots, **figpar_init)
    else:
        fig, ax = plot_util.init_fig(n_subplots, 2, sharey=True, 
            subplot_hei=subplot_hei, subplot_wid=subplot_wid)

    return fig, ax
Esempio n. 2
0
def plot_idx_correlations(idx_corr_df,
                          permpar,
                          figpar,
                          permute="sess",
                          corr_type="corr",
                          title=None,
                          small=True):
    """
    plot_idx_correlations(idx_corr_df, permpar, figpar)

    Plots ROI USI index correlations across sessions.

    Required args:
        - idx_corr_df (pd.DataFrame):
            dataframe with one row per line/plane, and the 
            following columns, in addition to the basic sess_df columns:

            for session comparisons, e.g. 1v2
            - {}v{}{norm_str}_corrs (float): intersession ROI index correlations
            - {}v{}{norm_str}_corr_stds (float): bootstrapped intersession ROI 
                index correlation standard deviation
            - {}v{}_null_CIs (list): adjusted null CI for intersession ROI 
                index correlations
            - {}v{}_raw_p_vals (float): p-value for intersession correlations
            - {}v{}_p_vals (float): p-value for intersession correlations, 
                corrected for multiple comparisons and tails

        - permpar (dict): 
            dictionary with keys of PermPar namedtuple
        - figpar (dict): 
            dictionary containing the following figure parameter dictionaries
            ["init"] (dict): dictionary with figure initialization parameters
            ["save"] (dict): dictionary with figure saving parameters
            ["dirs"] (dict): dictionary with additional figure parameters

    Optional args:
        - permute (bool):
            type of permutation to due ("tracking", "sess" or "all")
            default: "sess"
        - corr_type (str):
            type of correlation run, i.e. "corr" or "R_sqr"
            default: "corr"
        - title (str):
            plot title
            default: None
        - small (bool):
            if True, smaller subplots are plotted
            default: True

    Returns:
        - ax (2D array): 
            array of subplots
    """

    norm = False
    if permute in ["sess", "all"]:
        corr_type = f"diff_{corr_type}"
        if corr_type == "diff_corr":
            norm = True
            title = title.replace("Correlations", "Normalized correlations")

    norm_str = "_norm" if norm else ""

    sess_pairs = get_sorted_sess_pairs(idx_corr_df, norm=norm)
    n_pairs = int(np.ceil(len(sess_pairs) / 2) * 2)  # multiple of 2

    figpar = sess_plot_util.fig_init_linpla(figpar,
                                            kind="reg",
                                            n_sub=int(n_pairs / 2))

    figpar["init"]["ncols"] = n_pairs
    figpar["init"]["sharey"] = "row"

    figpar["init"]["gs"] = {"hspace": 0.25}
    if small:
        figpar["init"]["subplot_wid"] = 2.7
        figpar["init"]["subplot_hei"] = 4.21
        figpar["init"]["gs"]["wspace"] = 0.2
    else:
        figpar["init"]["subplot_wid"] = 3.3
        figpar["init"]["subplot_hei"] = 4.71
        figpar["init"]["gs"]["wspace"] = 0.3

    fig, ax = plot_util.init_fig(n_pairs * 2, **figpar["init"])
    if title is not None:
        fig.suptitle(title, y=0.98, weight="bold")

    plane_pts = get_idx_corr_ylims(idx_corr_df, norm=norm)
    lines = [None, None]

    comp_info = misc_analys.get_comp_info(permpar)

    logger.info(f"{comp_info}:", extra={"spacing": "\n"})
    for (line, plane), lp_df in idx_corr_df.groupby(["lines", "planes"]):
        li, pl, col, _ = plot_helper_fcts.get_line_plane_idxs(line, plane)
        linpla_name = plot_helper_fcts.get_line_plane_name(line, plane)
        lines[li] = line.split("-")[0].replace("23", "2/3")

        if len(lp_df) != 1:
            raise RuntimeError("Expected only one row per line/plane.")
        row = lp_df.loc[lp_df.index[0]]

        lp_sig_str = f"{linpla_name:6}:"
        for s, sess_pair in enumerate(sess_pairs):
            sub_ax = ax[pl, s]
            if s == 0:
                sub_ax.set_ylim(plane_pts[pl])

            col_base = f"{sess_pair[0]}v{sess_pair[1]}"

            CI = row[f"{col_base}_null_CIs"]
            extr = np.asarray([CI[0], CI[2]])
            plot_util.plot_CI(sub_ax,
                              extr,
                              med=CI[1],
                              x=li,
                              width=0.45,
                              med_rat=0.025)

            y = row[f"{col_base}{norm_str}_corrs"]

            err = row[f"{col_base}{norm_str}_corr_stds"]
            plot_util.plot_ufo(sub_ax,
                               x=li,
                               y=y,
                               err=err,
                               color=col,
                               capsize=8)

            # add significance markers
            p_val = row[f"{col_base}_p_vals"]
            side = np.sign(y - CI[1])
            sensitivity = misc_analys.get_sensitivity(permpar)
            sig_str = misc_analys.get_sig_symbol(p_val,
                                                 sensitivity=sensitivity,
                                                 side=side,
                                                 tails=permpar["tails"],
                                                 p_thresh=permpar["p_val"])

            if len(sig_str):
                high = np.max([CI[-1], y + err])
                plot_util.add_signif_mark(sub_ax,
                                          li,
                                          high,
                                          rel_y=0.1,
                                          color=col,
                                          fontsize=24,
                                          mark=sig_str)

            sess_str = f"S{sess_pair[0]}v{sess_pair[1]}: "
            lp_sig_str = f"{lp_sig_str}{TAB}{sess_str}{p_val:.5f}{sig_str:3}"

        logger.info(lp_sig_str, extra={"spacing": TAB})

    # Add plane, line info to plots
    sess_plot_util.format_linpla_subaxes(ax,
                                         datatype="roi",
                                         lines=["", ""],
                                         xticks=[0, 1],
                                         ylab="",
                                         xlab="",
                                         kind="traces")

    xs = np.arange(len(lines))
    pad_x = 0.6 * (xs[1] - xs[0])
    for row_n in range(len(ax)):
        for col_n in range(len(ax[row_n])):
            sub_ax = ax[row_n, col_n]
            sub_ax.tick_params(axis="x", which="both", bottom=False)
            sub_ax.set_xticks(xs)
            sub_ax.set_xticklabels(lines, weight="bold")
            sub_ax.set_xlim([xs[0] - pad_x, xs[-1] + pad_x])

            sub_ax.set_ylim(plane_pts[row_n])
            sub_ax.set_yticks(plane_pts[row_n])

            if row_n == 0:
                if col_n < len(sess_pairs):
                    s1, s2 = sess_pairs[col_n]
                    sess_pair_title = f"Session {s1} v {s2}"
                    sub_ax.set_title(sess_pair_title,
                                     fontweight="bold",
                                     y=1.07)
                sub_ax.spines["bottom"].set_visible(True)

        plot_util.set_interm_ticks(ax[row_n],
                                   3,
                                   axis="y",
                                   weight="bold",
                                   share=False,
                                   update_ticks=True)

    return ax
def run_sess_lstm(sessid, args):

    if args.parallel and args.plt_bkend is not None:
        plt.switch_backend(args.plt_bkend) # needs to be repeated within joblib

    args.seed = rand_util.seed_all(args.seed, args.device, seed_torch=True)

    train_p = 0.8
    lr = 1. * 10**(-args.lr_ex)
    if args.conv:
        conv_str = "_conv"
        outch_str = f"_{args.out_ch}outch"
    else:
        conv_str = ""
        outch_str = ""

    # Input output parameters
    n_stim_s  = 0.6
    n_roi_s = 0.3

    # Stim/traces for training
    train_gabfr = 0
    train_post = 0.9 # up to C
    roi_train_pre = 0 # from A
    stim_train_pre   = 0.3 # from preceeding grayscreen

    # Stim/traces for testing (separated for unexp vs exp)
    test_gabfr = 3
    test_post  = 0.6 # up to grayscreen
    roi_test_pre = 0 # from D/U
    stim_test_pre   = 0.3 # from preceeding C

    sess = sess_gen_util.init_sessions(
        sessid, args.datadir, args.mouse_df, args.runtype, full_table=False, 
        fluor="dff", dend="extr", run=True, temp_log="warning")[0]

    analysdir = sess_gen_util.get_analysdir(
        sess.mouse_n, sess.sess_n, sess.plane, stimtype=args.stimtype, 
        comp=None)
    dirname = Path(args.output, analysdir)
    file_util.createdir(dirname, log_dir=False)

    # Must not scale ROIs or running BEFOREHAND. Must do after to use only 
    # network available data.

    # seq x frame x gabor x par
    logger.info("Preparing stimulus parameter dataframe", 
        extra={"spacing": "\n"})
    train_stim_wins, run_stats = sess_data_util.get_stim_data(
        sess, args.stimtype, n_stim_s, train_gabfr, stim_train_pre, 
        train_post, gabk=16, run=True)

    logger.info("Adding ROI data")
    xran, train_roi_wins, roi_stats = sess_data_util.get_roi_data(
        sess, args.stimtype, n_roi_s, train_gabfr, roi_train_pre, train_post, 
        gabk=16)

    logger.warning("Preparing windowed datasets (too slow - to be improved)")
    raise NotImplementedError("Not implemented properly - some error leads "
        "to excessive memory requests.")
    test_stim_wins = []
    test_roi_wins  = []
    for unexp in [0, 1]:
        stim_wins = sess_data_util.get_stim_data(
            sess, args.stimtype, n_stim_s, test_gabfr, stim_test_pre, 
            test_post, unexp, gabk=16, run_mean=run_stats[0], 
            run_std=run_stats[1])
        test_stim_wins.append(stim_wins)
        
        roi_wins = sess_data_util.get_roi_data(sess, args.stimtype, n_roi_s,  
                           test_gabfr, roi_test_pre, test_post, unexp, gabk=16, 
                           roi_means=roi_stats[0], roi_stds=roi_stats[1])[1]
        test_roi_wins.append(roi_wins)

    n_pars = train_stim_wins.shape[-1] # n parameters (121)
    n_rois = train_roi_wins.shape[-1] # n ROIs

    hyperstr = (f"{args.hidden_dim}hd_{args.num_layers}hl_{args.lr_ex}lrex_"
                f"{args.batchsize}bs{outch_str}{conv_str}")

    dls = data_util.create_dls(train_stim_wins, train_roi_wins, train_p=train_p, 
                            test_p=0, batchsize=args.batchsize, thresh_cl=0, 
                            train_shuff=True)[0]
    train_dl, val_dl, _ = dls

    test_dls = []
    
    for s in [0, 1]:
        dl = data_util.init_dl(test_stim_wins[s], test_roi_wins[s], 
                            batchsize=args.batchsize)
        test_dls.append(dl)

    logger.info("Running LSTM")
    if args.conv:
        lstm = ConvPredROILSTM(args.hidden_dim, n_rois, out_ch=args.out_ch, 
                            num_layers=args.num_layers, dropout=args.dropout)
    else:
        lstm = PredLSTM(n_pars, args.hidden_dim, n_rois, 
                        num_layers=args.num_layers, dropout=args.dropout)

    lstm = lstm.to(args.device)
    lstm.loss_fn = torch.nn.MSELoss(size_average=False)
    lstm.opt = torch.optim.Adam(lstm.parameters(), lr=lr)

    loss_df = pd.DataFrame(
        np.nan, index=range(args.n_epochs), columns=["train", "val"])
    min_val = np.inf
    for ep in range(args.n_epochs):
        logger.info(f"====> Epoch {ep}", extra={"spacing": "\n"})
        if ep == 0:
            train_loss = run_dl(lstm, train_dl, args.device, train=False)    
        else:
            train_loss = run_dl(lstm, train_dl, args.device, train=True)
        val_loss = run_dl(lstm, val_dl, args.device, train=False)
        loss_df["train"].loc[ep] = train_loss/train_dl.dataset.n_samples
        loss_df["val"].loc[ep] = val_loss/val_dl.dataset.n_samples
        logger.info(f"Training loss  : {loss_df['train'].loc[ep]}")
        logger.info(f"Validation loss: {loss_df['val'].loc[ep]}")

        # record model if training is lower than val, and val reaches a new low
        if ep == 0 or val_loss < min_val:
            prev_model = glob.glob(str(Path(dirname, f"{hyperstr}_ep*.pth")))
            prev_df = glob.glob(str(Path(dirname, f"{hyperstr}.csv")))
            min_val = val_loss
            saved_ep = ep
                
            if len(prev_model) == 1 and len(prev_df) == 1:
                Path(prev_model[0]).unlink()
                Path(prev_df[0]).unlink()

            savename = f"{hyperstr}_ep{ep}"
            savefile = Path(dirname, savename)
        
            torch.save({"net": lstm.state_dict(), "opt": lstm.opt.state_dict()},
                        f"{savefile}.pth")
        
            file_util.saveinfo(loss_df, hyperstr, dirname, "csv")

    plot_util.linclab_plt_defaults(font=["Arial", "Liberation Sans"], 
                                   fontdir=DEFAULT_FONTDIR)
    fig, ax = plt.subplots(1)
    for dataset in ["train", "val"]:
        plot_util.plot_traces(ax, range(args.n_epochs), np.asarray(loss_df[dataset]), 
                  label=dataset, title=f"Average loss (MSE) ({n_rois} ROIs)", 
                  xticks="auto")
    fig.savefig(Path(dirname, f"{hyperstr}_loss"))

    savemod = Path(dirname, f"{hyperstr}_ep{saved_ep}.pth")
    checkpoint = torch.load(savemod)
    lstm.load_state_dict(checkpoint["net"]) 

    n_samples = 20
    val_idx = np.random.choice(range(val_dl.dataset.n_samples), n_samples)
    val_samples = val_dl.dataset[val_idx]
    xrans = data_util.get_win_xrans(xran, val_samples[1].shape[1], val_idx.tolist())

    fig, ax = plot_util.init_fig(n_samples, ncols=4, sharex=True, subplot_hei=2, 
                                subplot_wid=5)


    lstm.eval()
    with torch.no_grad():
        batch_len, seq_len, n_items = val_samples[1].shape
        pred_tr = lstm(val_samples[0].transpose(1, 0).to(args.device))
        pred_tr = pred_tr.view([seq_len, batch_len, n_items]).transpose(1, 0)

    for lab, data in zip(["target", "pred"], [val_samples[1], pred_tr]):
        data = data.numpy()
        for n in range(n_samples):
            roi_n = np.random.choice(range(data.shape[-1]))
            sub_ax = plot_util.get_subax(ax, n)
            plot_util.plot_traces(sub_ax, xrans[n], data[n, :, roi_n], 
                label=lab, xticks="auto")
            plot_util.set_ticks(sub_ax, "x", xran[0], xran[-1], n=7)

    sess_plot_util.plot_labels(ax, train_gabfr, plot_vals="exp", pre=roi_train_pre, 
                            post=train_post)

    fig.suptitle(f"Target vs predicted validation traces ({n_rois} ROIs)")
    fig.savefig(Path(dirname, f"{hyperstr}_traces"))
Esempio n. 4
0
def plot_glm_expl_var(analyspar, sesspar, stimpar, extrapar, glmpar,
                      sess_info, all_expl_var, figpar=None, savedir=None):
    """
    plot_glm_expl_var(analyspar, sesspar, stimpar, extrapar, 
                       sess_info, all_expl_var)

    From dictionaries, plots explained variance for different variables for 
    each ROI.

    Required args:
        - analyspar (dict)    : dictionary with keys of AnalysPar namedtuple
        - sesspar (dict)      : dictionary with keys of SessPar namedtuple 
        - stimpar (dict)      : dictionary with keys of StimPar namedtuple
        - glmpar (dict)       : dictionary with keys of GLMPar namedtuple
        - extrapar (dict)     : dictionary containing additional analysis 
                                parameters
            ["analysis"] (str): analysis type (e.g., "v")
        - sess_info (dict)    : dictionary containing information from each
                                session 
            ["mouse_ns"] (list)   : mouse numbers
            ["sess_ns"] (list)    : session numbers  
            ["lines"] (list)      : mouse lines
            ["planes"] (list)     : imaging planes
            ["nrois"] (list)      : number of ROIs in session

        - all_expl_var (list) : list of dictionaries with explained variance 
                                for each session set, with each glm 
                                coefficient as a key:
            ["full"] (list)    : list of full explained variance stats for 
                                 every ROI, structured as ROI x stats
            ["coef_all"] (dict): max explained variance for each ROI with each
                                 coefficient as a key, structured as ROI x stats
            ["coef_uni"] (dict): unique explained variance for each ROI with 
                                 each coefficient as a key, 
                                 structured as ROI x stats
            ["rois"] (list)    : ROI numbers (-1 for GLMs fit to 
                                 mean/median ROI activity)
    
    Optional args:
        - figpar (dict): dictionary containing the following figure parameter 
                         dictionaries
                         default: None
            ["init"] (dict): dictionary with figure initialization parameters
            ["save"] (dict): dictionary with figure saving parameters
            ["dirs"] (dict): dictionary with additional figure parameters
        - savedir (str): path of directory in which to save plots.
                         default: None
    
    Returns:
        - fulldir (str) : final path of the directory in which the figure is 
                          saved (may differ from input savedir, if datetime 
                          subfolder is added.)
        - savename (str): name under which the figure is saved
    """

    stimstr_pr = sess_str_util.stim_par_str(
        stimpar["stimtype"], stimpar["visflow_dir"], stimpar["visflow_size"], 
        stimpar["gabk"], "print")
    dendstr_pr = sess_str_util.dend_par_str(
        analyspar["dend"], sesspar["plane"], "roi", "print")

    sessstr = sess_str_util.sess_par_str(
        sesspar["sess_n"], stimpar["stimtype"], sesspar["plane"], 
        stimpar["visflow_dir"], stimpar["visflow_size"], stimpar["gabk"]) 
    dendstr = sess_str_util.dend_par_str(
        analyspar["dend"], sesspar["plane"], "roi")

    # extract some info from sess_info
    keys = ["mouse_ns", "sess_ns", "lines", "planes"]
    [mouse_ns, sess_ns, lines, planes] = [sess_info[key] for key in keys]

    n_sess = len(mouse_ns)
    
    nroi_strs = sess_str_util.get_nroi_strs(sess_info, style="par")

    plot_bools = [ev["rois"] not in [[-1], "all"] for ev in all_expl_var]
    n_sess = sum(plot_bools)

    if stimpar["stimtype"] == "gabors":
        xyzc_dims = ["unexpected", "gabor_frame", "run_data", "pup_diam_data"]
        log_dims = xyzc_dims + ["gabor_mean_orientation"]
    elif stimpar["stimtype"] == "visflow":
        xyzc_dims = [
            "unexpected", "main_flow_direction", "run_data", "pup_diam_data"
            ]
        log_dims = xyzc_dims
    
    # start plotting
    logger.info("Plotting GLM full and unique explained variance for "
        f"{', '.join(xyzc_dims)}.", extra={"spacing": "\n"})

    if n_sess > 0:
        if figpar is None:
            figpar = sess_plot_util.init_figpar()

        figpar = copy.deepcopy(figpar)
        cmap = plot_util.linclab_colormap(nbins=100, no_white=True)

        if figpar["save"]["use_dt"] is None:
            figpar["save"]["use_dt"] = gen_util.create_time_str()
        figpar["init"]["ncols"] = n_sess
        figpar["init"]["sharex"] = False
        figpar["init"]["sharey"] = False
        figpar["init"]["gs"] = {"wspace": 0.2, "hspace": 0.35}
        figpar["save"]["fig_ext"] = "png"
        
        fig, ax = plot_util.init_fig(2 * n_sess, **figpar["init"], proj="3d")

        fig.suptitle("Explained variance per ROI", y=1)

        # get colormap range
        c_range = [np.inf, -np.inf]
        c_key = xyzc_dims[3]

        for expl_var in all_expl_var:
            for var_type in ["coef_all", "coef_uni"]:
                rs = np.where(np.asarray(expl_var["rois"]) != -1)[0]
                if c_key in expl_var[var_type].keys():
                    c_data = np.asarray(expl_var[var_type][c_key])[rs, 0]
                    # adjust colormap range
                    c_range[0] = np.min([c_range[0], min(c_data)])
                    c_range[1] = np.max([c_range[1], max(c_data)])
        
        if not np.isfinite(sum(c_range)):
            c_range = [-0.5, 0.5] # dummy range
        else:
            c_range = plot_util.rounded_lims(c_range, out=True)

    else:
        logger.info("No plots, as only results across ROIs are included")
        fig = None

    i = 0
    for expl_var in all_expl_var:
        # collect info for plotting and logging results across ROIs
        rs = np.where(np.asarray(expl_var["rois"]) != -1)[0]
        all_rs = np.where(np.asarray(expl_var["rois"]) == -1)[0]
        if len(all_rs) != 1:
            raise RuntimeError("Expected only one result for all ROIs.")
        else:
            all_rs = all_rs[0]
            full_ev = expl_var["full"][all_rs]

        title = (f"Mouse {mouse_ns[i]} - {stimstr_pr}\n(sess {sess_ns[i]}, "
                f"{lines[i]} {planes[i]}{dendstr_pr},{nroi_strs[i]})")
        logger.info(title, extra={"spacing": "\n"})

        math_util.log_stats(full_ev, stat_str="\nFull explained variance")

        dim_length = max([len(dim) for dim in log_dims])
        
        for v, var_type in enumerate(["coef_all", "coef_uni"]):
            if var_type == "coef_all":
                sub_title = "Explained variance per coefficient"
            elif var_type == "coef_uni":
                sub_title = "Unique explained variance\nper coefficient"
            logger.info(sub_title, extra={"spacing": "\n"})

            dims_all = []
            for key in log_dims:
                if key in xyzc_dims:
                    # get mean/med
                    if key not in expl_var[var_type].keys():
                        dims_all.append("dummy")
                        continue

                    dims_all.append(np.asarray(expl_var[var_type][key])[rs, 0])
                math_util.log_stats(
                    expl_var[var_type][key][all_rs], 
                    stat_str=key.ljust(dim_length), log_spacing=TAB
                    )

            if not plot_bools[-1]:
                continue

            if v == 0:
                y = 1.12
                subpl_title = f"{title}\n{sub_title}"
            else:
                y = 1.02
                subpl_title = sub_title

            # retrieve values and names for each dimension, including dummy 
            # dimensions
            use_xyzc_dims = []
            n_vals = None
            dummies = []
            pads = [16, 16, 20]
            for d, dim in enumerate(dims_all):
                dim_name = xyzc_dims[d].replace("_", " ")
                if " direction"  in dim_name:
                    dim_name = dim_name.replace(" direction", "\ndirection")
                    pads[d] = 24
                if isinstance(dim, str) and dim == "dummy":
                    dummies.append(d)
                    use_xyzc_dims.append(f"{dim_name} (dummy)")
                else:
                    n_vals = len(dim)
                    use_xyzc_dims.append(dim_name)
            
            for d in dummies:
                dims_all[d] = np.zeros(n_vals)

            [x_data, y_data, z_data, c_data] = dims_all

            sub_ax = ax[v, i]
            im = sub_ax.scatter(
                x_data, y_data, z_data, c=c_data, cmap=cmap, 
                vmin=c_range[0], vmax=c_range[1]
                )
            sub_ax.set_title(subpl_title, y=y)
            # sub_ax.set_zlim3d(0, 1.0)

            # adjust padding for z axis
            sub_ax.tick_params(axis='z', which='major', pad=10)

            # add labels
            sub_ax.set_xlabel(use_xyzc_dims[0], labelpad=pads[0])
            sub_ax.set_ylabel(use_xyzc_dims[1], labelpad=pads[1])
            sub_ax.set_zlabel(use_xyzc_dims[2], labelpad=pads[2])

            if v == 0:
                full_ev_lab = math_util.log_stats(
                    full_ev, stat_str="Full EV", ret_str_only=True
                    )
                sub_ax.plot([], [], c="k", label=full_ev_lab)
                sub_ax.legend()

        i += 1

    if fig is not None:
        plot_util.add_colorbar(
            fig, im, n_sess, label=use_xyzc_dims[3],
            space_fact=np.max([2, n_sess])
            )

        # plot 0 planes, and lines
        for sub_ax in ax.reshape(-1):
            sub_ax.autoscale(False)
            all_lims = [sub_ax.get_xlim(), sub_ax.get_ylim(), sub_ax.get_zlim()]
            xs, ys, zs = [
                [vs[0] - (vs[1] - vs[0]) * 0.02, vs[1] + (vs[1] - vs[0]) * 0.02]
                for vs in all_lims
                ]
            
            for plane in ["x", "y", "z"]:
                if plane == "x":
                    xx, yy = np.meshgrid(xs, ys)
                    zz = xx * 0
                    x_flat = xs
                    y_flat, z_flat = [0, 0], [0, 0]
                elif plane == "y":
                    yy, zz = np.meshgrid(ys, zs)
                    xx = yy * 0
                    y_flat = ys
                    z_flat, x_flat = [0, 0], [0, 0]
                elif plane == "z":
                    zz, xx = np.meshgrid(zs, xs)
                    yy = zz * 0
                    z_flat = zs
                    x_flat, y_flat = [0, 0], [0, 0]
                
                sub_ax.plot_surface(xx, yy, zz, alpha=0.05, color="k")
                sub_ax.plot(
                    x_flat, y_flat, z_flat, alpha=0.4, color="k", ls=(0, (2, 2))
                    )

    if savedir is None:
        savedir = Path(
            figpar["dirs"]["roi"],
            figpar["dirs"]["glm"])

    savename = (f"roi_glm_ev_{sessstr}{dendstr}")

    fulldir = plot_util.savefig(fig, savename, savedir, **figpar["save"])

    return fulldir, savename                              
Esempio n. 5
0
def plot_mag_change(analyspar, sesspar, stimpar, extrapar, permpar, quantpar, 
                    sess_info, mags, figpar=None, savedir=None):
    """
    plot_mag_change(analyspar, sesspar, stimpar, extrapar, permpar, quantpar, 
                    sess_info, mags) 

    From dictionaries, plots magnitude of change in unexpected and expected
    responses across quantiles.

    Returns figure name and save directory path.

    Required args:
        - analyspar (dict): dictionary with keys of AnalysPar namedtuple
        - sesspar (dict)  : dictionary with keys of SessPar namedtuple 
        - stimpar (dict)  : dictionary with keys of StimPar namedtuple
        - extrapar (dict) : dictionary containing additional analysis 
                            parameters
            ["analysis"] (str): analysis type (e.g., "m")
            ["datatype"] (str): datatype (e.g., "run", "roi")
            ["seed"]     (int): seed value used
        - permpar (dict)  : dictionary with keys of PermPar namedtuple 
        - quantpar (dict) : dictionary with keys of QuantPar namedtuple
        - roigrppar (dict): dictionary with keys of RoiGrpPar namedtuple
        - sess_info (dict): dictionary containing information from each
                            session 
            ["mouse_ns"] (list)   : mouse numbers
            ["sess_ns"] (list)    : session numbers  
            ["lines"] (list)      : mouse lines
            ["planes"] (list)     : imaging planes
            ["nrois"] (list)      : number of ROIs in session

        - mags (dict)     : dictionary containing magnitude data to plot
            ["L2"] (array-like)    : nested list containing L2 norms, 
                                     structured as: 
                                         sess x scaling x unexp
            ["L2_sig"] (list)      : L2 significance results for each session 
                                         ("hi", "lo" or "no")
            ["mag_sig"] (list)     : magnitude significance results for each 
                                     session 
                                         ("hi", "lo" or "no")
            ["mag_st"] (array-like): array or nested list containing magnitude 
                                     stats across ROIs, structured as: 
                                         sess x scaling x unexp x stats

    Optional args:
        - figpar (dict): dictionary containing the following figure parameter 
                         dictionaries
                         default: None
            ["init"] (dict): dictionary with figure initialization parameters
            ["save"] (dict): dictionary with figure saving parameters
            ["dirs"] (dict): dictionary with additional figure parameters
        - savedir (str): path of directory in which to save plots.
                         default: None
    
    Returns:
        - fulldir (str) : final path of the directory in which the figure is 
                          saved (may differ from input savedir, if datetime 
                          subfolder is added.)
        - savename (str): name under which the figure is saved
    """
    
    sessstr_pr = sess_str_util.sess_par_str(
        sesspar["sess_n"], stimpar["stimtype"], sesspar["plane"], 
        stimpar["visflow_dir"], stimpar["visflow_size"], stimpar["gabk"], 
        "print")
    statstr_pr = sess_str_util.stat_par_str(
        analyspar["stats"], analyspar["error"], "print")
    dendstr_pr = sess_str_util.dend_par_str(
        analyspar["dend"], sesspar["plane"], extrapar["datatype"], "print")
        
    sessstr = sess_str_util.sess_par_str(
        sesspar["sess_n"], stimpar["stimtype"], sesspar["plane"], 
        stimpar["visflow_dir"],stimpar["visflow_size"], stimpar["gabk"]) 
    dendstr = sess_str_util.dend_par_str(
        analyspar["dend"], sesspar["plane"], extrapar["datatype"])
     
    datatype = extrapar["datatype"]
    dimstr = sess_str_util.datatype_dim_str(datatype)
    
    # extract some info from sess_info
    keys = ["mouse_ns", "sess_ns", "lines", "planes"]
    [mouse_ns, sess_ns, lines, planes] = [sess_info[key] for key in keys]
    nroi_strs = sess_str_util.get_nroi_strs(
        sess_info, empty=(datatype!="roi"), style="par"
        )    

    n_sess = len(mouse_ns)

    qu_ns = [gen_util.pos_idx(q, quantpar["n_quants"]) + 1 
        for q in quantpar["qu_idx"]]
    if len(qu_ns) != 2:
        raise ValueError(f"Expected 2 quantiles, not {len(qu_ns)}.")
    
    mag_st = np.asarray(mags["mag_st"])

    if figpar is None:
        figpar = sess_plot_util.init_figpar()

    figpar = copy.deepcopy(figpar)
    if figpar["save"]["use_dt"] is None:
        figpar["save"]["use_dt"] = gen_util.create_time_str()
 
    figpar["init"]["subplot_wid"] *= n_sess/2.0
    
    scales = [False, True]

    # get plot elements
    barw = 0.75
    # scaling strings for printing and filenames
    leg = ["exp", "unexp"]    
    cent, bar_pos, xlims = plot_util.get_barplot_xpos(n_sess, len(leg), barw)   
    title = (u"Magnitude ({}) of difference in activity".format(statstr_pr) +
        f"\nbetween Q{qu_ns[0]} and {qu_ns[1]} across {dimstr} "
        f"\n({sessstr_pr})")
    labels = [f"Mouse {mouse_ns[i]} sess {sess_ns[i]},\n {lines[i]} {planes[i]}"
        f"{dendstr_pr}{nroi_strs[i]}" for i in range(n_sess)]

    figs, axs = [], []
    for sc, scale in enumerate(scales):
        scalestr_pr = sess_str_util.scale_par_str(scale, "print")
        fig, ax = plot_util.init_fig(1, **figpar["init"])
        figs.append(fig)
        axs.append(ax)
        sub_ax = ax[0, 0]
        # always set ticks (even again) before setting labels
        sub_ax.set_xticks(cent)
        sub_ax.set_xticklabels(labels)
        title_scale = u"{}{}".format(title, scalestr_pr)
        sess_plot_util.add_axislabels(
            sub_ax, fluor=analyspar["fluor"], area=True, scale=scale, x_ax="", 
            datatype=datatype)
        for s, lab in enumerate(leg):
            xpos = list(zip(*bar_pos))[s]
            plot_util.plot_bars(
                sub_ax, xpos, mag_st[:, sc, s, 0], err=mag_st[:, sc, s, 1:], 
                width=barw, xlims=xlims, xticks="None", label=lab, capsize=4,
                title=title_scale, hline=0)
    
    # add significance markers
    for i in range(n_sess):
        signif = mags["mag_sig"][i]
        if signif in ["hi", "lo"]:
            xpos = bar_pos[i]
            for sc, (ax, scale) in enumerate(zip(axs, scales)):
                yval = mag_st[i, sc, :, 0]
                yerr = mag_st[i, sc, :, 1:]
                plot_util.plot_barplot_signif(ax[0, 0], xpos, yval, yerr)
    
    plot_util.turn_off_extra(ax, n_sess)

   # figure directory
    if savedir is None:
        savedir = Path(
            figpar["dirs"][datatype], 
            figpar["dirs"]["unexp_qu"], 
            figpar["dirs"]["mags"])
    
    log_dir = False
    for i, (fig, scale) in enumerate(zip(figs, scales)):
        if i == len(figs) - 1:
            log_dir = True
        scalestr = sess_str_util.scale_par_str(scale)
        savename = f"{datatype}_mag_diff_{sessstr}{dendstr}"
        savename_full = f"{savename}{scalestr}"
        fulldir = plot_util.savefig(
            fig, savename_full, savedir, log_dir=log_dir, ** figpar["save"])

    return fulldir, savename
Esempio n. 6
0
def plot_traces_by_qu_unexp_sess(analyspar, sesspar, stimpar, extrapar, 
                                quantpar, sess_info, trace_stats, figpar=None, 
                                savedir=None, modif=False):
    """
    plot_traces_by_qu_unexp_sess(analyspar, sesspar, stimpar, extrapar, 
                                quantpar, sess_info, trace_stats)

    From dictionaries, plots traces by quantile/unexpected with each session in a 
    separate subplot.
    
    Returns figure name and save directory path.
    
    Required args:
        - analyspar (dict)  : dictionary with keys of AnalysPar namedtuple
        - sesspar (dict)    : dictionary with keys of SessPar namedtuple
        - stimpar (dict)    : dictionary with keys of StimPar namedtuple
        - extrapar (dict)   : dictionary containing additional analysis 
                              parameters
            ["analysis"] (str): analysis type (e.g., "t")
            ["datatype"] (str): datatype (e.g., "run", "roi")
        - quantpar (dict)   : dictionary with keys of QuantPar namedtuple
        - sess_info (dict)  : dictionary containing information from each
                              session 
            ["mouse_ns"] (list)   : mouse numbers
            ["sess_ns"] (list)    : session numbers  
            ["lines"] (list)      : mouse lines
            ["planes"] (list)     : imaging planes
            if extrapar["datatype"] == "roi":
            ["nrois"] (list)      : number of ROIs in session

        - trace_stats (dict): dictionary containing trace stats information
            ["xrans"] (list)           : time values for the frames, for each 
                                         session
            ["all_stats"] (list)       : list of 4D arrays or lists of trace 
                                         data statistics across ROIs for each
                                         session, structured as:
                                            sess x unexp x quantiles x
                                            stats (me, err) x frames
            ["all_counts"] (array-like): number of sequences, structured as:
                                                sess x unexp x quantiles
                
    Optional args:
        - figpar (dict): dictionary containing the following figure parameter 
                         dictionaries
                         default: None
            ["init"] (dict): dictionary with figure initialization parameters
            ["save"] (dict): dictionary with figure saving parameters
            ["dirs"] (dict): dictionary with additional figure parameters
        - savedir (str): path of directory in which to save plots.
                         default: None    
        - modif (bool) : if True, modified (slimmed-down) plots are created
                         instead
                         default: False
    
    Returns:
        - fulldir (str) : final path of the directory in which the figure is 
                          saved (may differ from input savedir, if datetime 
                          subfolder is added.)
        - savename (str): name under which the figure is saved
    """
 
    stimstr_pr = sess_str_util.stim_par_str(
        stimpar["stimtype"], stimpar["visflow_dir"], stimpar["visflow_size"],
        stimpar["gabk"], "print")
    statstr_pr = sess_str_util.stat_par_str(
        analyspar["stats"], analyspar["error"], "print")
    dendstr_pr = sess_str_util.dend_par_str(
        analyspar["dend"], sesspar["plane"], extrapar["datatype"], "print")
        
    sessstr = sess_str_util.sess_par_str(
        sesspar["sess_n"], stimpar["stimtype"], sesspar["plane"], 
        stimpar["visflow_dir"], stimpar["visflow_size"], stimpar["gabk"])
    dendstr = sess_str_util.dend_par_str(
        analyspar["dend"], sesspar["plane"], extrapar["datatype"])
     
    datatype = extrapar["datatype"]
    dimstr = sess_str_util.datatype_dim_str(datatype)

    # extract some info from sess_info
    keys = ["mouse_ns", "sess_ns", "lines", "planes"]
    [mouse_ns, sess_ns, lines, planes] = [sess_info[key] for key in keys]
    nroi_strs = sess_str_util.get_nroi_strs(sess_info, empty=(datatype!="roi")) 

    n_sess = len(mouse_ns)

    xrans      = [np.asarray(xran) for xran in trace_stats["xrans"]]
    all_stats  = [np.asarray(sessst) for sessst in trace_stats["all_stats"]]
    all_counts = trace_stats["all_counts"]

    cols, lab_cols = sess_plot_util.get_quant_cols(quantpar["n_quants"])
    alpha = np.min([0.4, 0.8 / quantpar["n_quants"]])

    unexps = ["exp", "unexp"]
    n = 6
    if figpar is None:
        figpar = sess_plot_util.init_figpar()
    
    fig, ax = plot_util.init_fig(n_sess, **figpar["init"])
    for i in range(n_sess):
        sub_ax = plot_util.get_subax(ax, i)
        for s, [col, leg_ext] in enumerate(zip(cols, unexps)):
            for q, qu_idx in enumerate(quantpar["qu_idx"]):
                qu_lab = ""
                if quantpar["n_quants"] > 1:
                    qu_lab = "{} ".format(sess_str_util.quantile_str(
                        qu_idx, quantpar["n_quants"], str_type="print"
                        ))
                if modif:
                    line = "2/3" if "23" in lines[i] else "5"
                    plane = "somata" if "soma" in planes[i] else "dendrites"
                    title = f"M{mouse_ns[i]} - layer {line} {plane}{dendstr_pr}"
                    leg = f"{qu_lab}{leg_ext}" if i == 0 else None
                    y_ax = None if i == 0 else ""

                else:
                    title=(f"Mouse {mouse_ns[i]} - {stimstr_pr}, " 
                        u"{}\n".format(statstr_pr) + f"across {dimstr} (sess "
                        f"{sess_ns[i]}, {lines[i]} {planes[i]}{dendstr_pr}"
                        f"{nroi_strs[i]})")
                    leg = f"{qu_lab}{leg_ext} ({all_counts[i][s][q]})"
                    y_ax = None

                plot_util.plot_traces(
                    sub_ax, xrans[i], all_stats[i][s, q, 0], 
                    all_stats[i][s, q, 1:], title, color=col[q], alpha=alpha, 
                    label=leg, n_xticks=n, xticks="auto")
                sess_plot_util.add_axislabels(
                    sub_ax, fluor=analyspar["fluor"], datatype=datatype, 
                    y_ax=y_ax)
    
    plot_util.turn_off_extra(ax, n_sess)

    if stimpar["stimtype"] == "gabors": 
        sess_plot_util.plot_labels(
            ax, stimpar["gabfr"], "both", pre=stimpar["pre"], 
            post=stimpar["post"], cols=lab_cols, 
            sharey=figpar["init"]["sharey"])
    
    if savedir is None:
        savedir = Path(
            figpar["dirs"][datatype], 
            figpar["dirs"]["unexp_qu"])

    qu_str = f"_{quantpar['n_quants']}q"
    if quantpar["n_quants"] == 1:
        qu_str = ""

    savename = f"{datatype}_av_{sessstr}{dendstr}{qu_str}"
    fulldir = plot_util.savefig(fig, savename, savedir, **figpar["save"])

    return fulldir, savename
Esempio n. 7
0
def plot_rel_resp_data(rel_resp_df, analyspar, sesspar, stimpar, permpar, 
                       figpar, title=None, small=True):
    """
    plot_rel_resp_data((rel_resp_df, analyspar, sesspar, stimpar, permpar, 
                       figpar)

    Plots relative response errorbar data across sessions.

    Required args:
        - rel_resp_df (pd.DataFrame):
            dataframe with one row per session/line/plane, and the following 
            columns, in addition to the basic sess_df columns: 
            - rel_reg or rel_exp (list): 
                data stats for regular or expected data (me, err)
            - rel_unexp (list): data stats for unexpected data (me, err)
            for reg/exp/unexp data types, session comparisons, e.g. 1v2:
            - {data_type}_raw_p_vals_{}v{} (float): uncorrected p-value for 
                data differences between sessions 
            - {data_type}_p_vals_{}v{} (float): p-value for data between 
                sessions, corrected for multiple comparisons and tails

        - analyspar (dict): 
            dictionary with keys of AnalysPar namedtuple
        - sesspar (dict):
            dictionary with keys of SessPar namedtuple
        - stimpar (dict):
            dictionary with keys of StimPar namedtuple
        - permpar (dict): 
            dictionary with keys of PermPar namedtuple
        - figpar (dict): 
            dictionary containing the following figure parameter dictionaries
            ["init"] (dict): dictionary with figure initialization parameters
            ["save"] (dict): dictionary with figure saving parameters
            ["dirs"] (dict): dictionary with additional figure parameters

    Optional args:
        - title (str):
            plot title
            default: None
        - small (bool):
            if True, subplots are smaller
            default: False

    Returns:
        - ax (2D array): 
            array of subplots
    """

    sess_ns = misc_analys.get_sess_ns(sesspar, rel_resp_df)

    figpar = sess_plot_util.fig_init_linpla(figpar)

    figpar["init"]["sharey"] = "row"
    if small:
        figpar["init"]["subplot_hei"] = 4.1
        figpar["init"]["subplot_wid"] = 2.6
        figpar["init"]["gs"] = {"hspace": 0.2, "wspace": 0.25}
        figpar["init"]["gs"]["wspace"] = 0.25
    else:
        figpar["init"]["subplot_hei"] = 4.4
        figpar["init"]["subplot_wid"] = 3.0
        figpar["init"]["gs"] = {"wspace": 0.3}

    fig, ax = plot_util.init_fig(plot_helper_fcts.N_LINPLA, **figpar["init"])

    if title is not None:
        fig.suptitle(title, y=0.98, weight="bold")

    if stimpar["stimtype"] == "gabors":
        data_types = ["rel_reg", "rel_unexp"]
    elif stimpar["stimtype"] == "visflow":
        data_types = ["rel_exp", "rel_unexp"]
    else:
        gen_util.accepted_values_error(
            "stimpar['stimtype']", stimpar["stimtype"], ["gabors", "visflow"]
            )

    for (line, plane), lp_df in rel_resp_df.groupby(["lines", "planes"]):
        li, pl, col, dash = plot_helper_fcts.get_line_plane_idxs(line, plane)
        sub_ax = ax[pl, li]

        sess_indices = []
        lp_sess_ns = []
        for sess_n in sess_ns:
            rows = lp_df.loc[lp_df["sess_ns"] == sess_n]
            if len(rows) == 1:
                sess_indices.append(rows.index[0])
                lp_sess_ns.append(sess_n)
            elif len(rows) > 1:
                raise RuntimeError("Expected 1 row per line/plane/session.")

        sub_ax.axhline(
            y=1, ls=plot_helper_fcts.HDASH, c="k", lw=3.0, alpha=0.5, zorder=-13
            )

        colors = ["gray", col]
        fmts = ["-d", "-o"]
        alphas = [0.6, 0.8]
        ms = [12, None]
        for d, data_type in enumerate(data_types):
            data = np.asarray([lp_df.loc[i, data_type] for i in sess_indices])
            plot_util.plot_errorbars(
                sub_ax, data[:, 0], data[:, 1:].T, lp_sess_ns, color=colors[d], 
                alpha=alphas[d], ms=ms[d], fmt=fmts[d], line_dash=dash)
            
    highest = None
    for dry_run in [True, False]: # to get correct data heights
        for data_type in data_types:
            ctrl = ("unexp" not in data_type)
            highest = add_between_sess_sig(
                ax, rel_resp_df, permpar, data_col=data_type, highest=highest, 
                ctrl=ctrl, p_val_prefix=True, dry_run=dry_run)
            if not dry_run:
                highest = [val * 1.05 for val in highest] # increment a bit
            

    sess_plot_util.format_linpla_subaxes(ax, fluor=analyspar["fluor"], 
        datatype="roi", sess_ns=sess_ns, kind="reg", xticks=sess_ns, 
        modif_share=False)

    return ax

    
Esempio n. 8
0
def plot_sess_data(data_df, analyspar, sesspar, permpar, figpar, 
                   between_sess_sig=True, data_col="diff_stats", 
                   decoder_data=False, title=None, wide=False):
    """
    plot_sess_data(data_df, analyspar, sesspar, permpar, figpar)

    Plots errorbar data across sessions.

    Required args:
        - data_df (pd.DataFrame):
            dataframe with one row per session/line/plane, and the following 
            columns, in addition to the basic sess_df columns: 
            - {data_key} (list): data stats (me, err)
            - null_CIs (list): adjusted null CI for data
            - raw_p_vals (float): uncorrected p-value for data within 
                sessions
            - p_vals (float): p-value for data within sessions, 
                corrected for multiple comparisons and tails
            for session comparisons, e.g. 1v2:
            - raw_p_vals_{}v{} (float): uncorrected p-value for data 
                differences between sessions 
            - p_vals_{}v{} (float): p-value for data between sessions, 
                corrected for multiple comparisons and tails

        - analyspar (dict): 
            dictionary with keys of AnalysPar namedtuple
        - sesspar (dict):
            dictionary with keys of SessPar namedtuple
        - permpar (dict): 
            dictionary with keys of PermPar namedtuple
        - figpar (dict): 
            dictionary containing the following figure parameter dictionaries
            ["init"] (dict): dictionary with figure initialization parameters
            ["save"] (dict): dictionary with figure saving parameters
            ["dirs"] (dict): dictionary with additional figure parameters

    Optional args:
        - between_sess_sig (bool):
            if True, significance between sessions is logged and plotted
            default: True         
        - data_col (str):
            dataframe column in which data to plot is stored
            default: "diff_stats"
        - decoder_data (bool):
            if True, data plotted is decoder data
            default: False
        - title (str):
            plot title
            default: None
        - wide (bool):
            if True, subplots are wider
            default: False
        
    Returns:
        - ax (2D array): 
            array of subplots
    """

    sess_ns = misc_analys.get_sess_ns(sesspar, data_df)

    figpar = sess_plot_util.fig_init_linpla(figpar)
    
    sharey = True if decoder_data else "row"
    figpar["init"]["sharey"] = sharey
    figpar["init"]["subplot_hei"] = 4.4
    figpar["init"]["gs"] = {"hspace": 0.2}
    if wide:
        figpar["init"]["subplot_wid"] = 3.0
        figpar["init"]["gs"]["wspace"] = 0.3
    else:
        figpar["init"]["subplot_wid"] = 2.6
        figpar["init"]["gs"]["wspace"] = 0.3

    fig, ax = plot_util.init_fig(plot_helper_fcts.N_LINPLA, **figpar["init"])

    if title is not None:
        fig.suptitle(title, y=0.97, weight="bold")

    sensitivity = misc_analys.get_sensitivity(permpar)
    comp_info = misc_analys.get_comp_info(permpar)

    for pass_n in [0, 1]: # add significance markers on the second pass
        if pass_n == 1:
            logger.info(f"{comp_info}:", extra={"spacing": "\n"})
            
        for (line, plane), lp_df in data_df.groupby(["lines", "planes"]):
            li, pl, col, dash = plot_helper_fcts.get_line_plane_idxs(
                line, plane
                )
            line_plane_name = plot_helper_fcts.get_line_plane_name(line, plane)
            sub_ax = ax[pl, li]

            sess_indices = []
            lp_sess_ns = []
            for sess_n in sess_ns:
                rows = lp_df.loc[lp_df["sess_ns"] == sess_n]
                if len(rows) == 1:
                    sess_indices.append(rows.index[0])
                    lp_sess_ns.append(sess_n)
                elif len(rows) > 1:
                    raise RuntimeError("Expected 1 row per line/plane/session.")

            data = np.asarray([lp_df.loc[i, data_col] for i in sess_indices])

            if pass_n == 0:
                # plot errorbars
                plot_util.plot_errorbars(
                    sub_ax, data[:, 0], data[:, 1:].T, lp_sess_ns, color=col, 
                    alpha=0.8, xticks="auto", line_dash=dash
                    )

            if pass_n == 1:
                # plot CIs
                CIs = np.asarray(
                    [lp_df.loc[i, "null_CIs"] for i in sess_indices]
                    )
                CI_meds = CIs[:, 1]
                CIs = CIs[:, np.asarray([0, 2])]

                plot_util.plot_CI(sub_ax, CIs.T, med=CI_meds, x=lp_sess_ns, 
                    width=0.45, color="lightgrey", med_col="gray", med_rat=0.03, 
                    zorder=-12)

                # add significance markers within sessions
                y_maxes = data[:, 0] + data[:, -1]
                sides = [
                    np.sign(sub[0] - CI_med) 
                    for sub, CI_med in zip(data, CI_meds)
                    ]
                p_vals_corr = [lp_df.loc[i, "p_vals"] for i in sess_indices]
                lp_sig_str = f"{line_plane_name:6} (within session):"
                for s, sess_n in enumerate(lp_sess_ns):
                    sig_str = misc_analys.get_sig_symbol(
                        p_vals_corr[s], sensitivity=sensitivity, side=sides[s], 
                        tails=permpar["tails"], p_thresh=permpar["p_val"]
                        )

                    if len(sig_str):
                        plot_util.add_signif_mark(sub_ax, sess_n, y_maxes[s], 
                            rel_y=0.15, color=col, mark=sig_str)  

                    lp_sig_str = (
                        f"{lp_sig_str}{TAB} S{sess_n}: "
                        f"{p_vals_corr[s]:.5f}{sig_str:3}"
                        )

                logger.info(lp_sig_str, extra={"spacing": TAB})
        
    if between_sess_sig:
        add_between_sess_sig(ax, data_df, permpar, data_col=data_col)

    area, ylab = True, None
    if decoder_data:
        area = False
        if "balanced" in data_col:
            ylab = "Balanced accuracy (%)" 
        else:
            ylab = "Accuracy %"

    sess_plot_util.format_linpla_subaxes(ax, fluor=analyspar["fluor"], 
        area=area, ylab=ylab, datatype="roi", sess_ns=sess_ns, kind="reg", 
        xticks=sess_ns, modif_share=False)

    return ax
Esempio n. 9
0
def plot_roi_tracking(roi_mask_df, figpar, title=None):
    """
    plot_roi_tracking(roi_mask_df, figpar)
    
    Plots ROI tracking examples, for different session permutations, and union 
    across permutations.

    Required args:
        - roi_mask_df (pd.DataFrame in dict format):
            dataframe with a row for each mouse, and the following 
            columns, in addition to the basic sess_df columns: 
            - "roi_mask_shapes" (list): shape into which ROI mask indices index 
                (sess x hei x wid)
            - "union_n_conflicts" (int): number of conflicts after union
            for "union", "fewest" and "most" tracked ROIs:
            - "{}_registered_roi_mask_idxs" (list): list of mask indices, 
                registered across sessions, for each session 
                (flattened across ROIs) ((sess, hei, wid) x val),
                ordered by {}_sess_ns if "fewest" or "most"
            - "{}_n_tracked" (int): number of tracked ROIs
            for "fewest", "most" tracked ROIs:
            - "{}_sess_ns" (list): ordered session number 
        - figpar (dict): 
            dictionary containing the following figure parameter dictionaries
            ["init"] (dict): dictionary with figure initialization parameters
            ["save"] (dict): dictionary with figure saving parameters
            ["dirs"] (dict): dictionary with additional figure parameters  

    Optional args:
        - title (str):
            plot title
            default: None

    Returns:
        - ax (2D array): 
            array of subplots
    """

    if len(roi_mask_df) != 1:
        raise ValueError("Expected only one row in roi_mask_df")
    roi_mask_row = roi_mask_df.loc[roi_mask_df.index[0]]

    columns = ["fewest", "most", "", "union"]

    figpar["init"]["ncols"] = len(columns)
    figpar["init"]["sharex"] = False
    figpar["init"]["sharey"] = False
    figpar["init"]["subplot_hei"] = 5.05
    figpar["init"]["subplot_wid"] = 5.05
    figpar["init"]["gs"] = {"wspace": 0.06}

    # MUST ADJUST if anything above changes [right, bottom, width, height]
    new_axis_coords = [0.905, 0.125, 0.06, 0.74] 

    fig, ax = plot_util.init_fig(plot_helper_fcts.N_LINPLA, **figpar["init"])

    sub_ax_scale = fig.add_axes(new_axis_coords)
    plot_util.remove_axis_marks(sub_ax_scale)
    sub_ax_scale.spines["left"].set_visible(True)

    if title is not None:
        fig.suptitle(title, y=1.05, weight="bold")

    sess_cols = get_sess_cols(roi_mask_df)
    alpha = 0.6
    for c, column in enumerate(columns):
        sub_ax = ax[0, c]

        if c == 0:
            lp_col = plot_helper_fcts.get_line_plane_idxs(
                roi_mask_row["lines"], roi_mask_row["planes"]
                )[2]

            lp_name = plot_helper_fcts.get_line_plane_name(
                roi_mask_row["lines"], roi_mask_row["planes"]
                )
            sub_ax.set_ylabel(lp_name, fontweight="bold", color=lp_col)
            log_info = f"Conflicts and matches for a {lp_name} example:"

        if column == "":
            sub_ax.set_axis_off()
            subplot_title = \
                "     Union - conflicts\n...   ====================>"
            sub_ax.set_title(subplot_title, fontweight="bold", y=0.5)
            continue
        else:
            plot_util.remove_axis_marks(sub_ax)
            for spine in ["right", "left", "top", "bottom"]:
                sub_ax.spines[spine].set_visible(True)

        if column in ["fewest", "most"]:
            y = 1.01
            ord_sess_ns = roi_mask_row[f"{column}_sess_ns"]
            ord_sess_ns_str = ", ".join([str(n) for n in ord_sess_ns])

            n_matches = int(roi_mask_row[f"{column}_n_tracked"])
            subplot_title = f"{n_matches} matches\n(sess {ord_sess_ns_str})"
            log_info = (f"{log_info}\n{TAB}"
                f"{column.capitalize()} matches (sess {ord_sess_ns_str}): "
                f"{n_matches}")
        
        elif column == "union":
            y = 1.04
            ord_sess_ns = roi_mask_row["sess_ns"]
            n_union = int(roi_mask_row[f"{column}_n_tracked"])
            n_conflicts = int(roi_mask_row[f"{column}_n_conflicts"])
            n_matches = n_union - n_conflicts

            subplot_title = f"{n_matches} matches"
            log_info = (f"{log_info}\n{TAB}"
                "Union - conflicts: "
                f"{n_union} - {n_conflicts} = {n_matches} matches"
                )

        sub_ax.set_title(subplot_title, fontweight="bold", y=y)

        roi_masks = create_sess_roi_masks(
            roi_mask_row, 
            mask_key=f"{column}_registered_roi_mask_idxs"
            )
        
        for sess_n in roi_mask_row["sess_ns"]:
            col = sess_cols[int(sess_n)]
            s = ord_sess_ns.index(sess_n)
            add_roi_mask(sub_ax, roi_masks[s], col=col, alpha=alpha)

    # add scale marker
    hei_len = roi_mask_row["roi_mask_shapes"][1]
    add_scale_marker(
        sub_ax_scale, side_len=hei_len, ori="vertical", quadrant=3, fontsize=20
        )

    logger.info(log_info, extra={"spacing": "\n"})

    # add legend
    add_sess_col_leg(
        ax[0, columns.index("")], 
        sess_cols, 
        bbox_to_anchor=(0.67, 0.3), 
        alpha=alpha
        )

    return ax
Esempio n. 10
0
def plot_roi_masks_overlayed(roi_mask_df, figpar, title=None):
    """
    plot_roi_masks_overlayed(roi_mask_df, figpar)

    Plots ROI masks overlayed across sessions, optionally cropped.

    Required args:
        - roi_mask_df (pd.DataFrame in dict format):
            dataframe with a row for each mouse, and the following 
            columns, in addition to the basic sess_df columns: 
            - "registered_roi_mask_idxs" (list): list of mask indices, 
                registered across sessions, for each session 
                (flattened across ROIs) ((sess, hei, wid) x val)
            - "roi_mask_shapes" (list): shape into which ROI mask indices index 
                (sess x hei x wid)
            
            and optionally, if cropping:
            - "crop_fact" (num): factor by which to crop masks (> 1) 
            - "shift_prop_hei" (float): proportion by which to shift cropped 
                mask center vertically from left edge [0, 1]
            - "shift_prop_wid" (float): proportion by which to shift cropped 
                mask center horizontally from left edge [0, 1]

        - figpar (dict): 
            dictionary containing the following figure parameter dictionaries
            ["init"] (dict): dictionary with figure initialization parameters
            ["save"] (dict): dictionary with figure saving parameters
            ["dirs"] (dict): dictionary with additional figure parameters  

    Optional args:
        - title (str):
            plot title
            default: None

    Returns:
        - ax (2D array): 
            array of subplots
    """

    crop = "crop_fact" in roi_mask_df.columns

    figpar = sess_plot_util.fig_init_linpla(figpar)

    figpar["init"]["sharex"] = False
    figpar["init"]["sharey"] = False
    figpar["init"]["subplot_hei"] = 5.2
    figpar["init"]["subplot_wid"] = 5.2
    figpar["init"]["gs"] = {"wspace": 0.03, "hspace": 0.32}

    # MUST ADJUST if anything above changes [right, bottom, width, height]
    new_axis_coords = [0.885, 0.11, 0.1, 0.33]
    if crop: # move to the left
        new_axis_coords[0] = 0.04

    fig, ax = plot_util.init_fig(plot_helper_fcts.N_LINPLA, **figpar["init"])

    sub_ax_scale = fig.add_axes(new_axis_coords)
    plot_util.remove_axis_marks(sub_ax_scale)
    sub_ax_scale.spines["left"].set_visible(True)

    if title is not None:
        fig.suptitle(title, y=0.95, weight="bold")

    sess_cols = get_sess_cols(roi_mask_df)
    alpha = 0.6
    hei_lens = []
    for (line, plane), lp_mask_df in roi_mask_df.groupby(["lines", "planes"]):
        li, pl, _, _ = plot_helper_fcts.get_line_plane_idxs(line, plane)
        sub_ax = ax[pl, li]

        if len(lp_mask_df) != 1:
            raise RuntimeError("Expected only one row per line/plane.")
        lp_row = lp_mask_df.loc[lp_mask_df.index[0]]
        
        roi_masks = create_sess_roi_masks(lp_row, crop=crop)
        hei_lens.append(roi_masks.shape[1])

        for s, sess_n in enumerate(lp_row["sess_ns"]):
            col = sess_cols[int(sess_n)]
            add_roi_mask(sub_ax, roi_masks[s], col=col, alpha=alpha)

    # add legend
    add_sess_col_leg(
        ax[0, 1], sess_cols, bbox_to_anchor=(0.7, -0.01), alpha=alpha
        )

    # add scale marker
    hei_lens = np.unique(hei_lens)
    if len(hei_lens) != 1:
        raise NotImplementedError(
            "Adding scale bar not implemented if ROI mask image heights are "
            "different for different planes."
            )
    quadrant = 1 if crop else 3
    add_scale_marker(
        sub_ax_scale, side_len=hei_lens[0], ori="vertical", quadrant=quadrant, 
        fontsize=22
        )
 
    # Add plane, line info to plots
    sess_plot_util.format_linpla_subaxes(ax, ylab="", kind="map")

    return ax
Esempio n. 11
0
def plot_roi_masks_overlayed_with_proj(roi_mask_df, figpar, title=None):
    """
    plot_roi_masks_overlayed_with_proj(roi_mask_df, figpar)

    Plots ROI mask contours overlayed over imaging planes, and ROI masks 
    overlayed over each other across sessions.

    Required args:
        - roi_mask_df (pd.DataFrame in dict format):
            dataframe with a row for each mouse, and the following 
            columns, in addition to the basic sess_df columns: 

            - "max_projections" (list): pixel intensities of maximum projection 
                for the plane (hei x wid)
            - "registered_roi_mask_idxs" (list): list of mask indices, 
                registered across sessions, for each session 
                (flattened across ROIs) ((sess, hei, wid) x val)
            - "roi_mask_idxs" (list): list of mask indices for each session, 
                and each ROI (sess x (ROI, hei, wid) x val) (not registered)
            - "roi_mask_shapes" (list): shape into which ROI mask indices index 
                (sess x hei x wid)

            - "crop_fact" (num): factor by which to crop masks (> 1) 
            - "shift_prop_hei" (float): proportion by which to shift cropped 
                mask center vertically from left edge [0, 1]
            - "shift_prop_wid" (float): proportion by which to shift cropped 
                mask center horizontally from left edge [0, 1]

        - figpar (dict): 
            dictionary containing the following figure parameter dictionaries
            ["init"] (dict): dictionary with figure initialization parameters
            ["save"] (dict): dictionary with figure saving parameters
            ["dirs"] (dict): dictionary with additional figure parameters  

    Optional args:
        - title (str):
            plot title
            default: None

    Returns:
        - ax (2D array): 
            array of subplots
    """

    n_lines = len(roi_mask_df["lines"].unique())
    n_planes = len(roi_mask_df["planes"].unique())

    sess_cols = get_sess_cols(roi_mask_df)
    n_sess = len(sess_cols)
    n_cols = n_sess * n_lines

    figpar = sess_plot_util.fig_init_linpla(figpar)

    figpar["init"]["sharex"] = False
    figpar["init"]["sharey"] = False
    figpar["init"]["subplot_hei"] = 2.3
    figpar["init"]["subplot_wid"] = 2.3
    figpar["init"]["gs"] = {"wspace": 0.2, "hspace": 0.2}
    figpar["init"]["ncols"] = n_cols

    fig, ax = plot_util.init_fig(n_cols * n_planes * 2, **figpar["init"])

    if title is not None:
        fig.suptitle(title, y=0.93, weight="bold")

    crop = "crop_fact" in roi_mask_df.columns

    sess_cols = get_sess_cols(roi_mask_df)
    alpha = 0.6
    raster_zorder = -12

    for (line, plane), lp_mask_df in roi_mask_df.groupby(["lines", "planes"]):
        li, pl, _, _ = plot_helper_fcts.get_line_plane_idxs(line, plane)
        lp_col = plot_helper_fcts.get_line_plane_idxs(line, plane)[2]
        lp_name = plot_helper_fcts.get_line_plane_name(line, plane)

        if len(lp_mask_df) != 1:
            raise RuntimeError("Expected only one row per line/plane.")
        lp_row = lp_mask_df.loc[lp_mask_df.index[0]]

        # identify subplots
        base_row = (pl % n_planes) * n_planes
        base_col = (li % n_lines) * n_lines

        ax_grp = ax[base_row : base_row + 2, base_col : base_col + n_sess + 1]

        # add imaging planes and masks
        imaging_planes = add_proj_and_roi_masks(
            ax_grp, lp_row, sess_cols, crop=crop, alpha=alpha, 
            proj_zorder=raster_zorder - 1
            )

        # add markings
        shared_row = base_row + 1
        shared_col = base_col + int((n_sess - 1) // 2)
        shared_sub_ax = ax[shared_row, shared_col]

        if shared_col == 0:
            shared_sub_ax.set_ylabel(lp_name, fontweight="bold", color=lp_col)
        else:
            lp_sub_ax = ax[shared_row, 0]
            lp_sub_ax.set_xlim([0, 1])
            lp_sub_ax.set_ylim([0, 1])
            lp_sub_ax.text(
                0.5, 0.5, lp_name, fontweight="bold", color=lp_col, 
                ha="center", va="center", fontsize="x-large"
                )

        # add scale bar
        if n_sess < 2:
            raise NotImplementedError(
                "Scale bar placement not implemented for fewer than 2 "
                "sessions."
                )
        scale_ax = ax[shared_row, -1]
        wid_len = imaging_planes[0].shape[-1]
        add_scale_marker(
            scale_ax, side_len=wid_len, ori="horizontal", quadrant=1, 
            fontsize=20
            )

    logger.info("Rasterizing imaging plane images...", extra={"spacing": TAB})
    for i in range(ax.shape[0]):
        for j in range(ax.shape[1]):
            sub_ax = ax[i, j]
            plot_util.remove_axis_marks(sub_ax)
            if not(i % 2):
                sub_ax.set_rasterization_zorder(raster_zorder)

    # add legend
    if n_sess < 2:
        raise NotImplementedError(
            "Legend placement not implemented for fewer than 2 sessions."
            )
    add_sess_col_leg(
        ax[-1, -1], sess_cols, bbox_to_anchor=(1, 0.6), alpha=alpha, 
        fontsize="small"
        )

    return ax
Esempio n. 12
0
def plot_imaging_planes(imaging_plane_df, figpar, title=None):
    """
    plot_imaging_planes(imaging_plane_df, figpar)
    
    Plots imaging planes.

    Required args:
        - imaging_plane_df (pd.DataFrame in dict format):
            dataframe with a row for each mouse, and the following 
            columns, in addition to the basic sess_df columns: 
            - "max_projections" (list): pixel intensities of maximum projection 
                for the plane (hei x wid)
        - figpar (dict): 
            dictionary containing the following figure parameter dictionaries
            ["init"] (dict): dictionary with figure initialization parameters
            ["save"] (dict): dictionary with figure saving parameters
            ["dirs"] (dict): dictionary with additional figure parameters  

    Optional args:
        - title (str):
            plot title
            default: None

    Returns:
        - ax (2D array): 
            array of subplots
    """

    figpar = sess_plot_util.fig_init_linpla(figpar)

    figpar["init"]["sharex"] = False
    figpar["init"]["sharey"] = False
    figpar["init"]["subplot_hei"] = 2.4
    figpar["init"]["subplot_wid"] = 2.4
    figpar["init"]["gs"] = {"wspace": 0.25, "hspace": 0.2}

    # MUST ADJUST if anything above changes [right, bottom, width, height]
    new_axis_coords = [0.91, 0.115, 0.15, 0.34]

    fig, ax = plot_util.init_fig(plot_helper_fcts.N_LINPLA, **figpar["init"])

    sub_ax_scale = fig.add_axes(new_axis_coords)
    plot_util.remove_axis_marks(sub_ax_scale)
    sub_ax_scale.spines["left"].set_visible(True)

    if title is not None:
        fig.suptitle(title, y=1, weight="bold")

    hei_lens = []
    raster_zorder = -12
    for (line, plane), lp_mask_df in imaging_plane_df.groupby(["lines", "planes"]):
        li, pl, _, _ = plot_helper_fcts.get_line_plane_idxs(line, plane)
        sub_ax = ax[pl, li]

        if len(lp_mask_df) != 1:
            raise RuntimeError("Expected only one row per line/plane.")
        lp_row = lp_mask_df.loc[lp_mask_df.index[0]]
        
        # add projection
        imaging_plane = np.asarray(lp_row["max_projections"])
        hei_lens.append(imaging_plane.shape[0])
        add_imaging_plane(sub_ax, imaging_plane, alpha=0.98, 
            zorder=raster_zorder - 1
            )
        
    # add scale marker
    hei_lens = np.unique(hei_lens)
    if len(hei_lens) != 1:
        raise NotImplementedError(
            "Adding scale bar not implemented if ROI mask image heights are "
            "different for different planes."
            )
    add_scale_marker(
        sub_ax_scale, side_len=hei_lens[0], ori="vertical", quadrant=3, 
        fontsize=16
        )
 
    logger.info("Rasterizing imaging plane images...", extra={"spacing": TAB})
    for sub_ax in ax.reshape(-1):
        sub_ax.set_rasterization_zorder(raster_zorder)

    # Add plane, line info to plots
    sess_plot_util.format_linpla_subaxes(ax, ylab="", kind="map")
    for sub_ax in ax.reshape(-1):
        plot_util.remove_axis_marks(sub_ax)

    return ax
def plot_pup_diff_corr(analyspar, sesspar, stimpar, extrapar, 
                       sess_info, corr_data, figpar=None, savedir=None):
    """
    plot_pup_diff_corr(analyspar, sesspar, stimpar, extrapar, 
                       sess_info, corr_data)

    From dictionaries, plots correlation between unexpected-locked changes in 
    pupil diameter and running or ROI data for each session.

    Required args:
        - analyspar (dict)    : dictionary with keys of AnalysPar namedtuple
        - sesspar (dict)      : dictionary with keys of SessPar namedtuple 
        - stimpar (dict)      : dictionary with keys of StimPar namedtuple
        - extrapar (dict)     : dictionary containing additional analysis 
                                parameters
            ["analysis"] (str): analysis type (e.g., "c")
            ["datatype"] (str): datatype (e.g., "run", "roi")
        - sess_info (dict)    : dictionary containing information from each
                                session 
            ["mouse_ns"] (list)   : mouse numbers
            ["sess_ns"] (list)    : session numbers  
            ["lines"] (list)      : mouse lines
            ["planes"] (list)     : imaging planes
            ["nrois"] (list)      : number of ROIs in session
        - corr_data (dict)    : dictionary containing data to plot:
            ["corrs"] (list): list of correlation values between pupil and 
                              running or ROI differences for each session
            ["diffs"] (list): list of differences for each session, structured
                                  as [pupil, ROI/run] x trials x frames
    
    Optional args:
        - figpar (dict) : dictionary containing the following figure parameter 
                          dictionaries
                          default: None
            ["init"] (dict): dictionary with figure initialization parameters
            ["save"] (dict): dictionary with figure saving parameters
            ["dirs"] (dict): dictionary with additional figure parameters
        - savedir (Path): path of directory in which to save plots.
                          default: None
    
    Returns:
        - fulldir (Path): final path of the directory in which the figure is 
                          saved (may differ from input savedir, if datetime 
                          subfolder is added.)
        - savename (str): name under which the figure is saved
    """
    statstr_pr = sess_str_util.stat_par_str(
        analyspar["stats"], analyspar["error"], "print")
    stimstr_pr = sess_str_util.stim_par_str(
        stimpar["stimtype"], stimpar["visflow_dir"], stimpar["visflow_size"], 
        stimpar["gabk"], "print")
    dendstr_pr = sess_str_util.dend_par_str(
        analyspar["dend"], sesspar["plane"], extrapar["datatype"], "print")

    sessstr = sess_str_util.sess_par_str(
        sesspar["sess_n"], stimpar["stimtype"], sesspar["plane"], 
        stimpar["visflow_dir"],stimpar["visflow_size"], stimpar["gabk"]) 
    dendstr = sess_str_util.dend_par_str(
        analyspar["dend"], sesspar["plane"], extrapar["datatype"])
    
    datatype = extrapar["datatype"]
    datastr = sess_str_util.datatype_par_str(datatype)

    if datatype == "roi":
        label_str = sess_str_util.fluor_par_str(
            analyspar["fluor"], str_type="print")
        full_label_str = u"{}, {} across ROIs".format(
            label_str, analyspar["stats"])
    elif datatype == "run":
        label_str = datastr
        full_label_str = datastr
    
    lab_app = (f" ({analyspar['stats']} over "
        f"{stimpar['pre']}/{stimpar['post']} sec)")

    logger.info(f"Plotting pupil vs {datastr} changes.")
    
    delta = "\u0394"

    # extract some info from sess_info
    keys = ["mouse_ns", "sess_ns", "lines", "planes"]
    [mouse_ns, sess_ns, lines, planes] = [sess_info[key] for key in keys]

    n_sess = len(mouse_ns)
    nroi_strs = sess_str_util.get_nroi_strs(
        sess_info, empty=(datatype!="roi"), style="comma"
        ) 

    if figpar is None:
        figpar = sess_plot_util.init_figpar()

    figpar = copy.deepcopy(figpar)
    if figpar["save"]["use_dt"] is None:
        figpar["save"]["use_dt"] = gen_util.create_time_str()
    figpar["init"]["sharex"] = False
    figpar["init"]["sharey"] = False
    figpar["init"]["ncols"] = n_sess
    
    fig, ax = plot_util.init_fig(2 * n_sess, **figpar["init"])
    suptitle = (f"Relationship between pupil diam. and {datastr} changes, "
        "locked to unexpected events")
    
    for i, sess_diffs in enumerate(corr_data["diffs"]):
        sub_axs = ax[:, i]
        title = (f"Mouse {mouse_ns[i]} - {stimstr_pr}, " + 
            u"{}".format(statstr_pr) + f"\n(sess {sess_ns[i]}, {lines[i]} "
            f"{planes[i]}{dendstr_pr}{nroi_strs[i]})")
        
        # top plot: correlations
        corr = f"Corr = {corr_data['corrs'][i]:.2f}"
        sub_axs[0].plot(
            sess_diffs[0], sess_diffs[1], marker=".", linestyle="None", 
            label=corr)
        sub_axs[0].set_title(title, y=1.01)
        sub_axs[0].set_xlabel(u"{} pupil diam.{}".format(delta, lab_app))
        if i == 0:
            sub_axs[0].set_ylabel(u"{} {}\n{}".format(
                delta, full_label_str, lab_app))
        sub_axs[0].legend()
        
        # bottom plot: differences across occurrences
        data_lab = u"{} {}".format(delta, label_str)   
        pup_lab = u"{} pupil diam.".format(delta)
        cols = []
        scaled = []
        for d, lab in enumerate([pup_lab, data_lab]):
            scaled.append(math_util.scale_data(
                np.asarray(sess_diffs[d]), sc_type="min_max")[0])
            art, = sub_axs[1].plot(scaled[-1], marker=".")
            cols.append(sub_axs[-1].lines[-1].get_color())
            if i == n_sess - 1: # only for last graph
                art.set_label(lab)
                sub_axs[1].legend()
        sub_axs[1].set_xlabel("Unexpected event occurrence")
        if i == 0:
            sub_axs[1].set_ylabel(
                u"{} response locked\nto unexpected onset (scaled)".format(delta))
        # shade area between lines
        plot_util.plot_btw_traces(
            sub_axs[1], scaled[0], scaled[1], color=cols, alpha=0.4)

    fig.suptitle(suptitle, fontsize="xx-large", y=1)

    if savedir is None:
        savedir = Path(
            figpar["dirs"][datatype],
            figpar["dirs"]["pupil"])

    savename = f"{datatype}_diff_corr_{sessstr}{dendstr}"

    fulldir = plot_util.savefig(fig, savename, savedir, **figpar["save"])

    return fulldir, savename                              
def plot_pup_roi_stim_corr(analyspar, sesspar, stimpar, extrapar, 
                           sess_info, corr_data, figpar=None, savedir=None):
    """
    plot_pup_roi_stim_corr(analyspar, sesspar, stimpar, extrapar, 
                           sess_info, corr_data)

    From dictionaries, plots correlation between unexpected-locked changes in 
    pupil diameter and each ROI, for gabors versus visual flow responses for 
    each session.
    
    Required args:
        - analyspar (dict)    : dictionary with keys of AnalysPar namedtuple
        - sesspar (dict)      : dictionary with keys of SessPar namedtuple 
        - stimpar (dict)      : dictionary with keys of StimPar namedtuple
        - extrapar (dict)     : dictionary containing additional analysis 
                                parameters
            ["analysis"] (str): analysis type (e.g., "r")
            ["datatype"] (str): datatype (e.g., "roi")
        - sess_info (dict)    : dictionary containing information from each
                                session 
            ["mouse_ns"] (list)   : mouse numbers
            ["sess_ns"] (list)    : session numbers  
            ["lines"] (list)      : mouse lines
            ["planes"] (list)     : imaging planes
            ["nrois"] (list)      : number of ROIs in session
        - corr_data (dict)    : dictionary containing data to plot:
            ["stim_order"] (list): ordered list of stimtypes
            ["roi_corrs"] (list) : nested list of correlations between pupil 
                                   and ROI responses changes locked to 
                                   unexpected, structured as 
                                       session x stimtype x ROI
            ["corrs"] (list)     : list of correlation between stimtype
                                   correlations for each session
    
    Optional args:
        - figpar (dict) : dictionary containing the following figure parameter 
                          dictionaries
                          default: None
            ["init"] (dict): dictionary with figure initialization parameters
            ["save"] (dict): dictionary with figure saving parameters
            ["dirs"] (dict): dictionary with additional figure parameters
        - savedir (Path): path of directory in which to save plots.
                          default: None
    
    Returns:
        - fulldir (Path): final path of the directory in which the figure is 
                          saved (may differ from input savedir, if datetime 
                          subfolder is added.)
        - savename (str): name under which the figure is saved
    """

    stimstr_prs = []
    for stimtype in corr_data["stim_order"]:
        stimstr_pr = sess_str_util.stim_par_str(
            stimtype, stimpar["visflow_dir"], stimpar["visflow_size"], 
            stimpar["gabk"], "print")
        stimstr_pr = stimstr_pr[:-1] if stimstr_pr[-1] == "s" else stimstr_pr
        stimstr_prs.append(stimstr_pr)
        
    dendstr_pr = sess_str_util.dend_par_str(
        analyspar["dend"], sesspar["plane"], extrapar["datatype"], "print")

    sessstr = f"sess{sesspar['sess_n']}_{sesspar['plane']}" 
    dendstr = sess_str_util.dend_par_str(
        analyspar["dend"], sesspar["plane"], extrapar["datatype"])

    label_str = sess_str_util.fluor_par_str(
        analyspar["fluor"], str_type="print")
    lab_app = (f" ({analyspar['stats']} over "
        f"{stimpar['pre']}/{stimpar['post']} sec)")

    logger.info("Plotting pupil-ROI difference correlations for "
        "{} vs {}.".format(*corr_data["stim_order"]))

    # extract some info from sess_info
    keys = ["mouse_ns", "sess_ns", "lines", "planes"]
    [mouse_ns, sess_ns, lines, planes] = [sess_info[key] for key in keys]

    n_sess = len(mouse_ns)
    nroi_strs = sess_str_util.get_nroi_strs(sess_info, style="comma")

    if figpar is None:
        figpar = sess_plot_util.init_figpar()

    figpar = copy.deepcopy(figpar)
    if figpar["save"]["use_dt"] is None:
        figpar["save"]["use_dt"] = gen_util.create_time_str()
    figpar["init"]["sharex"] = True
    figpar["init"]["sharey"] = True
    
    fig, ax = plot_util.init_fig(n_sess, **figpar["init"])
    suptitle = (u"Relationship between pupil diam. and {} changes, locked to "
        "unexpected events\n{} for each ROI ({} vs {})".format(
            label_str, lab_app, *corr_data["stim_order"]))
    
    for i, sess_roi_corrs in enumerate(corr_data["roi_corrs"]):
        sub_ax = plot_util.get_subax(ax, i)
        title = (f"Mouse {mouse_ns[i]} (sess {sess_ns[i]}, {lines[i]} "
            f"{planes[i]}{dendstr_pr}{nroi_strs[i]})")
        
        # top plot: correlations
        corr = f"Corr = {corr_data['corrs'][i]:.2f}"
        sub_ax.plot(
            sess_roi_corrs[0], sess_roi_corrs[1], marker=".", linestyle="None", 
            label=corr)
        sub_ax.set_title(title, y=1.01)
        if sub_ax.is_last_row():
            sub_ax.set_xlabel(f"{stimstr_prs[0].capitalize()} correlations")
        if sub_ax.is_first_col():
            sub_ax.set_ylabel(f"{stimstr_prs[1].capitalize()} correlations")
        sub_ax.legend()

    plot_util.turn_off_extra(ax, n_sess)

    fig.suptitle(suptitle, fontsize="xx-large", y=1)

    if savedir is None:
        savedir = Path(
            figpar["dirs"]["roi"],
            figpar["dirs"]["pupil"])

    savename = f"roi_diff_corrbyroi_{sessstr}{dendstr}"

    fulldir = plot_util.savefig(fig, savename, savedir, **figpar["save"])

    return fulldir, savename                           
Esempio n. 15
0
def plot_idx_corr_scatterplots(idx_corr_df,
                               permpar,
                               figpar,
                               permute="sess",
                               title=None):
    """
    plot_idx_corr_scatterplots(idx_corr_df, permpar, figpar)

    Plots ROI USI index correlation scatterplots for individual session 
    comparisons.

    Required args:
        - idx_corr_df (pd.DataFrame):
            dataframe with one row per line/plane, and the 
            following columns, in addition to the basic sess_df columns:

            for correlation data (normalized if corr_type is "diff_corr") for 
            session comparisons (x, y), e.g. 1v2
            - binned_rand_stats (list): number of random correlation values per 
                bin (xs x ys)
            - corr_data_xs (list): USI values for x
            - corr_data_ys (list): USI values for y
            - corrs (float): correlation between session data (x and y)
            - p_vals (float): p-value for correlation, corrected for 
                multiple comparisons and tails
            - rand_corr_meds (float): median of the random correlations
            - raw_p_vals (float): p-value for intersession correlations
            - regr_coefs (float): regression correlation coefficient (slope)
            - regr_intercepts (float): regression correlation intercept
            - x_bin_mids (list): x mid point for each random correlation bin
            - y_bin_mids (list): y mid point for each random correlation bin

        - permpar (dict): 
            dictionary with keys of PermPar namedtuple
        - figpar (dict): 
            dictionary containing the following figure parameter dictionaries
            ["init"] (dict): dictionary with figure initialization parameters
            ["save"] (dict): dictionary with figure saving parameters
            ["dirs"] (dict): dictionary with additional figure parameters

    Optional args:
        - permute (bool):
            type of permutation to due ("tracking", "sess" or "all")
            default: "sess"
        - title (str):
            plot title
            default: None

    Returns:
        - ax (2D array): 
            array of subplots
    """

    diffs = False
    if permute in ["sess", "all"]:
        diffs = True

    figpar = sess_plot_util.fig_init_linpla(figpar, kind="reg")

    figpar["init"]["sharex"] = False
    figpar["init"]["sharey"] = False
    figpar["init"]["subplot_hei"] = 4
    figpar["init"]["subplot_wid"] = 4
    figpar["init"]["gs"] = {"hspace": 0.4, "wspace": 0.4}

    fig, ax = plot_util.init_fig(4, **figpar["init"])

    if title is not None:
        fig.suptitle(title, fontweight="bold", y=0.97)

    sess_ns = None

    # first pass to plot
    for (line, plane), lp_df in idx_corr_df.groupby(["lines", "planes"]):
        li, pl, col, _ = plot_helper_fcts.get_line_plane_idxs(line, plane)
        sub_ax = ax[pl, li]

        if len(lp_df) != 1:
            raise RuntimeError("Expected exactly one row.")
        lp_row = lp_df.loc[lp_df.index[0]]

        if sess_ns is None:
            sess_ns = lp_row["sess_ns"]
            xlabel = f"Session {sess_ns[0]} USIs"
            ylabel = f"Session {sess_ns[1]} USIs"
            if diffs:
                ylabel = f"Session {sess_ns[1]} - {sess_ns[0]} USIs"

        elif sess_ns != lp_row["sess_ns"]:
            raise RuntimeError("Expected all sess_ns to match.")

        density_data = [
            lp_row["x_bin_mids"], lp_row["y_bin_mids"],
            np.asarray(lp_row["binned_rand_stats"]).T
        ]
        sub_ax.contour(*density_data,
                       levels=6,
                       cmap="Greys",
                       zorder=-13,
                       linewidths=4)

        alpha = 0.3**(len(lp_row["corr_data_xs"]) / 300)
        sub_ax.scatter(lp_row["corr_data_xs"],
                       lp_row["corr_data_ys"],
                       color=col,
                       alpha=alpha,
                       lw=2,
                       s=35)

    # Add plane, line info to plots
    sess_plot_util.format_linpla_subaxes(ax,
                                         datatype="roi",
                                         xticks=None,
                                         ylab=ylabel,
                                         xlab=xlabel,
                                         kind="reg")

    # second pass to add plot markings
    comp_info = misc_analys.get_comp_info(permpar)

    logger.info(f"{comp_info}:", extra={"spacing": "\n"})
    sig_str = ""
    for (line, plane), lp_df in idx_corr_df.groupby(["lines", "planes"]):
        li, pl, col, _ = plot_helper_fcts.get_line_plane_idxs(line, plane)
        line_plane_name = plot_helper_fcts.get_line_plane_name(line, plane)
        sub_ax = ax[pl, li]

        # add markers back in (removed due to kind='reg')
        sub_ax.tick_params(axis="x", which="both", bottom=True, top=False)
        sub_ax.spines["bottom"].set_visible(True)

        lp_row = lp_df.loc[lp_df.index[0]]

        p_val_corr = lp_row["p_vals"]
        lp_sig_str = add_scatterplot_markers(sub_ax,
                                             permpar,
                                             lp_row["corrs"],
                                             lp_row["rand_corr_meds"],
                                             lp_row["regr_coefs"],
                                             lp_row["regr_intercepts"],
                                             lp_row["p_vals"],
                                             col=col,
                                             diffs=diffs)

        sig_str = (f"{sig_str}{TAB}{line_plane_name}: "
                   f"{p_val_corr:.5f}{lp_sig_str:3}")

    logger.info(sig_str, extra={"spacing": TAB})

    return ax
Esempio n. 16
0
def plot_roi_correlations(corr_df, figpar, title=None, log_scale=True):
    """
    plot_roi_correlations(corr_df, figpar)

    Plots correlation histograms.

    Required args:
        - corr_df (pd.DataFrame):
            dataframe with one row per session/line/plane, and the 
            following columns, in addition to the basic sess_df columns:
            - bin_edges (list): first and last bin edge
            - corrs_binned (list): number of correlation values per bin
        - figpar (dict): 
            dictionary containing the following figure parameter dictionaries
            ["init"] (dict): dictionary with figure initialization parameters
            ["save"] (dict): dictionary with figure saving parameters
            ["dirs"] (dict): dictionary with additional figure parameters

    Optional args:
        - title (str):
            plot title
            default: None
        - log_scale (bool):
            if True, a near logarithmic scale is used for the y axis (with a 
            linear range to reach 0, and break marks to mark the transition 
            from linear to log range)
            default: True

    Returns:
        - ax (2D array): 
            array of subplots
    """

    sess_ns = np.arange(corr_df.sess_ns.min(), corr_df.sess_ns.max() + 1)
    n_sess = len(sess_ns)

    figpar = sess_plot_util.fig_init_linpla(figpar,
                                            kind="prog",
                                            n_sub=len(sess_ns))
    figpar["init"]["subplot_hei"] = 3.0
    figpar["init"]["subplot_wid"] = 2.8
    figpar["init"]["sharex"] = log_scale
    if log_scale:
        figpar["init"]["sharey"] = True

    fig, ax = plot_util.init_fig(4 * len(sess_ns), **figpar["init"])
    if title is not None:
        fig.suptitle(title, y=1.02, weight="bold")

    sess_plot_util.format_linpla_subaxes(ax,
                                         datatype="roi",
                                         ylab="Density",
                                         xlab="Correlation",
                                         sess_ns=sess_ns,
                                         kind="prog",
                                         single_lab=True)

    log_base = 2
    for (line, plane), lp_df in corr_df.groupby(["lines", "planes"]):
        li, pl, col, _ = plot_helper_fcts.get_line_plane_idxs(line, plane)
        for s, sess_n in enumerate(sess_ns):
            sess_rows = lp_df.loc[lp_df["sess_ns"] == sess_n]
            if len(sess_rows) == 0:
                continue
            elif len(sess_rows) > 1:
                raise RuntimeError("Expected exactly one row.")
            sess_row = sess_rows.loc[sess_rows.index[0]]

            sub_ax = ax[pl, s + li * n_sess]

            weights = np.asarray(sess_row["corrs_binned"])

            bin_edges = np.linspace(*sess_row["bin_edges"], len(weights) + 1)

            sub_ax.hist(bin_edges[:-1],
                        bin_edges,
                        weights=weights,
                        color=col,
                        alpha=0.6,
                        density=True)
            sub_ax.axvline(0,
                           ls=plot_helper_fcts.VDASH,
                           c="k",
                           lw=3.0,
                           alpha=0.5)

            sub_ax.spines["bottom"].set_visible(True)
            sub_ax.tick_params(axis="x", which="both", bottom=True, top=False)

            if log_scale:
                sub_ax.set_yscale("log", base=log_base)
                sub_ax.set_xlim(-1, 1)
            else:
                sub_ax.autoscale(axis="x", tight=True)

            sub_ax.autoscale(axis="y", tight=True)

    if log_scale:  # update x ticks
        set_symlog_scale(ax, log_base=log_base, col_per_grp=n_sess, n_ticks=4)

    else:  # update x and y ticks
        for i in range(ax.shape[0]):
            for j in range(int(ax.shape[1] / n_sess)):
                sub_axes = ax[i, j * n_sess:(j + 1) * n_sess]
                plot_util.set_interm_ticks(sub_axes,
                                           4,
                                           axis="y",
                                           share=True,
                                           update_ticks=True)

    plot_util.set_interm_ticks(ax,
                               4,
                               axis="x",
                               share=log_scale,
                               update_ticks=True,
                               fontweight="bold")

    return ax
Esempio n. 17
0
def plot_sess_traces(data_df, analyspar, sesspar, figpar, 
                     trace_col="trace_stats", row_col="sess_ns", 
                     row_order=None, split="by_exp", title=None, size="reg"):
    """
    plot_sess_traces(data_df, analyspar, sesspar, figpar) 
    
    Plots traces from dataframe.

    Required args:
        - data_df (pd.DataFrame):
            traces data frame with, in addition to the basic sess_df columns, 
            columns specified by trace_col, row_col, and a "time_values" column
        - analyspar (dict): 
            dictionary with keys of AnalysPar namedtuple
        - sesspar (dict):
            dictionary with keys of SessPar namedtuple
        - figpar (dict): 
            dictionary containing the following figure parameter dictionaries
            ["init"] (dict): dictionary with figure initialization parameters
            ["save"] (dict): dictionary with figure saving parameters
            ["dirs"] (dict): dictionary with additional figure parameters

    Optional args:
        - trace_col (str):
             dataframe column containing trace statistics, as 
             split x ROIs x frames x stats 
             default: "trace_stats"
        - row_col (str):
            dataframe column specifying the variable that defines rows 
            within each line/plane
            default: "sess_ns"
        - row_order (list):
            ordered list specifying the order in which to plot from row_col.
            If None, automatic sorting order is used.
            default: None 
        - split (str):
            data split, e.g. "exp_lock", "unexp_lock", "stim_onset" or 
            "stim_offset"
            default: False
        - title (str):
            plot title
            default: None
        - size (str):
            subplot sizes
            default: "reg"

    Returns:
        - ax (2D array): 
            array of subplots
    """
    
    # retrieve session numbers, and infer row_order, if necessary
    sess_ns = None
    if row_col == "sess_ns":
        sess_ns = row_order
        if row_order is None:
            row_order = misc_analys.get_sess_ns(sesspar, data_df)

    elif row_order is None:
        row_order = data_df[row_col].unique()

    figpar = sess_plot_util.fig_init_linpla(
        figpar, kind="traces", n_sub=len(row_order), sharey=False
        )

    if size == "small":
        figpar["init"]["subplot_hei"] = 1.51
        figpar["init"]["subplot_wid"] = 3.7
    elif size == "wide":
        figpar["init"]["subplot_hei"] = 1.36
        figpar["init"]["subplot_wid"] = 4.8
        figpar["init"]["gs"] = {"wspace": 0.3, "hspace": 0.5}
    elif size == "reg":
        figpar["init"]["subplot_hei"] = 1.36
        figpar["init"]["subplot_wid"] = 3.4
    else:
        gen_util.accepted_values_error("size", size, ["small", "wide", "reg"])

    fig, ax = plot_util.init_fig(len(row_order) * 4, **figpar["init"])
    if title is not None:
        fig.suptitle(title, y=1.0, weight="bold")

    for (line, plane), lp_df in data_df.groupby(["lines", "planes"]):
        li, pl, col, dash = plot_helper_fcts.get_line_plane_idxs(line, plane)

        for r, row_val in enumerate(row_order):
            rows = lp_df.loc[lp_df[row_col] == row_val]
            if len(rows) == 0:
                continue
            elif len(rows) > 1:
                raise RuntimeError(
                    "Expected row_order instances to be unique per line/plane."
                    )
            row = rows.loc[rows.index[0]]

            sub_ax = ax[r + pl * len(row_order), li]

            if line == "L2/3-Cux2":
                exp_col = "darkgray" # oddly, lighter than gray
            else:
                exp_col = "gray"

            plot_traces(
                sub_ax, row["time_values"], row[trace_col], split=split, 
                col=col, ls=dash, exp_col=exp_col, lab=False
                )
            
    for sub_ax in ax.reshape(-1):
        plot_util.set_minimal_ticks(sub_ax, axis="y")

    sess_plot_util.format_linpla_subaxes(ax, fluor=analyspar["fluor"], 
        area=False, datatype="roi", sess_ns=sess_ns, xticks=None, 
        kind="traces", modif_share=False)

   # fix x ticks and lims
    plot_util.set_interm_ticks(ax, 3, axis="x", fontweight="bold")
    xlims = [np.min(row["time_values"]), np.max(row["time_values"])]
    if split != "by_exp":
        xlims = [-xlims[1], xlims[1]]
    sub_ax.set_xlim(xlims)

    return ax
Esempio n. 18
0
def plot_snr_sigmeans_nrois(data_df,
                            figpar,
                            datatype="snrs",
                            title="ROI SNRs"):
    """
    plot_snr_sigmeans_nrois(data_df, figpar)

    Plots SNR, signal means or number of ROIs, depending on the case.

    Required args:
        - data_df (pd.DataFrame):
            dataframe with SNR, signal mean or number of ROIs data for each 
            session, in addition to the basic sess_df columns
        - figpar (dict): 
            dictionary containing the following figure parameter dictionaries
            ["init"] (dict): dictionary with figure initialization parameters
            ["save"] (dict): dictionary with figure saving parameters
            ["dirs"] (dict): dictionary with additional figure parameters

    Optional args:
        - datatype (str):
            type of data to plot, also corresponding to column name
            default: "snrs"
        - title (str):
            plot title
            default: "ROI SNRs"

    Returns:
        - ax (2D array): 
            array of subplots
    """

    sess_ns = np.arange(data_df.sess_ns.min(), data_df.sess_ns.max() + 1)

    figpar = sess_plot_util.fig_init_linpla(figpar, kind="reg")
    figpar["init"]["sharey"] = "row"

    figpar["init"]["subplot_hei"] = 4.4
    figpar["init"]["gs"] = {"wspace": 0.2, "hspace": 0.2}
    if datatype != "nrois":
        figpar["init"]["subplot_wid"] = 3.2
    else:
        figpar["init"]["subplot_wid"] = 2.5

    fig, ax = plot_util.init_fig(4, **figpar["init"])
    if title is not None:
        fig.suptitle(title, y=0.97, weight="bold")

    for (line, plane), lp_df in data_df.groupby(["lines", "planes"]):
        li, pl, col, dash = plot_helper_fcts.get_line_plane_idxs(line, plane)

        sub_ax = ax[pl, li]

        if datatype == "snrs":
            sub_ax.axhline(y=1,
                           ls=plot_helper_fcts.HDASH,
                           c="k",
                           lw=3.0,
                           alpha=0.5)
        elif datatype == "signal_means":
            sub_ax.axhline(y=0,
                           ls=plot_helper_fcts.HDASH,
                           c="k",
                           lw=3.0,
                           alpha=0.5)
        elif datatype != "nrois":
            gen_util.accepted_values_error("datatype", datatype,
                                           ["snrs", "signal_means", "nrois"])

        if datatype == "nrois":
            plot_nrois(sub_ax, lp_df, sess_ns=sess_ns, col=col, dash=dash)
            continue

        data = []
        use_sess_ns = []
        for sess_n in sess_ns:
            rows = lp_df.loc[lp_df["sess_ns"] == sess_n]
            if len(rows) > 0:
                use_sess_ns.append(sess_n)
                data.append(np.concatenate(rows[datatype].tolist()))

        sub_ax.boxplot(data,
                       positions=use_sess_ns,
                       notch=True,
                       patch_artist=True,
                       whis=[5, 95],
                       widths=0.6,
                       boxprops=dict(facecolor="white",
                                     color=col,
                                     linewidth=3.0),
                       capprops=dict(color=col, linewidth=3.0),
                       whiskerprops=dict(color=col, linewidth=3.0),
                       flierprops=dict(color=col,
                                       markeredgecolor=col,
                                       markersize=8),
                       medianprops=dict(color=col, linewidth=3.0))

    sess_plot_util.format_linpla_subaxes(ax,
                                         datatype="roi",
                                         ylab="",
                                         xticks=sess_ns,
                                         kind="reg",
                                         single_lab=True)

    return ax
Esempio n. 19
0
def plot_ex_gabor_traces(ex_traces_df, stimpar, figpar, title=None):
    """
    plot_ex_gabor_traces(ex_traces_df, stimpar, figpar)

    Plots example Gabor traces.

    Required args:
        - ex_traces_df (pd.DataFrame):
            dataframe with a row for each ROI, and the following columns, 
            in addition to the basic sess_df columns: 
            - time_values (list): values for each frame, in seconds
            - roi_ns (list): selected ROI number
             - traces_sm (list): selected ROI sequence traces, smoothed, with 
                dims: seq x frames
            - trace_stat (list): selected ROI trace mean or median
        - stimpar (dict):
            dictionary with keys of StimPar namedtuple
        - figpar (dict): 
            dictionary containing the following figure parameter dictionaries
            ["init"] (dict): dictionary with figure initialization parameters
            ["save"] (dict): dictionary with figure saving parameters
            ["dirs"] (dict): dictionary with additional figure parameters
    
    Optional args:
        - title (str):
            plot title
            default: None

    Returns:
        - ax (2D array): 
            array of subplots
    """

    if stimpar["stimtype"] != "gabors":
        raise ValueError("Expected stimpar['stimtype'] to be 'gabors'.")

    group_columns = ["lines", "planes"]
    n_per = np.max(
        [len(lp_df) for _, lp_df in ex_traces_df.groupby(group_columns)]
        )
    per_rows, per_cols = math_util.get_near_square_divisors(n_per)
    n_per = per_rows * per_cols

    figpar = sess_plot_util.fig_init_linpla(
        figpar, kind="traces", n_sub=per_rows
        )
    figpar["init"]["subplot_hei"] = 1.36
    figpar["init"]["subplot_wid"] = 2.47
    figpar["init"]["ncols"] = per_cols * 2

    fig, ax = plot_util.init_fig(
        plot_helper_fcts.N_LINPLA * n_per, **figpar["init"]
        )
    if title is not None:
        fig.suptitle(title, y=1.03, weight="bold")

    ylims = np.full(ax.shape + (2, ), np.nan)
    
    logger.info("Plotting individual traces...", extra={"spacing": TAB})
    raster_zorder = -12
    for (line, plane), lp_df in ex_traces_df.groupby(["lines", "planes"]):
        li, pl, col, dash = plot_helper_fcts.get_line_plane_idxs(line, plane)
        for i, idx in enumerate(lp_df.index):
            row_idx = int(pl * per_rows + i % per_rows)
            col_idx = int(li * per_cols + i // per_rows)
            sub_ax = ax[row_idx, col_idx]

            ylims[row_idx, col_idx] = plot_ex_gabor_roi_traces(
                sub_ax, 
                lp_df.loc[idx],
                col=col,
                dash=dash,
                zorder=raster_zorder - 1
            )

        time_values = np.asarray(lp_df.loc[lp_df.index[-1], "time_values"])
        
    sess_plot_util.format_linpla_subaxes(ax, fluor="dff", 
        area=False, datatype="roi", sess_ns=None, xticks=None, kind="traces", 
        modif_share=False)

   # fix x ticks and lims
    for sub_ax in ax.reshape(-1):
        xlims = [time_values[0], time_values[-1]]
        xticks = np.linspace(*xlims, 6)
        sub_ax.set_xticks(xticks)
    plot_util.set_interm_ticks(ax, 3, axis="x", fontweight="bold", skip=False)
    for sub_ax in ax.reshape(-1):
        sub_ax.set_xlim(xlims)
    
    # reset y limits
    for r in range(ax.shape[0]):
        for c in range(ax.shape[1]):
            if not np.isfinite(ylims[r, c].sum()):
                continue
            ax[r, c].set_ylim(ylims[r, c])

    plot_util.set_interm_ticks(
        ax, 2, axis="y", share=False, weight="bold", update_ticks=True
        )  

    # rasterize the gray lines
    logger.info("Rasterizing individual traces...", extra={"spacing": TAB})
    for sub_ax in ax.reshape(-1):
        sub_ax.set_rasterization_zorder(raster_zorder)

    return ax
Esempio n. 20
0
def plot_idxs(idx_df, sesspar, figpar, plot="items", density=True, n_bins=40, 
              title=None, size="reg"):
    """
    plot_idxs(idx_df, sesspar, figpar)

    Returns exact color for a specific line.

    Required args:
        - idx_df (pd.DataFrame):
            dataframe with indices for different line/plane combinations, and 
            the following columns, in addition to the basic sess_df columns:
            - rand_idx_binned (list): bin counts for the random ROI indices
            - bin_edges (list): first and last bin edge
            - CI_edges (list): confidence interval limit values
            - CI_perc (list): confidence interval percentile limits
            if plot == "items":
            - roi_idx_binned (list): bin counts for the ROI indices
            if plot == "percs":
            - perc_idx_binned (list): bin counts for the ROI index percentiles
            optionally:
            - n_signif_lo (int): number of significant ROIs (low) 
            - n_signif_hi (int): number of significant ROIs (high)

        - sesspar (dict): 
            dictionary with keys of SessPar namedtuple
        - figpar (dict): 
            dictionary containing the following figure parameter dictionaries
            ["init"] (dict): dictionary with figure initialization parameters
            ["save"] (dict): dictionary with figure saving parameters
            ["dirs"] (dict): dictionary with additional figure parameters

    Optional args:
        - plot (str): 
            type of data to plot ("items" or "percs")
            default: "items"
        - density (bool): 
            if True, histograms are plotted as densities
            default: True
        - n_bins (int): 
            number of bins to use in histograms
            default: 40
        - title (str): 
            plot title
            default: None
        - size (str): 
            plot size ("reg", "small" or "tall")
            default: "reg"
        
    Returns:
        - ax (2D array): 
            array of subplots
    """

    if plot == "items":
        data_key = "roi_idx_binned"
        CI_key = "CI_edges"
    elif plot == "percs":
        data_key = "perc_idx_binned"
        CI_key = "CI_perc"
    else:
        gen_util.accepted_values_error("plot", plot, ["items", "percs"])

    sess_ns = misc_analys.get_sess_ns(sesspar, idx_df)

    n_plots = len(sess_ns) * 4
    figpar["init"]["sharey"] = "row"
    figpar = sess_plot_util.fig_init_linpla(figpar, kind="idx", 
        n_sub=len(sess_ns), sharex=(plot == "percs"))

    y = 1
    if size == "reg":
        subplot_hei = 3.2
        subplot_wid = 5.5
    elif size == "small":
        y = 1.04
        subplot_hei = 2.40
        subplot_wid = 3.75
        figpar["init"]["gs"] = {"hspace": 0.25, "wspace": 0.30}
    elif size == "tall":
        y = 0.98
        subplot_hei = 5.3
        subplot_wid = 5.55
    else:
        gen_util.accepted_values_error("size", size, ["reg", "small", "tall"])
    
    figpar["init"]["subplot_hei"] = subplot_hei
    figpar["init"]["subplot_wid"] = subplot_wid
    figpar["init"]["sharey"] = "row"
    
    fig, ax = plot_util.init_fig(n_plots, **figpar["init"])
    if title is not None:
        fig.suptitle(title, y=y, weight="bold")

    for (line, plane), lp_df in idx_df.groupby(["lines", "planes"]):
        li, pl, col, _ = plot_helper_fcts.get_line_plane_idxs(line, plane)

        for s, sess_n in enumerate(sess_ns):
            rows = lp_df.loc[lp_df["sess_ns"] == sess_n]
            if len(rows) == 0:
                continue
            elif len(rows) > 1:
                raise RuntimeError(
                    "Expected sess_ns to be unique per line/plane."
                    )
            row = rows.loc[rows.index[0]]

            sub_ax = ax[s + pl * len(sess_ns), li]

            # get percentage significant label
            perc_label = None
            if "n_signif_lo" in row.keys() and "n_signif_hi" in row.keys():
                n_sig_lo, n_sig_hi = row["n_signif_lo"], row["n_signif_hi"]
                nrois = np.sum(row["nrois"])
                perc_signif = np.sum([n_sig_lo, n_sig_hi]) / nrois * 100
                perc_label = (f"{perc_signif:.2f}% sig\n"
                    f"({n_sig_lo}-/{n_sig_hi}+ of {nrois})")                

            plot_stim_idx_hist(
                sub_ax, row[data_key], row[CI_key], n_bins=n_bins, 
                rand_data=row["rand_idx_binned"], 
                orig_edges=row["bin_edges"], 
                plot=plot, col=col, density=density, perc_label=perc_label)
            
            if size == "small":
                sub_ax.legend(fontsize="small")

    # Add plane, line info to plots
    y_lab = "Density" if density else f"N ROIs" 
    sess_plot_util.format_linpla_subaxes(ax, datatype="roi", ylab=y_lab, 
        xticks=None, sess_ns=None, kind="idx", modif_share=False, 
        xlab="Index", single_lab=True)

    # Add indices after setting formatting
    if plot == "percs":
        nticks = 5
        xticks = [int(np.around(x, 0)) for x in np.linspace(0, 100, nticks)]
        for sub_ax in ax[-1]:
            sub_ax.set_xticks(xticks)
            sub_ax.set_xticklabels(xticks, weight="bold")
    
    elif plot == "items":
        nticks = 3
        plot_util.set_interm_ticks(
            ax, nticks, axis="x", weight="bold", share=False, skip=False
            )
    
    else:
        gen_util.accepted_values_error("plot", plot, ["items", "percs"])
        
    return ax
Esempio n. 21
0
def plot_full_traces(analyspar, sesspar, extrapar, sess_info, trace_info, 
                     roi_tr=None, figpar=None, savedir=None):
    """
    plot_full_traces(analyspar, sesspar, extrapar, sess_info, trace_info)

    From dictionaries, plots full traces for each session in a separate subplot.
    
    Returns figure name and save directory path.
    
    Required args:
        - analyspar (dict)  : dictionary with keys of AnalysPar namedtuple
        - sesspar (dict)    : dictionary with keys of SessPar namedtuple
        - extrapar (dict)   : dictionary containing additional analysis 
                              parameters
            ["analysis"] (str): analysis type (e.g., "f")
            ["datatype"] (str): datatype (e.g., "run", "roi")
        - sess_info (dict)  : dictionary containing information from each
                              session 
            ["mouse_ns"] (list)   : mouse numbers
            ["sess_ns"] (list)    : session numbers  
            ["lines"] (list)      : mouse lines
            ["planes"] (list)     : imaging planes
            ["nrois"] (list)      : number of ROIs in session

        - trace_info (dict): dictionary containing trace information
            ["all_tr"] (nested list): trace values structured as
                                          sess x 
                                          (me/err if datatype is "roi" x)
                                          frames
            ["all_edges"] (list)    : frame edge values for each parameter, 
                                      structured as sess x block x 
                                                    edges ([start, end])
            ["all_pars"] (list)     : stimulus parameter strings structured as 
                                                    sess x block
                
    Optional args:
        - roi_tr (list): trace values for each ROI, structured as 
                         sess x ROI x frames
                         default: None
        - figpar (dict): dictionary containing the following figure parameter 
                         dictionaries
                         default: None
            ["init"] (dict): dictionary with figure initialization parameters
            ["save"] (dict): dictionary with figure saving parameters
            ["dirs"] (dict): dictionary with additional figure parameters
        - savedir (str): path of directory in which to save plots.
                         default: None    
    
    Returns:
        - fulldir (str) : final path of the directory in which the figure is 
                          saved (may differ from input savedir, if datetime 
                          subfolder is added.)
        - savename (str): name under which the figure is saved
    """
 
    statstr_pr = sess_str_util.stat_par_str(
        analyspar["stats"], analyspar["error"], "print")
    dendstr_pr = sess_str_util.dend_par_str(
        analyspar["dend"], sesspar["plane"], extrapar["datatype"], "print")
        
    sessstr = f"sess{sesspar['sess_n']}_{sesspar['plane']}"
    dendstr = sess_str_util.dend_par_str(
        analyspar["dend"], sesspar["plane"], extrapar["datatype"])

    datatype = extrapar["datatype"]

    # extract some info from sess_info
    keys = ["mouse_ns", "sess_ns", "lines", "planes"]
    [mouse_ns, sess_ns, lines, planes] = [sess_info[key] for key in keys]
    nroi_strs = sess_str_util.get_nroi_strs(sess_info, empty=(datatype!="roi")) 
    n_sess = len(mouse_ns)

    if figpar is None:
        figpar = sess_plot_util.init_figpar()
    figpar = copy.deepcopy(figpar)
    figpar["init"]["subplot_wid"] = 10
    figpar["init"]["ncols"] = n_sess
    if datatype == "roi":
        figpar["save"]["fig_ext"] = "jpg"
        figpar["init"]["sharex"] = False
        figpar["init"]["sharey"] = False
        # set subplot ratios and removes extra space between plots vertically
        gs = {"height_ratios": [5, 1], "hspace": 0.1} 
        n_rows = 2
        if roi_tr is None:
            raise ValueError("Cannot plot data as ROI traces are missing "
                "(not recorded in dictionary, likely due to size).")
    else:
        gs = None
        n_rows = 1

    if datatype == "roi" and not figpar["save"]["save_fig"]:
        warnings.warn("Figure plotting is being skipped. Since full ROI traces "
            "are not saved to dictionary, to actually plot traces, analysis "
            "will have to be rerun with 'save_fig' set to True.", 
            stacklevel=1)

    fig, ax = plot_util.init_fig(n_sess*n_rows, gs=gs, **figpar["init"])

    label_height = 0.8
    if datatype == "roi":
        fig.subplots_adjust(top=0.92) # remove extra white space at top
        label_height = 0.55
    for i in range(n_sess):
        title = (f"Mouse {mouse_ns[i]} (sess {sess_ns[i]}, {lines[i]} "
            f"{planes[i]}{dendstr_pr}{nroi_strs[i]})")
        sub_axs = ax[:, i]
        sub_axs[0].set_title(title, y=1.02)
        if datatype == "roi":
            xran = range(len(trace_info["all_tr"][i][1]))   
            # each ROI (top subplot)
            plot_util.plot_sep_data(sub_axs[0], np.asarray(roi_tr[i]), 0.1)
            sess_plot_util.add_axislabels(
                sub_axs[0], fluor=analyspar["fluor"], scale=True, 
                datatype=datatype, x_ax="")
            
            # average across ROIs (bottom subplot)
            av_tr = np.asarray(trace_info["all_tr"][i])
            subtitle = u"{} across ROIs".format(statstr_pr)
            plot_util.plot_traces(
                sub_axs[1], xran, av_tr[0], av_tr[1:], lw=0.2, xticks="auto",
                title=subtitle
                )
        else:
            xran = range(len(trace_info["all_tr"][i]))
            run_tr = np.asarray(trace_info["all_tr"][i])
            sub_axs[0].plot(run_tr, lw=0.2)
        for b, block in enumerate(trace_info["all_edges"][i]):
            # all block labels to the lower plot
            plot_util.add_labels(
                sub_axs[-1], trace_info["all_pars"][i][b], np.mean(block), 
                label_height, color="k")
            sess_plot_util.add_axislabels(
                sub_axs[-1], fluor=analyspar["fluor"], datatype=datatype, 
                x_ax="")
            plot_util.remove_ticks(sub_axs[-1], True, False)
            plot_util.remove_graph_bars(sub_axs[-1], bars="horiz")
            # add lines to both plots
            for r in range(n_rows):
                plot_util.add_bars(sub_axs[r], bars=block)
                
    if savedir is None:
        savedir = Path(
            figpar["dirs"][datatype], 
            figpar["dirs"]["full"])

    y = 1 if datatype == "run" else 0.98
    fig.suptitle("Full traces across sessions", fontsize="xx-large", y=y)

    savename = f"{datatype}_tr_{sessstr}{dendstr}"
    fulldir = plot_util.savefig(
        fig, savename, savedir, dpi=400, **figpar["save"])


    return fulldir, savename
Esempio n. 22
0
def plot_perc_sig_usis(perc_sig_df, analyspar, permpar, figpar, by_mouse=False, 
                       title=None):
    """
    plot_perc_sig_usis(perc_sig_df, analyspar, figpar)

    Plots percentage of significant USIs.

    Required args:
        - perc_sig_df (pd.DataFrame):
            dataframe with one row per session/line/plane, and the following 
            columns, in addition to the basic sess_df columns:
            for sig in ["lo", "hi"]: for low vs high ROI indices
            - perc_sig_{sig}_idxs (num): percent significant ROIs (0-100)
            - perc_sig_{sig}_idxs_stds (num): bootstrapped standard deviation 
                over percent significant ROIs
            - perc_sig_{sig}_idxs_CIs (list): adjusted CI for percent sig. ROIs 
            - perc_sig_{sig}_idxs_null_CIs (list): adjusted null CI for percent 
                sig. ROIs
            - perc_sig_{sig}_idxs_raw_p_vals (num): uncorrected p-value for 
                percent sig. ROIs
            - perc_sig_{sig}_idxs_p_vals (num): p-value for percent sig. 
                ROIs, corrected for multiple comparisons and tails

        - analyspar (dict): 
            dictionary with keys of AnalysPar namedtuple
        - permpar (dict): 
            dictionary with keys of PermPar namedtuple
        - figpar (dict): 
            dictionary containing the following figure parameter dictionaries
            ["init"] (dict): dictionary with figure initialization parameters
            ["save"] (dict): dictionary with figure saving parameters
            ["dirs"] (dict): dictionary with additional figure parameters

    Optional args:
        - by_mouse (bool):
            if True, plotting is done per mouse
            default: False
        - title (str):
            plot title
            default: None
        
    Returns:
        - ax (2D array): 
            array of subplots
    """  

    perc_sig_df = perc_sig_df.copy(deep=True)

    nanpol = None if analyspar["rem_bad"] else "omit"

    sess_ns = perc_sig_df["sess_ns"].unique()
    if len(sess_ns) != 1:
        raise NotImplementedError(
            "Plotting function implemented for 1 session only."
            )

    figpar = sess_plot_util.fig_init_linpla(figpar, kind="idx", n_sub=1, 
        sharex=True, sharey=True)

    figpar["init"]["sharey"] = True
    figpar["init"]["subplot_wid"] = 3.4
    figpar["init"]["gs"] = {"wspace": 0.18}
    if by_mouse:
        figpar["init"]["subplot_hei"] = 8.4
    else:
        figpar["init"]["subplot_hei"] = 3.5

    fig, ax = plot_util.init_fig(2, **figpar["init"])
    if title is not None:
        y = 0.98 if by_mouse else 1.07
        fig.suptitle(title, y=y, weight="bold")

    tail_order = ["Low tail", "High tail"]
    tail_keys = ["lo", "hi"]
    chance = permpar["p_val"] / 2 * 100

    ylims = get_perc_sig_ylims(perc_sig_df, high_pt_min=40)
    n_linpla = plot_helper_fcts.N_LINPLA

    comp_info = misc_analys.get_comp_info(permpar)
    
    logger.info(f"{comp_info}:", extra={"spacing": "\n"})
    for t, (tail, key) in enumerate(zip(tail_order, tail_keys)):
        sub_ax = ax[0, t]
        sub_ax.set_title(tail, fontweight="bold")
        sub_ax.set_ylim(ylims)

        # replace bottom spine with line at 0
        sub_ax.spines['bottom'].set_visible(False)
        sub_ax.axhline(y=0, c="k", lw=4.0)

        data_key = f"perc_sig_{key}_idxs"

        CIs = np.full((plot_helper_fcts.N_LINPLA, 2), np.nan)
        CI_meds = np.full(plot_helper_fcts.N_LINPLA, np.nan)

        tail_sig_str = f"{tail:9}:"
        linpla_names = []
        for (line, plane), lp_df in perc_sig_df.groupby(["lines", "planes"]):
            li, pl, col, _ = plot_helper_fcts.get_line_plane_idxs(line, plane)
            x_index = 2 * li + pl
            linpla_name = plot_helper_fcts.get_line_plane_name(line, plane)
            linpla_names.append(linpla_name)
            
            if len(lp_df) == 0:
                continue
            elif len(lp_df) > 1 and not by_mouse:
                raise RuntimeError("Expected a single row per line/plane.")

            lp_df = lp_df.sort_values("mouse_ns") # sort by mouse
            df_indices = lp_df.index.tolist()

            if by_mouse:
                # plot means or medians per mouse
                mouse_data = lp_df[data_key].to_numpy()
                mouse_cols = plot_util.get_hex_color_range(
                    len(lp_df), col=col, 
                    interval=plot_helper_fcts.MOUSE_COL_INTERVAL
                    )
                mouse_data_mean = math_util.mean_med(
                    mouse_data, stats=analyspar["stats"], nanpol=nanpol
                    )
                CI_dummy = np.repeat(mouse_data_mean, 2)
                plot_util.plot_CI(sub_ax, CI_dummy, med=mouse_data_mean, 
                    x=x_index, width=0.6, med_col=col, med_rat=0.01)
            else:
                # collect confidence interval data
                row = lp_df.loc[df_indices[0]]
                mouse_cols = [col]
                CIs[x_index] = np.asarray(row[f"{data_key}_null_CIs"])[
                    np.asarray([0, 2])
                    ]
                CI_meds[x_index] = row[f"{data_key}_null_CIs"][1]

            if by_mouse:
                perc_p_vals = []
                rel_y = 0.05
            else:
                tail_sig_str = f"{tail_sig_str}{TAB}{linpla_name}: "
                rel_y = 0.1

            for df_i, mouse_col in zip(df_indices, mouse_cols):
                # plot UFOs
                err = None
                no_line = True
                if not by_mouse:
                    err = perc_sig_df.loc[df_i, f"{data_key}_stds"]
                    no_line = False
                # indicate bootstrapped error with wider capsize
                plot_util.plot_ufo(
                    sub_ax, x_index, perc_sig_df.loc[df_i, data_key], err,
                    color=mouse_col, capsize=8, no_line=no_line
                    )

                # add significance markers
                p_val = perc_sig_df.loc[df_i, f"{data_key}_p_vals"]
                perc = perc_sig_df.loc[df_i, data_key]
                nrois = np.sum(perc_sig_df.loc[df_i, "nrois"])
                side = np.sign(perc - chance)
                sensitivity = misc_analys.get_binom_sensitivity(
                    nrois, null_perc=chance, side=side
                    )                
                sig_str = misc_analys.get_sig_symbol(
                    p_val, sensitivity=sensitivity, side=side, 
                    tails=permpar["tails"], p_thresh=permpar["p_val"]
                    )

                if len(sig_str):
                    perc_high = perc + err if err is not None else perc
                    plot_util.add_signif_mark(sub_ax, x_index, perc_high, 
                        rel_y=rel_y, color=mouse_col, fontsize=24, 
                        mark=sig_str) 

                if by_mouse:
                    perc_p_vals.append(
                        (int(np.around(perc)), p_val, sig_str)
                    )
                else:
                    tail_sig_str = (
                        f"{tail_sig_str}{p_val:.5f}{sig_str:3}"
                        )

            if by_mouse: # sort p-value logging by percentage value
                tail_sig_str = f"{tail_sig_str}\n\t{linpla_name:6}: "
                order = np.argsort([vals[0] for vals in perc_p_vals])
                for i in order:
                    perc, p_val, sig_str = perc_p_vals[i]
                    perc_str = f"(~{perc}%)"
                    tail_sig_str = (
                        f"{tail_sig_str}{TAB}{perc_str:6} "
                        f"{p_val:.5f}{sig_str:3}"
                        )
                
        # add chance information
        if by_mouse:
            sub_ax.axhline(
                y=chance, ls=plot_helper_fcts.VDASH, c="k", lw=3.0, alpha=0.5, 
                zorder=-12
                )
        else:
            plot_util.plot_CI(sub_ax, CIs.T, med=CI_meds, 
                x=np.arange(n_linpla), width=0.45, med_rat=0.025, zorder=-12)

        logger.info(tail_sig_str, extra={"spacing": TAB})
    
    for sub_ax in fig.axes:
        sub_ax.tick_params(axis="x", which="both", bottom=False) 
        plot_util.set_ticks(
            sub_ax, min_tick=0, max_tick=n_linpla - 1, n=n_linpla, pad_p=0.2)
        sub_ax.set_xticklabels(linpla_names, rotation=90, weight="bold")

    ax[0, 0].set_ylabel("%", fontweight="bold")
    plot_util.set_interm_ticks(ax, 3, axis="y", weight="bold", share=True)

    # adjustment if tick interval is repeated in the negative
    if ax[0, 0].get_ylim()[0] < 0:
        ax[0, 0].set_ylim([ylims[0], ax[0, 0].get_ylim()[1]])

    return ax
Esempio n. 23
0
def plot_traces_by_qu_lock_sess(analyspar, sesspar, stimpar, extrapar, 
                                quantpar, sess_info, trace_stats, 
                                figpar=None, savedir=None, modif=False):
    """
    plot_traces_by_qu_lock_sess(analyspar, sesspar, stimpar, extrapar, 
                                quantpar, sess_info, trace_stats)

    From dictionaries, plots traces by quantile, locked to transitions from 
    unexpected to expected or v.v. with each session in a separate subplot.
    
    Returns figure name and save directory path.
    
    Required args:
        - analyspar (dict)  : dictionary with keys of AnalysPar namedtuple
        - sesspar (dict)    : dictionary with keys of SessPar namedtuple
        - stimpar (dict)    : dictionary with keys of StimPar namedtuple
        - extrapar (dict)   : dictionary containing additional analysis 
                              parameters
            ["analysis"] (str): analysis type (e.g., "l")
            ["datatype"] (str): datatype (e.g., "run", "roi")
        - quantpar (dict)   : dictionary with keys of QuantPar namedtuple
        - sess_info (dict)  : dictionary containing information from each
                              session 
            ["mouse_ns"] (list)   : mouse numbers
            ["sess_ns"] (list)    : session numbers  
            ["lines"] (list)      : mouse lines
            ["planes"] (list)     : imaging planes
            if datatype == 
            ["nrois"] (list)      : number of ROIs in session

        - trace_stats (dict): dictionary containing trace stats information
            ["xrans"] (list)           : time values for the 2p frames for each 
                                         session
            ["all_stats"] (list)       : list of 4D arrays or lists of trace 
                                         data statistics across ROIs for each 
                                         session, structured as:
                                            (unexp_len x) quantiles x
                                            stats (me, err) x frames
            ["all_counts"] (array-like): number of sequences, structured as:
                                                sess x (unexp_len x) quantiles
            ["lock"] (str)             : value to which segments are locked:
                                         "unexp", "exp" or "unexp_split"
            ["baseline"] (num)         : number of seconds used for baseline
            ["exp_stats"] (list)       : list of 3D arrays or lists of trace 
                                         data statistics across ROIs for
                                         expected sampled sequences, 
                                         structured as:
                                            quantiles (1) x stats (me, err) 
                                            x frames
            ["exp_counts"] (array-like): number of sequences corresponding to
                                         exp_stats, structured as:
                                            sess x quantiles (1)
            
            if data is by unexp_len:
            ["unexp_lens"] (list)       : number of consecutive segments for
                                         each unexp_len, structured by session
                
    Optional args:
        - figpar (dict): dictionary containing the following figure parameter 
                         dictionaries
                         default: None
            ["init"] (dict): dictionary with figure initialization parameters
            ["save"] (dict): dictionary with figure saving parameters
            ["dirs"] (dict): dictionary with additional figure parameters
        - savedir (str): path of directory in which to save plots.
                         default: None   
        - modif (bool) : if True, modified (slimmed-down) plots are created
                         instead
                         default: False
    
    Returns:
        - fulldir (str) : final path of the directory in which the figure is 
                          saved (may differ from input savedir, if datetime 
                          subfolder is added.)
        - savename (str): name under which the figure is saved
    """
    analyspar["dend"] = None
    stimstr_pr = sess_str_util.stim_par_str(
        stimpar["stimtype"], stimpar["visflow_dir"], stimpar["visflow_size"],
        stimpar["gabk"], "print")
    statstr_pr = sess_str_util.stat_par_str(
        analyspar["stats"], analyspar["error"], "print")
    dendstr_pr = sess_str_util.dend_par_str(
        analyspar["dend"], sesspar["plane"], extrapar["datatype"], "print")
        
    sessstr = sess_str_util.sess_par_str(
        sesspar["sess_n"], stimpar["stimtype"], sesspar["plane"], 
        stimpar["visflow_dir"], stimpar["visflow_size"], stimpar["gabk"])
    dendstr = sess_str_util.dend_par_str(
        analyspar["dend"], sesspar["plane"], extrapar["datatype"])
     
    basestr = sess_str_util.base_par_str(trace_stats["baseline"])
    basestr_pr = sess_str_util.base_par_str(trace_stats["baseline"], "print")

    datatype = extrapar["datatype"]
    dimstr = sess_str_util.datatype_dim_str(datatype)

    # extract some info from sess_info
    keys = ["mouse_ns", "sess_ns", "lines", "planes"]
    [mouse_ns, sess_ns, lines, planes] = [sess_info[key] for key in keys]
    nroi_strs = sess_str_util.get_nroi_strs(sess_info, empty=(datatype!="roi")) 

    n_sess = len(mouse_ns)

    xrans      = [np.asarray(xran) for xran in trace_stats["xrans"]]
    all_stats  = [np.asarray(sessst) for sessst in trace_stats["all_stats"]]
    exp_stats  = [np.asarray(expst) for expst in trace_stats["exp_stats"]]
    all_counts = trace_stats["all_counts"]
    exp_counts = trace_stats["exp_counts"]

    lock  = trace_stats["lock"]
    col_idx = 0
    if "unexp" in lock:
        lock = "unexp"
        col_idx = 1
    
    # plot unexp_lens default values
    if stimpar["stimtype"] == "gabors":
        DEFAULT_UNEXP_LEN = [3.0, 4.5, 6.0]
        if stimpar["gabfr"] not in ["any", "all"]:
            offset = sess_str_util.gabfr_nbrs(stimpar["gabfr"])
    else:
        DEFAULT_UNEXP_LEN = [2.0, 3.0, 4.0]
    
    offset = 0
    unexp_lab, len_ext = "", ""
    unexp_lens = [[None]] * n_sess
    unexp_len_default = True
    if "unexp_lens" in trace_stats.keys():
        unexp_len_default = False
        unexp_lens = trace_stats["unexp_lens"]
        len_ext = "_bylen"
        
        if stimpar["stimtype"] == "gabors":
            unexp_lens = [
                [sl * 1.5/5 - 0.3 * offset for sl in sls] for sls in unexp_lens
                ]

    inv = 1 if lock == "unexp" else -1
    # RANGE TO PLOT
    if modif:
        st_val = -2.0
        end_val  = 6.0
        n_ticks = int((end_val - st_val) // 2 + 1)
    else:
        n_ticks = 21

    if figpar is None:
        figpar = sess_plot_util.init_figpar()
    figpar = copy.deepcopy(figpar)
    if modif:
        figpar["init"]["subplot_wid"] = 6.5
    else:
        figpar["init"]["subplot_wid"] *= 2

    fig, ax = plot_util.init_fig(n_sess, **figpar["init"])
    exp_min, exp_max = np.inf, -np.inf
    for i, (stats, counts) in enumerate(zip(all_stats, all_counts)):
        sub_ax = plot_util.get_subax(ax, i)

        # plot expected data
        if exp_stats[i].shape[0] != 1:
            raise ValueError("Expected only one quantile for exp_stats.")

        n_lines = quantpar["n_quants"] * len(unexp_lens[i])
        cols = sess_plot_util.get_quant_cols(n_lines)[0][col_idx]
        if len(cols) < n_lines:
            cols = [None] * n_lines

        if modif:
            line = "2/3" if "23" in lines[i] else "5"
            plane = "somata" if "soma" in planes[i] else "dendrites"
            title = f"M{mouse_ns[i]} - layer {line} {plane}{dendstr_pr}"
            lab = "exp" if i == 0 else None
            y_ax = None if i == 0 else ""

            st, end = 0, len(xrans[i])
            st_vals = list(filter(
                lambda j: xrans[i][j] <= st_val, range(len(xrans[i]))
                ))
            end_vals = list(filter(
                lambda j: xrans[i][j] >= end_val, range(len(xrans[i]))
                ))
            if len(st_vals) != 0:
                st = st_vals[-1]
            if len(end_vals) != 0:
                end = end_vals[0] + 1
            time_slice = slice(st, end)

        else:
            title = (f"Mouse {mouse_ns[i]} - {stimstr_pr}, "
                u"{} ".format(statstr_pr) + f"{lock} locked across {dimstr}"
                f"{basestr_pr}\n(sess {sess_ns[i]}, {lines[i]} {planes[i]}"
                f"{dendstr_pr}{nroi_strs[i]})")
            lab = f"exp (no lock) ({exp_counts[i][0]})"
            y_ax = None
            st = 0
            end = len(xrans[i])
            time_slice = slice(None) # use all

        # add length markers
        use_unexp_lens = unexp_lens[i]
        if unexp_len_default:
            use_unexp_lens = DEFAULT_UNEXP_LEN
        leng_col = sess_plot_util.get_quant_cols(1)[0][col_idx][0]
        for leng in use_unexp_lens:
            if leng is None:
                continue
            edge = leng * inv
            if edge < 0:
                edge = np.max([xrans[i][st], edge])
            elif edge > 0:
                edge = np.min([xrans[i][end - 1], edge])
            plot_util.add_vshade(
                sub_ax, 0, edge, color=leng_col, alpha=0.1)

        sess_plot_util.add_axislabels(
            sub_ax, fluor=analyspar["fluor"], datatype=datatype, y_ax=y_ax
            )
        plot_util.add_bars(sub_ax, hbars=0)
        alpha = np.min([0.4, 0.8 / n_lines])

        if stimpar["stimtype"] == "gabors":
            sess_plot_util.plot_gabfr_pattern(
                sub_ax, xrans[i], offset=offset, bars_omit=[0] + unexp_lens[i]
                )

        plot_util.plot_traces(
            sub_ax, xrans[i][time_slice], exp_stats[i][0][0, time_slice], 
            exp_stats[i][0][1:, time_slice], n_xticks=n_ticks,
            alpha=alpha, label=lab, alpha_line=0.8, color="darkgray", 
            xticks="auto")

        # get expected data range to adjust y lims
        exp_min = np.min([exp_min, np.nanmin(exp_stats[i][0][0])])
        exp_max = np.max([exp_max, np.nanmax(exp_stats[i][0][0])])

        n = 0 # count lines plotted
        for s, unexp_len in enumerate(unexp_lens[i]):
            if unexp_len is not None:
                counts, stats = all_counts[i][s], all_stats[i][s]       
                # remove offset   
                unexp_lab = f"unexp len {unexp_len + 0.3 * offset}"
            else:
                unexp_lab = "unexp" if modif else f"{lock} lock"
            for q, qu_idx in enumerate(quantpar["qu_idx"]):
                qu_lab = ""
                if quantpar["n_quants"] > 1:
                    qu_lab = "{} ".format(sess_str_util.quantile_str(
                        qu_idx, quantpar["n_quants"], str_type="print"
                        ))
                lab = f"{qu_lab}{unexp_lab}"
                if modif:
                    lab = lab if i == 0 else None
                else:
                    lab = f"{lab} ({counts[q]})"
                if n == 2 and cols[n] is None:
                    sub_ax.plot([], []) # to advance the color cycle (past gray)
                plot_util.plot_traces(sub_ax, xrans[i][time_slice], 
                    stats[q][0, time_slice], stats[q][1:, time_slice], title, 
                    alpha=alpha, label=lab, n_xticks=n_ticks, alpha_line=0.8, 
                    color=cols[n], xticks="auto")
                n += 1
            if unexp_len is not None:
                plot_util.add_bars(
                    sub_ax, hbars=unexp_len, color=sub_ax.lines[-1].get_color(), 
                    alpha=1)
    
    plot_util.turn_off_extra(ax, n_sess)

    if savedir is None:
        savedir = Path(
            figpar["dirs"][datatype], 
            figpar["dirs"]["unexp_qu"], 
            f"{lock}_lock", basestr.replace("_", ""))

    if not modif:
        if stimpar["stimtype"] == "visflow":
            plot_util.rel_confine_ylims(sub_ax, [exp_min, exp_max], 5)

    qu_str = f"_{quantpar['n_quants']}q"
    if quantpar["n_quants"] == 1:
        qu_str = ""
 
    savename = (f"{datatype}_av_{lock}_lock{len_ext}{basestr}_{sessstr}"
        f"{dendstr}{qu_str}")
    fulldir = plot_util.savefig(fig, savename, savedir, **figpar["save"])

    return fulldir, savename
Esempio n. 24
0
def plot_tracked_idxs(idx_only_df, sesspar, figpar, title=None, wide=False):
    """
    plot_tracked_idxs(idx_only_df, sesspar, figpar)

    Plots tracked ROI USIs as individual lines.

    Required args:
        - idx_only_df (pd.DataFrame):
            dataframe with one row per (mouse/)session/line/plane, and the 
            following columns, in addition to the basic sess_df columns:
            - roi_idxs (list): index for each ROI

        - sesspar (dict): 
            dictionary with keys of SessPar namedtuple
        - figpar (dict): 
            dictionary containing the following figure parameter dictionaries
            ["init"] (dict): dictionary with figure initialization parameters
            ["save"] (dict): dictionary with figure saving parameters
            ["dirs"] (dict): dictionary with additional figure parameters

    Optional args:
        - title (str):
            plot title
            default: None
        - wide (bool):
            if True, subplots are wider
            default: False

    Returns:
        - ax (2D array): 
            array of subplots    
    """

    sess_ns = misc_analys.get_sess_ns(sesspar, idx_only_df)

    figpar = sess_plot_util.fig_init_linpla(figpar)

    figpar["init"]["sharey"] = "row"
    figpar["init"]["subplot_hei"] = 4.1
    figpar["init"]["subplot_wid"] = 2.5
    figpar["init"]["gs"] = {"wspace": 0.25, "hspace": 0.2}
    if wide:
        figpar["init"]["subplot_wid"] = 3.3
        figpar["init"]["gs"]["wspace"] = 0.25

    fig, ax = plot_util.init_fig(plot_helper_fcts.N_LINPLA, **figpar["init"])

    if title is not None:
        fig.suptitle(title, y=0.98, weight="bold")

    for (line, plane), lp_df in idx_only_df.groupby(["lines", "planes"]):
        li, pl, col, _ = plot_helper_fcts.get_line_plane_idxs(line, plane)
        sub_ax = ax[pl, li]

        # mouse_ns
        lp_mouse_ns = sorted(lp_df["mouse_ns"].unique())

        lp_data = []
        for mouse_n in lp_mouse_ns:
            mouse_df = lp_df.loc[lp_df["mouse_ns"] == mouse_n]
            nrois = mouse_df["nrois"].unique()
            if len(nrois) != 1:
                raise RuntimeError(
                    "Each mouse in idx_stats_df should retain the same number "
                    " of ROIs across sessions.")
            
            mouse_data = np.full((len(sess_ns), nrois[0]), np.nan)
            for s, sess_n in enumerate(sess_ns):
                rows = mouse_df.loc[mouse_df["sess_ns"] == sess_n]
                if len(rows) == 1:
                    mouse_data[s] = rows.loc[rows.index[0], "roi_idxs"]
                elif len(rows) > 1:
                    raise RuntimeError(
                        "Expected 1 row per line/plane/session/mouse."
                        )
            lp_data.append(mouse_data)

        lp_data = np.concatenate(lp_data, axis=1)

        sub_ax.axhline(
            y=0, ls=plot_helper_fcts.HDASH, c="k", lw=3.0, alpha=0.5, 
            zorder=-13
            )
        sub_ax.plot(sess_ns, lp_data, color=col, lw=2, alpha=0.3)
    
    # Add plane, line info to plots
    sess_plot_util.format_linpla_subaxes(
        ax, datatype="roi", xticks=sess_ns, ylab="", kind="reg"
        )

    for sub_ax in ax.reshape(-1):
        xticks = sub_ax.get_xticks()
        plot_util.set_ticks(
            sub_ax, "x", np.min(xticks), np.max(xticks), n=len(xticks), 
            pad_p=0.2
            )
    
    return ax
Esempio n. 25
0
def plot_autocorr(analyspar, sesspar, stimpar, extrapar, autocorrpar, 
                  sess_info, autocorr_data, figpar=None, savedir=None):
    """
    plot_autocorr(analyspar, sesspar, stimpar, extrapar, autocorrpar, 
                  sess_info, autocorr_data)

    From dictionaries, plots autocorrelation during stimulus blocks.

    Required args:
        - analyspar (dict)    : dictionary with keys of AnalysPar namedtuple
        - sesspar (dict)      : dictionary with keys of SessPar namedtuple 
        - stimpar (dict)      : dictionary with keys of StimPar namedtuple
        - extrapar (dict)     : dictionary containing additional analysis 
                                parameters
            ["analysis"] (str): analysis type (e.g., "a")
            ["datatype"] (str): datatype (e.g., "run", "roi")
        - autocorrpar (dict)  : dictionary with keys of AutocorrPar namedtuple
        - sess_info (dict)    : dictionary containing information from each
                                session 
            ["mouse_ns"] (list)   : mouse numbers
            ["sess_ns"] (list)    : session numbers  
            ["lines"] (list)      : mouse lines
            ["planes"] (list)     : imaging planes
            ["nrois"] (list)      : number of ROIs in session

        - autocorr_data (dict): dictionary containing data to plot:
            ["xrans"] (list): list of lag values in seconds for each session
            ["stats"] (list): list of 3D arrays (or nested lists) of
                              autocorrelation statistics, structured as:
                                     sessions stats (me, err) 
                                     x ROI or 1x and 10x lag 
                                     x lag
    
    Optional args:
        - figpar (dict): dictionary containing the following figure parameter 
                         dictionaries
                         default: None
            ["init"] (dict): dictionary with figure initialization parameters
            ["save"] (dict): dictionary with figure saving parameters
            ["dirs"] (dict): dictionary with additional figure parameters
        - savedir (str): path of directory in which to save plots.
                         default: None
    
    Returns:
        - fulldir (str) : final path of the directory in which the figure is 
                          saved (may differ from input savedir, if datetime 
                          subfolder is added.)
        - savename (str): name under which the figure is saved
    """


    statstr_pr = sess_str_util.stat_par_str(
        analyspar["stats"], analyspar["error"], "print")
    stimstr_pr = sess_str_util.stim_par_str(
        stimpar["stimtype"], stimpar["visflow_dir"], stimpar["visflow_size"], 
        stimpar["gabk"], "print")
    dendstr_pr = sess_str_util.dend_par_str(
        analyspar["dend"], sesspar["plane"], extrapar["datatype"], "print")

    sessstr = sess_str_util.sess_par_str(
        sesspar["sess_n"], stimpar["stimtype"], sesspar["plane"], 
        stimpar["visflow_dir"],stimpar["visflow_size"], stimpar["gabk"]) 
    dendstr = sess_str_util.dend_par_str(
        analyspar["dend"], sesspar["plane"], extrapar["datatype"])
     
    datatype = extrapar["datatype"]
    if datatype == "roi":
        fluorstr_pr = sess_str_util.fluor_par_str(
            analyspar["fluor"], str_type="print")
        if autocorrpar["byitem"]:
            title_str = u"{}\nautocorrelation".format(fluorstr_pr)
        else:
            title_str = "\nautocorr. acr. ROIs" .format(fluorstr_pr)

    elif datatype == "run":
        datastr = sess_str_util.datatype_par_str(datatype)
        title_str = u"\n{} autocorrelation".format(datastr)

    if stimpar["stimtype"] == "gabors":
        seq_bars = [-1.5, 1.5] # light lines
    else:
        seq_bars = [-1.0, 1.0] # light lines

    # extract some info from sess_info
    keys = ["mouse_ns", "sess_ns", "lines", "planes"]
    [mouse_ns, sess_ns, lines, planes] = [sess_info[key] for key in keys]
    nroi_strs = sess_str_util.get_nroi_strs(sess_info, empty=(datatype!="roi")) 

    n_sess = len(mouse_ns)

    xrans = autocorr_data["xrans"]
    stats = [np.asarray(stat) for stat in autocorr_data["stats"]]

    lag_s = autocorrpar["lag_s"]
    xticks = np.linspace(-lag_s, lag_s, lag_s*2+1)
    yticks = np.linspace(0, 1, 6)

    if figpar is None:
        figpar = sess_plot_util.init_figpar()

    byitemstr = ""
    if autocorrpar["byitem"]:
        byitemstr = "_byroi"

    fig, ax = plot_util.init_fig(n_sess, **figpar["init"])
    for i in range(n_sess):
        sub_ax = plot_util.get_subax(ax, i)
        title = (f"Mouse {mouse_ns[i]} - {stimstr_pr}, "
            u"{} ".format(statstr_pr) + f"{title_str} (sess "
            f"{sess_ns[i]}, {lines[i]} {planes[i]}{dendstr_pr}{nroi_strs[i]})")
        # transpose to ROI/lag x stats x series
        sess_stats = stats[i].transpose(1, 0, 2) 
        for s, sub_stats in enumerate(sess_stats):
            lab = None
            if not autocorrpar["byitem"]:
                lab = ["actual lag", "10x lag"][s]

            plot_util.plot_traces(
                sub_ax, xrans[i], sub_stats[0], sub_stats[1:], xticks=xticks, 
                yticks=yticks, alpha=0.2, label=lab)

        plot_util.add_bars(sub_ax, hbars=seq_bars)
        sub_ax.set_ylim([0, 1])
        sub_ax.set_title(title, y=1.02)
        if sub_ax.is_last_row():
            sub_ax.set_xlabel("Lag (s)")

    plot_util.turn_off_extra(ax, n_sess)

    if savedir is None:
        savedir = Path(
            figpar["dirs"][datatype], 
            figpar["dirs"]["autocorr"])

    savename = (f"{datatype}_autocorr{byitemstr}_{sessstr}{dendstr}")

    fulldir = plot_util.savefig(fig, savename, savedir, **figpar["save"])

    return fulldir, savename
Esempio n. 26
0
def plot_tracked_idx_stats(idx_stats_df, sesspar, figpar, permpar=None,
                           absolute=True, between_sess_sig=True, 
                           by_mouse=False, bootstr_err=None, title=None, 
                           wide=False):
    """
    plot_tracked_idx_stats(idx_stats_df, sesspar, figpar)

    Plots tracked ROI USI statistics.

    Required args:
        - idx_stats_df (pd.DataFrame):
            dataframe with one row per session, and the following columns, in 
            addition to the basic sess_df columns:
            - roi_idxs (list): index statistics
            or
            - abs_roi_idxs (list): absolute index statistics
        - sesspar (dict): 
            dictionary with keys of SessPar namedtuple
        - figpar (dict): 
            dictionary containing the following figure parameter dictionaries
            ["init"] (dict): dictionary with figure initialization parameters
            ["save"] (dict): dictionary with figure saving parameters
            ["dirs"] (dict): dictionary with additional figure parameters

    Optional args:
        - permpar (dict): 
            dictionary with keys of PermPar namedtuple. Required if 
            between_sess_sig is True.
            default: None
        - absolute (bool):
            if True, data statistics are on absolute ROI indices
            default: True
        - between_sess_sig (bool):
            if True, significance between sessions is logged and plotted
            default: True
        - by_mouse (bool):
            if True, plotting is done per mouse
            default: False
        - bootstr_err (bool):
            if True, error is bootstrapped standard deviation
            default: False
        - title (str):
            plot title
            default: None
        - wide (bool):
            if True, subplots are wider
            default: False

    Returns:
        - ax (2D array): 
            array of subplots    
    """

    sess_ns = misc_analys.get_sess_ns(sesspar, idx_stats_df)

    figpar = sess_plot_util.fig_init_linpla(figpar)

    figpar["init"]["sharey"] = "row"
    figpar["init"]["subplot_hei"] = 4.1
    figpar["init"]["subplot_wid"] = 2.6
    figpar["init"]["gs"] = {"wspace": 0.25, "hspace": 0.2}
    if wide:
        figpar["init"]["subplot_wid"] = 3.3

    fig, ax = plot_util.init_fig(plot_helper_fcts.N_LINPLA, **figpar["init"])

    if title is not None:
        fig.suptitle(title, y=0.98, weight="bold")
    
    data_col = "roi_idx_stats"
    if absolute:
        data_col = f"abs_{data_col}"
    
    if data_col not in idx_stats_df.columns:
        raise KeyError(f"Expected to find {data_col} in idx_stats_df columns.")

    for (line, plane), lp_df in idx_stats_df.groupby(["lines", "planes"]):
        li, pl, col, dash = plot_helper_fcts.get_line_plane_idxs(line, plane)
        sub_ax = ax[pl, li]

        sub_ax.axhline(
            y=0, ls=plot_helper_fcts.HDASH, c="k", lw=3.0, alpha=0.5, 
            zorder=-13
            )

        mouse_ns = ["any"]
        mouse_cols = [col]
        if by_mouse:
            mouse_ns = sorted(lp_df["mouse_ns"].unique())
            mouse_cols = plot_util.get_hex_color_range(
                len(mouse_ns), col=col, 
                interval=plot_helper_fcts.MOUSE_COL_INTERVAL
                )

        for mouse_n, mouse_col in zip(mouse_ns, mouse_cols):
            sub_df = lp_df
            if by_mouse:
                sub_df = lp_df.loc[lp_df["mouse_ns"] == mouse_n]
            
            sess_indices = []
            sub_sess_ns = []

            for sess_n in sess_ns:
                rows = sub_df.loc[sub_df["sess_ns"] == sess_n]
                if len(rows) == 1:
                    sess_indices.append(rows.index[0])
                    sub_sess_ns.append(sess_n)

            data = np.asarray([sub_df.loc[i, data_col] for i in sess_indices])

            # plot errorbars
            alpha = 0.6 if by_mouse else 0.8
            capsize = 8 if bootstr_err else None
            plot_util.plot_errorbars(
                sub_ax, data[:, 0], data[:, 1:].T, sub_sess_ns, color=mouse_col, 
                alpha=alpha, xticks="auto", line_dash=dash, capsize=capsize,
                )

    if between_sess_sig:
        if permpar is None:
            raise ValueError(
                "If 'between_sess_sig' is True, must provide permpar."
                )
        if by_mouse:
            raise NotImplementedError(
                "Plotting between session statistical signifiance is not "
                "implemented if 'by_mouse' if True."
                )

        seq_plots.add_between_sess_sig(
            ax, idx_stats_df, permpar, data_col=data_col
            )
    
    # Add plane, line info to plots
    sess_plot_util.format_linpla_subaxes(
        ax, datatype="roi", xticks=sess_ns, ylab="", kind="reg"
        )
    
    return ax
def plot_stim_data_df(stim_data_df, stimpar, permpar, figpar, pop_stats=True, 
                      title=None):
    """
    plot_stim_data_df(stim_data_df, stimpar, permpar, figpar)

    Plots stimulus comparison data.

    Required args:
        - stim_stats_df (pd.DataFrame):
            dataframe with one row per line/plane and one for all line/planes 
            together, and the basic sess_df columns, in addition to, 
            for each stimtype:
            - stimtype (list): absolute fractional change statistics (me, err)
            - raw_p_vals (float): uncorrected p-value for data differences 
                between stimulus types 
            - p_vals (float): p-value for data differences between stimulus 
                types, corrected for multiple comparisons and tails
        - stimpar (dict): 
            dictionary with keys of StimPar namedtuple
        - permpar (dict): 
            dictionary with keys of PermPar namedtuple
        - figpar (dict): 
            dictionary containing the following figure parameter dictionaries
            ["init"] (dict): dictionary with figure initialization parameters
            ["save"] (dict): dictionary with figure saving parameters
            ["dirs"] (dict): dictionary with additional figure parameters

    Optional args:
        - pop_stats (bool):
            if True, analyses are run on population statistics, and not 
            individual tracked ROIs
            default: True
        - title (str):
            plot title
            default: None
    
    Returns:
        - ax (2D array): 
            array of subplots 
            (does not include added subplot for all line/plane data together)
    """

    figpar = sess_plot_util.fig_init_linpla(figpar, kind="reg")

    figpar["init"]["subplot_wid"] = 2.1
    figpar["init"]["subplot_hei"] = 4.2
    figpar["init"]["gs"] = {"hspace": 0.20, "wspace": 0.3}
    figpar["init"]["sharey"] = "row"
    
    fig, ax = plot_util.init_fig(plot_helper_fcts.N_LINPLA, **figpar["init"])
    fig.suptitle(title, y=0.98, weight="bold")

    sub_ax_all = fig.add_axes([1.05, 0.11, 0.3, 0.77])

    stimtypes = stimpar["stimtype"][:] # deep copy

    # indicate bootstrapped error with wider capsize
    capsize = 8 if pop_stats else 6

    lp_data = []
    cols = []
    for (line, plane), lp_df in stim_data_df.groupby(["lines", "planes"]):
        x = [0, 1]
        data = np.vstack(
            [lp_df[stimtypes[0]].tolist(), lp_df[stimtypes[1]].tolist()]
            ).T
        y = data[0]
        err = data[1:]

        if line != "all" and plane != "all":
            li, pl, col, dash = plot_helper_fcts.get_line_plane_idxs(
                line, plane
                )
            alpha = 0.5
            sub_ax = ax[pl, li]
            lp_data.append(y)
            cols.append(col)
        else:
            col = plot_helper_fcts.NEARBLACK
            dash = None
            alpha = 0.2
            sub_ax = sub_ax_all
            sub_ax.set_title("all", fontweight="bold")
    
        plot_util.plot_bars(
            sub_ax, x, y=y, err=err, width=0.5, lw=None, alpha=alpha, 
            color=col, ls=dash, capsize=capsize
            )

    # add dots to the all subplot
    x_vals = np.asarray([-0.17, 0.25, -0.25, 0.17]) # to spread dots out
    lw = 4
    ms = 200
    for s, _ in enumerate(stimtypes):
        lp_stim_data = [data[s] for data in lp_data]
        sorter = np.argsort(lp_stim_data)
        for i, idx in enumerate(sorter):
            x_val = s + x_vals[i]
            # white behind
            sub_ax_all.scatter(
                x=x_val, y=lp_stim_data[idx], s=ms, linewidth=lw, alpha=0.8, 
                color="white", zorder=10
                )
            
            # colored dots
            sub_ax_all.scatter(
                x=x_val, y=lp_stim_data[idx], s=ms, alpha=0.6, linewidth=0, 
                color=cols[idx], zorder=11
                )
            
            # dot borders
            sub_ax_all.scatter(
                x=x_val, y=lp_stim_data[idx], s=ms, color="None", 
                edgecolor=cols[idx], linewidth=lw, alpha=1, zorder=12
                )

    # add between stim significance 
    add_between_stim_sig(ax, sub_ax_all, stim_data_df, permpar)

    # add plane, line info to plots
    sess_plot_util.format_linpla_subaxes(ax, datatype="roi", lines=None, 
        planes=["", ""], xticks=[0, 1], ylab="Absolute fractional change", 
        kind="reg", xlab=""
        )
    
    # adjust plot details
    stimtype_names = stimtypes[:]
    stimtype_names[stimtypes.index("visflow")] = "visual\nflow"
    for sub_ax in fig.axes:
        y_max = sub_ax.get_ylim()[1]
        sub_ax.set_ylim([0, y_max])
        sub_ax.set_xticks([0, 1])
        sub_ax.set_xticklabels(
            stimtypes, weight="bold", rotation=45, ha="right"
            )
        sub_ax.tick_params(axis="x", bottom=False)
    sub_ax_all.set_xlim(ax[0, 0].get_xlim())
        
    plot_util.set_interm_ticks(
        np.asarray(sub_ax_all), 4, axis="y", share=False, weight="bold"
        )

    return ax
Esempio n. 28
0
def plot_pupil_run_trace_stats(trace_df, analyspar, figpar, split="by_exp", 
                               title=None):
    """
    plot_pupil_run_trace_stats(trace_df, analyspar, figpar)

    Plots pupil and running trace statistics.

    Required args:
        - trace_df (pd.DataFrame):
            dataframe with one row per session number, and the following 
            columns, in addition to the basic sess_df columns: 
            - run_trace_stats (list): 
                running velocity trace stats (split x frames x stats (me, err))
            - run_time_values (list):
                values for each frame, in seconds
                (only 0 to stimpar.post, unless split is "by_exp")
            - pupil_trace_stats (list): 
                pupil diameter trace stats (split x frames x stats (me, err))
            - pupil_time_values (list):
                values for each frame, in seconds
                (only 0 to stimpar.post, unless split is "by_exp")    

        - analyspar (dict): 
            dictionary with keys of AnalysPar namedtuple
        - figpar (dict): 
            dictionary containing the following figure parameter dictionaries
            ["init"] (dict): dictionary with figure initialization parameters
            ["save"] (dict): dictionary with figure saving parameters
            ["dirs"] (dict): dictionary with additional figure parameters

    Optional args:
        - split (str):
            data split, e.g. "exp_lock", "unexp_lock", "stim_onset" or 
            "stim_offset"
            default: False
        - title (str):
            plot title
            default: None

    Returns:
        - ax (2D array): 
            array of subplots
    """

    if split != "by_exp":
        raise NotImplementedError("Only implemented split 'by_exp'.")

    if analyspar["scale"]:
        raise NotImplementedError(
            "Expected running and pupil data to not be scaled."
            )

    datatypes = ["run", "pupil"]

    figpar["init"]["subplot_wid"] = 4.2
    figpar["init"]["subplot_hei"] = 2.2
    figpar["init"]["gs"] = {"hspace": 0.3}
    figpar["init"]["ncols"] = 1
    figpar["init"]["sharey"] = False
    figpar["init"]["sharex"] = True

    fig, ax = plot_util.init_fig(len(datatypes), **figpar["init"])

    if title is not None:
        fig.suptitle(title, weight="bold", y=1.0)

    if len(trace_df) != 1:
        raise NotImplementedError(
            "Only implemented for a trace_df with one row."
            )
    row_idx = trace_df.index[0]

    exp_col = plot_util.LINCLAB_COLS["gray"]
    unexp_col = plot_util.LINCLAB_COLS["red"]
    for d, datatype in enumerate(datatypes):
        sub_ax = ax[d, 0]

        time_values = trace_df.loc[row_idx, f"{datatype}_time_values"]
        trace_stats = trace_df.loc[row_idx, f"{datatype}_trace_stats"]

        seq_plots.plot_traces(
            sub_ax, time_values, trace_stats, split=split, col=unexp_col, 
            lab=False, exp_col=exp_col, hline=False
            )
    
        if datatype == "run":
            ylabel = "Running\nvelocity\n(cm/s)"
        elif datatype == "pupil":
            ylabel = "Pupil\ndiameter\n(mm)"
        sub_ax.set_ylabel(ylabel, weight="bold")

   # fix x ticks and lims
    plot_util.set_interm_ticks(ax, 3, axis="x", fontweight="bold")
    xlims = [np.min(time_values), np.max(time_values)]
    if split != "by_exp":
        xlims = [-xlims[1], xlims[1]]
    sub_ax.set_xlim(xlims)
    sub_ax.set_xlabel("Time (s)", weight="bold")

    # expand y lims a bit and fix y ticks
    for sub_ax in ax.reshape(-1):
        plot_util.expand_lims(sub_ax, axis="y", prop=0.21)

    plot_util.set_interm_ticks(
        ax, 2, axis="y", share=False, weight="bold", update_ticks=True
        )

    return ax