def plot_levelvar(self, title: str = None, outdir: Path = None) -> None: # label is always g1_v_g2, we want "attention" to be orange, "nonattend" # to be black if self.label in ["rest_v_task", "nopain_v_pain", "control_v_control_pre", "park_pre_v_parkinsons"]: c1, c2 = "#000000", "#FD8208" elif self.label in [ "allpain_v_nopain", "allpain_v_duloxetine", "high_v_low", "control_pre_v_park_pre", "control_v_parkinsons", ]: c1, c2 = "#FD8208", "#000000" else: c1, c2 = "#EA00FF", "#FD8208" df1 = pd.read_pickle(self.levelvar[0]).set_index("L") df2 = pd.read_pickle(self.levelvar[1]).set_index("L") L = np.array(df1.index) _configure_sbn_style() fig: plt.Figure ax: plt.Axes fig, ax = plt.subplots() # plot curves for each group for col in df1: sbn.lineplot(x=L, y=df1[col], color=c1, alpha=0.05, ax=ax) for col in df2: sbn.lineplot(x=L, y=df2[col], color=c2, alpha=0.05, ax=ax) # plot bootstrapped means and CIs for each group boots = _percentile_boot(df1) sbn.lineplot(x=L, y=boots["mean"], color=c1, label=self.subgroup1, ax=ax) ax.fill_between(x=L, y1=boots["low"], y2=boots["high"], color=c1, alpha=0.3) boots = _percentile_boot(df2) sbn.lineplot(x=L, y=boots["mean"], color=c2, label=self.subgroup2, ax=ax) ax.fill_between(x=L, y1=boots["low"], y2=boots["high"], color=c2, alpha=0.3) # plot theoretically-expected curves # sbn.lineplot(x=L, y=Poisson.level_variance(L=L), color="#08FD4F", label="Poisson", ax=ax) sbn.lineplot(x=L, y=GOE.level_variance(L=L), color="#0066FF", label="GOE", ax=ax) ax.legend().set_visible(True) ax.set_title(f"{self.label}: Level Variance" if title is None else title) ax.set_xlabel("L") ax.set_ylabel("Σ²(L)", fontname="DejaVu Sans") fig.set_size_inches(w=8, h=8) if outdir is None: plt.show() plt.close() else: os.makedirs(outdir, exist_ok=True) outfile = outdir / f"{self.levelvar[0].stem}_{self.label}.png" plt.savefig(outfile, dpi=300) plt.close() print(f"Pooled levelvar plots saved to {relpath(outfile)}")
def plot_pred_rigidity( args: Any, dataset_name: str, comparison: str, unfold: List[int] = [5, 7, 9, 11, 13], ensembles: bool = True, silent: bool = False, force: bool = False, ) -> None: global ARGS # ARGS.fullpre = True for trim_idx in ["(1,-1)", "(1,-20)"]: ARGS.trim = trim_idx all_pairs = [] for normalize in [False]: args.normalize = normalize for degree in unfold: ARGS.unfold["degree"] = degree pairings = Pairings(args, dataset_name) pairing = list(filter(lambda p: p.label == comparison, pairings.pairs)) if len(pairing) != 1: raise ValueError("Too many pairings, something is wrong.") all_pairs.append(pairing[0]) g1, _, g2 = all_pairs[0].label.split("_") # groupnames fig: plt.Figure fig, axes = plt.subplots(nrows=1, ncols=len(all_pairs), sharex=True) for i, pair in enumerate(all_pairs): ax: plt.Axes = axes.flat[i] df1 = pd.read_pickle(pair.rigidity[0]).set_index("L") df2 = pd.read_pickle(pair.rigidity[1]).set_index("L") boots1 = _percentile_boot(df1) boots2 = _percentile_boot(df2) sbn.lineplot(x=df1.index, y=boots1["mean"], color="#FD8208", label=g1, ax=ax) ax.fill_between(x=df1.index, y1=boots1["low"], y2=boots1["high"], color="#FD8208", alpha=0.3) sbn.lineplot(x=df2.index, y=boots2["mean"], color="#000000", label=g2, ax=ax) ax.fill_between(x=df2.index, y1=boots2["low"], y2=boots2["high"], color="#000000", alpha=0.3) if ensembles: L = df1.index poisson = GDE.spectral_rigidity(L=L) goe = GOE.spectral_rigidity(L=L) sbn.lineplot(x=L, y=poisson, color="#08FD4F", label="Poisson", ax=ax) sbn.lineplot(x=L, y=goe, color="#0066FF", label="GOE", ax=ax) ax.legend().set_visible(False) ax.set_title(f"Unfolding Degree {unfold[i]}") ax.set_xlabel("") ax.set_ylabel("") axes.flat[-1].legend().set_visible(True) fig.text(0.5, 0.04, "L", ha="center", va="center") # xlabel fig.text( 0.03, 0.5, "∆₃(L)", ha="center", va="center", rotation="vertical", fontdict={"fontname": "DejaVu Sans"} ) # ylabel fig.set_size_inches(w=12, h=3) fig.subplots_adjust(top=0.83, bottom=0.14, left=0.085, right=0.955, hspace=0.2, wspace=0.2) plt.suptitle(f"{dataset_name} {ARGS.trim} - Spectral Rigidity") plt.show(block=False)
def _brody_fit( unfolded: ndarray, method: str = "spacing", title: str = "Brody distribution fit", mode: PlotMode = "block", outfile: Path = None, save_dpi: int = None, ensembles: List[str] = ["goe", "poisson"], bins: int = 50, kde: bool = True, trim: float = 5.0, trim_kde: bool = False, kde_bw: Union[float, str] = "scott", ) -> PlotResult: _configure_sbn_style() fig, axes = plt.subplots(1, 2, sharex=True, sharey=True) _spacings( unfolded=unfolded, bins=bins, kde=kde, trim=trim, trim_kde=trim_kde, kde_bw=kde_bw, brody=True, brody_fit=method, title="Brody distribution fit: density", mode="return", ensembles=ensembles, fig=fig, axes=axes[0], ) spacings = np.diff(unfolded) s = spacings[spacings > 0] res = brody_fit_evaluate(s, method=method) x = res["spacings"] ecdf, bcdf = res["ecdf"], res["brody_cdf"] ax = axes.flat[1] ax_e = ax.plot(x, ecdf) plt.setp(ax_e, label="Empirical CDF") ax_b = ax.plot(x, bcdf) plt.setp(ax_b, label="Brody CDF", color="#9d0000", linestyle="--") if "goe" in ensembles: ax_goe = ax.plot(x, GOE.nnsd_cdf(spacings=x)) plt.setp(ax_goe, label="GOE", color="#FD8208") if "poisson" in ensembles: ax_poi = ax.plot(x, Poisson.nnsd_cdf(spacings=x)) plt.setp(ax_poi, label="Poisson", color="#08FD4F") ax.legend().set_visible(True) ax.set_title("Brody distribution fit: cumulative density") return _handle_plot_mode(mode, fig, axes, outfile, save_dpi)
def plot_pred_nnsd( args: Any, dataset_name: str, comparison: str, unfold: List[int] = [5, 7, 9, 11, 13], ensembles: bool = True, trim: float = 3.0, silent: bool = False, force: bool = False, ) -> None: global ARGS # ARGS.fullpre = True BINS = np.linspace(0, trim, 20) # for trim_idx in ["(1,-1)", "(1,-20)"]: for trim_idx in ["(1,-1)"]: ARGS.trim = trim_idx all_pairs = [] for normalize in [False]: args.normalize = normalize for degree in unfold: ARGS.unfold["degree"] = degree pairings = Pairings(args, dataset_name) pairing = list( filter(lambda p: p.label == comparison, pairings.pairs)) if len(pairing) != 1: raise ValueError("Too many pairings, something is wrong.") all_pairs.append(pairing[0]) g1, _, g2 = all_pairs[0].label.split("_") # groupnames fig: plt.Figure fig, axes = plt.subplots(nrows=1, ncols=len(all_pairs), sharex=True, squeeze=False) for i, (pair, unfold_degree) in enumerate(zip(all_pairs, unfold)): ax: plt.Axes = axes.flat[i] eigs1, eigs2 = pair.eigs1, pair.eigs2 unfold_args = {**ARGS.unfold, **dict(degree=unfold_degree)} unf1 = [ Eigenvalues(np.load(e)).unfold(**unfold_args) for e in eigs1 ] unf2 = [ Eigenvalues(np.load(e)).unfold(**unfold_args) for e in eigs2 ] alpha1, alpha2 = 1 / len(unf1), 1 / len(unf2) # alpha_adj = 0.02 # good for just plotting hists, no brody alpha_adj = 0.00 alpha1 += alpha_adj alpha2 += alpha_adj for j, unf in enumerate(unf1): spacings = unf.spacings if trim > 0.0: spacings = spacings[spacings <= trim] beta = fit_brody_mle(spacings) brody = brody_dist(spacings, beta) # Generate expected distributions for classical ensembles sbn.distplot( spacings, norm_hist=True, bins=BINS, kde=False, # label=g1 if j == 0 else None, axlabel="spacing (s)", color="#FD8208", # hist_kws={"alpha": alpha1, "histtype": "step", "linewidth": 0.5}, hist_kws={"alpha": alpha1}, # kde_kws={"alpha": alpha1, "color":"#FD8208"}, ax=ax, ) sbn.lineplot(x=spacings, y=brody, color="#FD8208", ax=ax, alpha=0.9, label=g1 if j == 0 else None, linewidth=0.5) for j, unf in enumerate(unf2): spacings = unf.spacings if trim > 0.0: spacings = spacings[spacings <= trim] beta = fit_brody_mle(spacings) brody = brody_dist(spacings, beta) sbn.distplot( spacings, norm_hist=True, bins=BINS, # doane kde=False, # label=g2 if j == 0 else None, axlabel="spacing (s)", color="#000000", # hist_kws={"alpha": alpha2, "histtype": "step", "linewidth": 0.5}, hist_kws={"alpha": alpha2}, # kde_kws={"alpha": alpha2, "color":"#000000"}, ax=ax, ) sbn.lineplot(x=spacings, y=brody, color="#000000", ax=ax, alpha=0.9, label=g2 if j == 0 else None, linewidth=0.5) if ensembles: s = np.linspace(0, trim, 10000) poisson = GDE.nnsd(spacings=s) goe = GOE.nnsd(spacings=s) sbn.lineplot(x=s, y=poisson, color="#08FD4F", label="Poisson", ax=ax, alpha=0.5) sbn.lineplot(x=s, y=goe, color="#0066FF", label="GOE", ax=ax, alpha=0.5) ax.legend().set_visible(False) ax.set_title(f"Unfolding Degree {unfold[i]}") ax.set_xlabel("") ax.set_ylabel("") axes.flat[0].legend().set_visible(True) fig.text(0.5, 0.04, "spacing (s)", ha="center", va="center") # xlabel fig.text(0.03, 0.5, "p(s)", ha="center", va="center", rotation="vertical") # ylabel fig.set_size_inches(w=7, h=1.5) # TMI full-page max width is 7 inches # fig.set_size_inches(w=3.5, h=3.5) # TMI half-page max width is 3.5 inches fig.subplots_adjust(top=0.83, bottom=0.2, left=0.075, right=0.955, hspace=0.2, wspace=0.23) # fontdic = {"fontname": "Arial", "fontsize": 10.0} # fig.suptitle(f"{dataset_name} {ARGS.trim} - NNSD", fontdict=fontdic) make_plot(fig, show=False, fmt="png", fignum="9")
def _next_spacings( unfolded: ndarray, bins: int = 50, kde: bool = True, trim: float = 0.0, trim_kde: bool = False, brody: bool = False, brody_fit: str = "spacing", title: str = "next Nearest-Neigbors Spacing Distribution", mode: PlotMode = "block", outfile: Path = None, ensembles: List[str] = ["goe", "poisson"], fig: Figure = None, axes: Axes = None, ) -> PlotResult: """Plots a histogram of the next Nearest-Neighbors Spacing Distribution Parameters ---------- unfolded: ndarray the unfolded eigenvalues bins: int the number of (equal-sized) bins to display and use for the histogram kde: boolean If False (default), do not display a kernel density estimate. If true, use [statsmodels.nonparametric.kde.KDEUnivariate](https://www.statsmodels.org/stable/generated/statsmodels.nonparametric.kde.KDEUnivariate.html#statsmodels.nonparametric.kde.KDEUnivariate) with arguments {kernel="gau", bw="scott", cut=0} to compute and display the kde trim: float If True, only use spacings <= `trim` for computing the KDE and plotting. Useful for when large spacings distort the histogram. trim_kde: bool If True, fit the KDE using only spacings <= `trim`. Otherwise, fit the KDE using all available spacings. brody: bool If True, compute the best-fitting Brody distribution via MLE, and plot that distribution. brody_fit: "spacing" | "mle" Method for parametric distribution fitting of the Brody distribution to the data. If "spacing", use [maximum spacing estimation](https://en.wikipedia.org/wiki/Maximum_spacing_estimation). If "mle", use the maximum likelihood method. The default is "spacing", as this may be preferable for the J-shape of the Brody distribution. title: string The plot title string mode: "block" | "noblock" | "save" | "return" If "block", call plot.plot() and display plot in a blocking fashion. If "noblock", attempt to generate plot in nonblocking fashion. If "save", save plot to pathlib Path specified in `outfile` argument If "return", return (fig, axes), the matplotlib figure and axes object for modification. outfile: Path If mode="save", save generated plot to Path specified in `outfile` argument. Intermediate directories will be created if needed. ensembles: List["poisson", "goe"] Which ensembles to display the expected next-NNSD curves for. fig: Figure If provided with `axes`, configure plotting with the provided `fig` object instead of creating a new figure. Useful for creating subplots. axes: Axes If provided with `fig`, plot to the provided `axes` object. Useful for creating subplots. Returns ------- (fig, axes): (Figure, Axes) The handles to the matplotlib objects, only if `mode` is "return". """ _configure_sbn_style() fig, axes = _setup_plotting(fig, axes) _spacings = np.sort((unfolded[2:] - unfolded[:-2]) / 2) all_spacings = np.copy(_spacings) if trim > 0.0: _spacings = _spacings[_spacings <= trim] # Generate expected distributions for classical ensembles s_min, s_max = _spacings.min(), _spacings.max() s = np.linspace(s_min, s_max, 10000) axes = sbn.distplot( _spacings, norm_hist=True, bins=bins, # doane kde=False, label="next NNSD", axlabel="spacing (s_2)", color="black", ax=axes, ) if kde is True: if trim_kde: _kde_plot(_spacings, s, axes) else: _kde_plot(all_spacings, s, axes) if brody is True: beta = fit_brody(_spacings) brody_vals = brody_dist(s, beta=beta) brod = axes.plot(s, brody_vals, label="Brody dist.") plt.setp(brod, color="#9d0000", linestyle="--") if "goe" in ensembles: goe = GOE.nnnsd(spacings=s) goe = axes.plot(s, goe, label="Gaussian Orthogonal") plt.setp(goe, color="#FD8208") if "poisson" in ensembles: poisson = Poisson.nnnsd(spacings=s) poisson = axes.plot(s, poisson, label="Poisson") plt.setp(poisson, color="#08FD4F") # adjusting the right bounds can be necessary when / if there are # many large eigenvalue spacings axes.set_ylim(top=2.0, bottom=0) axes.set_xlim(left=0, right=2.5) axes.set(title=title, ylabel="Density p(s)") axes.legend().set_visible(True) return _handle_plot_mode(mode, fig, axes, outfile)
def plot_nnsd( self, trim_args: str, unfold_args: dict, n_bins: int = 20, max_spacing: float = 5.0, title: str = None, outdir: Path = None, ) -> None: # label is always g1_v_g2, we want "attention" to be orange, "nonattend" # to be black if self.label in ["rest_v_task", "nopain_v_pain", "control_v_control_pre", "park_pre_v_parkinsons"]: c1, c2 = "#000000", "#FD8208" elif self.label in [ "allpain_v_nopain", "allpain_v_duloxetine", "high_v_low", "control_pre_v_park_pre", "control_v_parkinsons", ]: c1, c2 = "#FD8208", "#000000" elif self.label in ["control_pre_v_parkinsons", "control_v_park_pre"]: return # meaningless comparison else: c1, c2 = "#EA00FF", "#FD8208" unfolded1 = [] for path in self.eigs1: vals = np.load(path) if trim_args in ["(1,:)", "", "(0,:)"]: vals = vals[1:] # smallest eigenvalue is always spurious here else: low, high = eval(trim_args) vals = vals[low:high] unfolded1.append(np.sort(Eigenvalues(vals).unfold(**unfold_args).vals)) unfolded2 = [] for path in self.eigs2: vals = np.load(path) if trim_args in ["(1,:)", "", "(0,:)"]: vals = vals[1:] # smallest eigenvalue is always spurious here else: low, high = eval(trim_args) vals = vals[low:high] unfolded2.append(np.sort(Eigenvalues(vals).unfold(**unfold_args).vals)) spacings1 = [np.diff(unfolded) for unfolded in unfolded1] spacings2 = [np.diff(unfolded) for unfolded in unfolded2] # trim largest histogram skewing spacing spacings1 = [spacings[spacings < max_spacing] for spacings in spacings1] spacings2 = [spacings[spacings < max_spacing] for spacings in spacings2] mean_brody1 = np.round(float(pd.read_pickle(self.brody[0]).mean(axis=1)), 2) mean_brody2 = np.round(float(pd.read_pickle(self.brody[1]).mean(axis=1)), 2) _configure_sbn_style() fig, axes = plt.subplots(ncols=2, sharex=True, sharey=True) # plot curves for each group for spacings in spacings1: sbn.distplot( spacings, norm_hist=True, bins=n_bins, kde=True, axlabel="spacing (s)", color=c1, kde_kws={"alpha": np.max([0.1, 1 / len(spacings1)])}, hist_kws={"alpha": np.max([0.1, 1 / len(spacings1)])}, ax=axes[0], ) for spacings in spacings2: sbn.distplot( spacings, norm_hist=True, bins=n_bins, kde=True, axlabel="spacing (s)", color=c2, kde_kws={"alpha": np.max([0.1, 1 / len(spacings1)])}, hist_kws={"alpha": np.max([0.1, 1 / len(spacings2)])}, ax=axes[1], ) # plot bootstrapped means and CIs for each group # boots = _percentile_boot(df1) # sbn.lineplot(x=L, y=boots["mean"], color=c1, label=self.subgroup1, ax=ax) # ax.fill_between(x=L, y1=boots["low"], y2=boots["high"], color=c1, alpha=0.3) # boots = _percentile_boot(df2) # sbn.lineplot(x=L, y=boots["mean"], color=c2, label=self.subgroup2, ax=ax) # ax.fill_between(x=L, y1=boots["low"], y2=boots["high"], color=c2, alpha=0.3) # plot theoretically-expected curves s = np.linspace(0, max_spacing, 5000) sbn.lineplot(x=s, y=Poisson.nnsd(spacings=s), color="#08FD4F", label="Poisson", ax=axes[0]) sbn.lineplot(x=s, y=Poisson.nnsd(spacings=s), color="#08FD4F", label="Poisson", ax=axes[1]) sbn.lineplot(x=s, y=GOE.nnsd(spacings=s), color="#0066FF", label="GOE", ax=axes[0]) sbn.lineplot(x=s, y=GOE.nnsd(spacings=s), color="#0066FF", label="GOE", ax=axes[1]) # ensure all plots have identical axes axes[0].set_ylim(top=2.0) axes[1].set_ylim(top=2.0) # ax.legend().set_visible(True) fig.suptitle(f"{self.label}: NNSD" if title is None else title) for i, ax in enumerate(axes): ax.set_xlabel("spacing (s)") ax.set_ylabel("density p(s)", fontname="DejaVu Sans") ax.set_title( f"{self.subgroup1 if i == 0 else self.subgroup2} (<β> = {mean_brody1 if i == 0 else mean_brody2})" ) if outdir is None: plt.show() plt.close() else: os.makedirs(outdir, exist_ok=True) prefix = _prefix(trim_args, unfold_args) outfile = outdir / f"{prefix}_{self.label}_nnsd.png" fig.set_size_inches(10, 5) plt.savefig(outfile, dpi=300) plt.close() print(f"Pooled nnsd plot saved to {relpath(outfile)}")
def plot_subject_levelvar(summaries: Dict[str, Dict[Observable, Path]], args: Any, outfile: Path = None) -> None: sbn.set_context("paper") sbn.set_style("ticks", {"ytick.left": False}) c1 = "#000000" fig, axes = plt.subplots(ncols=6, nrows=4, sharex=True, sharey=True) top = [] for i, (subj_id, summary) in enumerate(summaries.items()): ax: Axes = axes.flat[i] label = f"subj-{subj_id}" levelvars = pd.read_pickle(summary["levelvar"]).set_index("L") rename_columns(levelvars) L = levelvars.index.to_numpy(dtype=float).ravel() for col in levelvars: sbn.lineplot(x=L, y=levelvars[col], color=c1, alpha=1.0 / 8.0, ax=ax) # sbn.lineplot(x=L, y=levelvars[col], label=f"run-{col}", color=c1, alpha=1.0/8.0, ax=ax) boots = _percentile_boot(levelvars, B=2000) top.append(np.max(boots["mean"])) # sbn.lineplot(x=L, y=boots["mean"], color=c1, label=label) sbn.lineplot(x=L, y=boots["mean"], color=c1, ax=ax) ax.fill_between(x=L, y1=boots["low"], y2=boots["high"], color=c1, alpha=0.3) # plot theoretically-expected curves sbn.lineplot(x=L, y=Poisson.level_variance(L=L), color="#08FD4F", label="Poisson", ax=ax) sbn.lineplot(x=L, y=GOE.level_variance(L=L), color="#0066FF", label="GOE", ax=ax) ax.set_ylabel("") ax.set_title(label) ax.legend(frameon=False, framealpha=0) plt.setp(ax.get_legend().get_texts(), fontsize="6") if i != 0: handle, labels = ax.get_legend_handles_labels() ax._remove_legend(handle) ticks = [1, 5, 10] ax.set_xticks(ticks) ax.set_xticklabels(ticks, rotation=45, horizontalalignment="right") plt.setp(axes, ylim=(0.0, np.percentile(top, 90))) # better use of space fig.suptitle("Per-Subject Task Level Number Variances") fig.set_size_inches(10, 6) fig.subplots_adjust(left=0.13, bottom=0.15, wspace=0.1, hspace=0.35) fig.text(0.5, 0.04, "L", ha="center", va="center") # xlabel fig.text(0.05, 0.5, "Σ²(L)", ha="center", va="center", rotation="vertical", fontname="DejaVu Sans") # ylabel if outfile is None: plt.show() else: fig.savefig(str(outfile.resolve()), dpi=300) print(f"Saved levelvar plot to {str(outfile.relative_to(DATA_ROOT))}") plt.close()
def plot_subject_nnsd(args: Any, n_bins: int = 20, outfile: Path = None) -> None: subjects = get_subjects_dict() sbn.set_context("paper") sbn.set_style("ticks", {"ytick.left": False}) c1 = "#000000" fig, axes = plt.subplots(ncols=6, nrows=4, sharex=True, sharey=True) pbar = tqdm(desc="NNSD", total=8 * len(subjects)) for i, (subj_id, subject) in enumerate(subjects.items()): ax: Axes = axes.flat[i] eigpaths = subject["runs"] unfoldeds = [] for path in eigpaths: vals = np.load(path) if args.trim in ["(1,:)", "", "(0,:)"]: vals = vals[1:] # smallest eigenvalue is always spurious here else: low, high = eval(args.trim) vals = vals[low:high] unfoldeds.append( np.sort(Eigenvalues(vals).unfold(**args.unfold).vals)) all_spacings = [np.diff(unfolded) for unfolded in unfoldeds] kde_gridsize = 1000 kdes = np.empty([len(all_spacings), kde_gridsize], dtype=float) s = np.linspace(0, 3, kde_gridsize) bins = np.linspace(0, 3, n_bins + 1) for j, spacings in enumerate(all_spacings): sbn.distplot( spacings[(spacings > 0) & (spacings <= 3)], norm_hist=True, bins=bins, kde=False, color=c1, hist_kws={ "alpha": 1.0 / 8, "range": (0.0, 3.0) }, ax=ax, ) kde = _kde(spacings, grid=s) kdes[j, :] = kde pbar.update() # sbn.lineplot(x=s, y=kde, color=c1, alpha=1.0 / 8, ax=ax) # sbn.lineplot(x=s, y=kdes.mean(axis=0), color="#9d0000", label="Mean KDE", ax=ax) sbn.lineplot(x=s, y=kdes.mean(axis=0), color=c1, label="Mean KDE", ax=ax) sbn.lineplot(x=s, y=Poisson.nnsd(spacings=s), color="#08FD4F", label="Poisson", ax=ax) sbn.lineplot(x=s, y=GOE.nnsd(spacings=s), color="#0066FF", label="GOE", ax=ax) ax.set_title(f"subj-{subj_id}") ax.set_ylabel("") ax.legend(frameon=False, framealpha=0) plt.setp(ax.get_legend().get_texts(), fontsize="6") # if i != 0: handle, labels = ax.get_legend_handles_labels() ax._remove_legend(handle) ticks = [0, 1, 2, 3] ax.set_xticks(ticks) # ax.set_xticklabels(ticks, rotation=45, horizontalalignment="right") ax.set_xticklabels(ticks) pbar.close() fig.suptitle("Per-Subject NNSD") fig.set_size_inches(10, 6) fig.subplots_adjust(left=0.13, bottom=0.15, wspace=0.1, hspace=0.35) fig.text(0.5, 0.04, "spacing (s)", ha="center", va="center") # xlabel fig.text(0.05, 0.5, "density p(s)", ha="center", va="center", rotation="vertical") # ylabel plt.setp(axes, xlim=(0.0, 3.0), ylim=(0.0, 1.2)) # better use of space handles, labels = axes.flat[-1].get_legend_handles_labels() fig.legend(handles, labels, loc="center right", frameon=False, framealpha=0, fontsize="8") if outfile is None: plt.show() else: fig.savefig(str(outfile.resolve()), dpi=300) print(f"Saved NNSD plot to {str(outfile.relative_to(DATA_ROOT))}") plt.close()