Esempio n. 1
0
    def plot_rigidity(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.rigidity[0]).set_index("L")
        df2 = pd.read_pickle(self.rigidity[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.spectral_rigidity(L=L), color="#08FD4F", label="Poisson", ax=ax)
        sbn.lineplot(x=L, y=GOE.spectral_rigidity(L=L), color="#0066FF", label="GOE", ax=ax)

        ax.legend().set_visible(True)
        ax.set_title(f"{self.label}: Rigidity" 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.rigidity[0].stem}_{self.label}.png"
            fig.savefig(outfile, dpi=300)
            plt.close()
            print(f"Pooled rigidity plots saved to {relpath(outfile)}")
Esempio n. 2
0
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)
Esempio n. 3
0
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)
Esempio n. 4
0
    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)}")
Esempio n. 5
0
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()
Esempio n. 6
0
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()