Пример #1
0
def find_current_run_id(subject_id, session_id, kind, bids_root=None):
    """

    Parameters
    ----------
    subject_id :
    session_id :
    kind :
    bids_root :

    Returns
    -------

    """
    if bids_root is None:
        bids_root = _get_bidsroot_path()
    subdir = os.path.join(bids_root, "sub-" + subject_id)
    sesdir = os.path.join(subdir, "ses-" + session_id)
    kinddir = os.path.join(sesdir, kind)
    if not os.path.exists(subdir):
        warn("new subject")
        return "01"
    elif not os.path.exists(sesdir):
        warn("new session")
        return "01"
    elif not os.path.exists(kinddir):
        warn("new kind")
        return "01"
    else:
        runs = [f for f in os.listdir(kinddir) if f.endswith(".edf")]
        return str(len(runs) + 1).zfill(2)
Пример #2
0
def annotate_bad_chs(ctx, subject_id, chs, session_id, acquisition_id):
    """
    Annotate (remove) channels from analysis.

    Command Format:
    ez annotate_bad_chs --subject_id <subject_id> --chs <chs> --session_id <session_id>
    --acquisition_id <acquisition_id>

    """
    clear_screen()
    input_params = {
        "subject_id": subject_id,
        "chs": chs,
        "session_id": session_id,
        "acquisition_id": acquisition_id,
    }
    _check_annotatechannelfunc_params(input_params, "annotate_bad_chs")
    logger.info(
        f"ez annotate_bad_chs with subject_id: {subject_id}, chs: {chs}, "
        f"session_id: {session_id}, and acquisition_id: {acquisition_id}")
    bids_root = Path(_get_bidsroot_path())
    if acquisition_id is None:
        acquisition_ids = ["ecog", "seeg"]
        # _get_subject_acquisitions(bids_root, subject_id, session_id)
    else:
        acquisition_ids = [acquisition_id]

    for acquisition_id in acquisition_ids:
        bad_channels = channels_to_list(chs)
        add_bad_chs(bids_root, subject_id, session_id, acquisition_id,
                    bad_channels)
Пример #3
0
def _check_annotatechannelfunc_params(annotate_params, function):
    """Check channel annotation (view, write, read) function parameters and output user-friendly text."""
    bids_root = Path(_get_bidsroot_path())

    subject_id = annotate_params["subject_id"]
    session_id = annotate_params["session_id"]
    acquisition_id = annotate_params["acquisition_id"]

    subject_ids = [
        os.path.basename(x).split("-")[-1] for x in bids_root.glob("*")
        if x.is_dir() if "sub" in x.as_posix()
    ]
    if subject_id not in subject_ids:
        message_str = (f"Subject {subject_id} you passed in "
                       f"is not a valid subject in your subject pool. "
                       f"Here are the current subjects {subject_ids}.")
        error_msg = f"ez {function}: Subject {subject_id} is not a valid subject"
        logger.error(error_msg)
        counted("CLI_exceptions")
        raise CLI_Input_Error(message_str)

    if function is not "view_bad_chs":
        chs = annotate_params["chs"]
        if chs is None:
            logger.error(f"ez {function}: Must pass in channels to annotate.")
            counted("CLI_exceptions")
            raise CLI_Input_Error(
                f"ez {function}: Must pass in channels to annotate.")
Пример #4
0
def view_bad_chs(ctx, subject_id, session_id, acquisition_id):
    """Print bad channels for a specific subject.

    Command Format:
    ez view_bad_chs --subject_id <subject_id> --session_id <session_id> --acquisition_id <acquisition_id>

    """
    clear_screen()
    input_params = {
        "subject_id": subject_id,
        "session_id": session_id,
        "acquisition_id": acquisition_id,
    }
    _check_annotatechannelfunc_params(input_params, "view_bad_chs")
    logger.info(
        f"ez view_bad_chs with subject_id: {subject_id}, "
        f"session_id: {session_id}, and acquisition_id: {acquisition_id}")
    bids_root = Path(_get_bidsroot_path())
    print_channel_status(bids_root, subject_id, session_id, acquisition_id)
Пример #5
0
def _check_metadatafunc_params(run_params, function):
    """Checks metadata or pat-summary function parameters and outputs user-friendly text."""
    # subject_ids = layout.get_subjects()
    # faster using os.glob if too many subjects
    bids_root = Path(_get_bidsroot_path())

    # Get subject_id from dict
    subject_id = run_params["subject_id"]
    # subject_id of None is allowed for these commands
    if subject_id is None:
        pass
    # if not none, check if a valid subject
    subject_ids = [
        os.path.basename(x).split("-")[-1] for x in bids_root.glob("*")
        if x.is_dir() if "sub" in x.as_posix()
    ]
    if subject_id not in subject_ids and subject_id is not "all":
        message_str = (f"Subject {subject_id} you passed in "
                       f"is not a valid subject in your subject pool. "
                       f"Here are the current subjects {subject_ids}.")
        error_msg = f"ez {function}: Subject {subject_id} is not a valid subject"
        logger.error(error_msg)
        counted("CLI_exceptions")
        raise CLI_Input_Error(message_str)
Пример #6
0
def metadata(ctx, toggle_options, display_cols, clear=True):
    """
    Display (filtered) metadata to the terminal.

    Metadata needs no arguments, unless you wish to see a subset of the entire patient set. When this is desired, use
    the toggle options. For instance, the below command will display only male subjects over age 20:

        ez metadata 'sex=M' 'age>20'

    """
    if clear:
        clear_screen()

    baseBids = BaseBids(_get_bidsroot_path())
    participants_path = baseBids.participants_tsv_fpath
    dateparse = lambda x: pd.datetime.strptime(x, "%m/%d/%y")
    participants_df = pd.read_csv(participants_path,
                                  sep="\t",
                                  parse_dates=True,
                                  date_parser=dateparse)

    # Print out the column names only
    if display_cols:
        click.echo(f"Available columns and types: \n {participants_df.dtypes}")
        return

    participants_df, cols, bools, keys = filter_metadata(
        participants_df, toggle_options)
    toggle_count = len(cols)

    sub_ids = participants_df["participant_id"]
    subject_ids = []
    for sub in sub_ids:
        sub_id = sub.split("-")[1]
        subject_ids.append(sub_id)

    # display meessage and logger output
    click.echo("Displaying Subjects: " + ", ".join(subject_ids))
    info_msg = f"{toggle_count} filters: {', '.join(''.join(x) for x in zip(cols, bools, keys))}"
    logger.info(f"ez metadata with {info_msg}")

    # define layout using pybids
    layout = BIDSLayout(_get_bidsroot_path())
    # subject_ids = layout.get_subjects()
    # faster using os.glob if too many subjects
    bids_root = Path(_get_bidsroot_path())

    # for each subject, display metadata
    # query for sessions, tasks and acquisitions before run_ids
    for ind, subject in enumerate(subject_ids):
        subj_bids_parser = BidsParser(bids_root, subject)
        scans_fpaths = subj_bids_parser.get_scans_fpaths()
        acquisition_ids = subj_bids_parser.get_acquisitions()

        # display summary for each acquisition
        for acquisition in acquisition_ids:
            # get the file paths to datasets
            _fpaths = [
                x.as_posix()
                for x in Path(bids_root / f"sub-{subject}").rglob("*.vhdr")
            ]
            _fpaths = [x for x in _fpaths if acquisition in x]

            # extract run
            run_ids = [_extract_run(x) for x in _fpaths]
            run_ids = ",".join(run_ids)

            # display message to user about subject, their metadata, acquisition and run_ids
            message_str = (
                f"Subject ID: {subject} - {', '.join([x + ': ' + str(participants_df.at[ind + 1, x]) for x in cols])} \n"
                f"{acquisition} run_ids: {run_ids} \n"
                f"Here is a table of each dataset:")
            click.echo(_linebreak(message_str))
            click.echo(message_str)

            # display summary for all datasets with this acquisition
            for scans_fpath in scans_fpaths:
                pd.options.display.max_colwidth = 90
                # read in scans.tsv for each subject
                scans_tsv = _from_tsv(scans_fpath)
                scans_df = pd.DataFrame.from_dict(scans_tsv)
                acquisition_scans = scans_df["filename"].str.contains(
                    acquisition)
                scans_df = scans_df[acquisition_scans]
                click.echo(scans_df)
Пример #7
0
def pat_summary(ctx, subject_id, clear=True):
    """
    Display the summary data for a certain patient, or all patients.

    If subject_id not specified, or listed as all, will simply print metadata again.

    Command Format:
    ez pat-summary --subject_id <subject_id>

    """
    if clear:
        clear_screen()

    summary_params = {"subject_id": subject_id}
    _check_metadatafunc_params(summary_params, "pat-summary")
    logger.info(f"ez pat-summary with subject_id: {subject_id}")

    # faster using os.glob if too many subjects - compared pybids
    bids_root = Path(_get_bidsroot_path())

    # read in participants.tsv file and display to user
    bids_parser = BaseBids(bids_root)
    participants_tsv = _from_tsv(bids_parser.participants_tsv_fpath)
    participants_df = pd.DataFrame.from_dict(participants_tsv)

    # summarize all patients
    if subject_id == "all":
        click.echo("All Subjects Summary:")
        click.echo(participants_df)

    # summarize a specific patient
    if subject_id is not None and subject_id != "all":
        # set display options for the pandas dataframes
        pd.options.display.max_columns = None
        pd.options.display.max_rows = None
        pd.options.display.max_colwidth = 500

        # find the location of the scans file
        subj_bids_parser = BidsParser(bids_root, subject_id)
        scans_fpaths = subj_bids_parser.get_scans_fpaths()

        # for each set of scans files, print out summary data
        for i, scans_fpath in enumerate(scans_fpaths):
            # read in scans.tsv for each subject
            scans_tsv = _from_tsv(scans_fpath)
            # get the scans sidecar dictionaries for eeg and ieeg data
            eeg_dict, ieeg_dict = split_dict(scans_tsv)

            # load in files and their corresponding sidecar info
            # and add it to the summary dictionaries
            _populate_summary_dict(eeg_dict, eeg_dict["filename"], bids_root)
            _populate_summary_dict(ieeg_dict, ieeg_dict["filename"], bids_root)

        # summarize a specific subject
        participants_df = participants_df.loc[participants_df["participant_id"]
                                              == f"sub-{subject_id}"]
        click.echo(f"Subject {subject_id} Summary:")
        click.echo(participants_df)

        message_str = "\n\nSummary of Snapshots for %s" % subject_id
        click.echo(message_str)
        click.echo(_linebreak(message_str))

        ###################################################
        # Display the summary for either EEG,  or iEEG data.
        ###################################################
        if eeg_dict["filename"] == []:
            message_str = "No scalp EEG data to summarize."
            click.echo(message_str)
            click.echo(_linebreak(message_str))
        else:
            message_str = "EEG data summary."
            click.echo(message_str)
            click.echo(_linebreak(message_str))
            eeg_df = pd.DataFrame.from_dict(eeg_dict)
            click.echo(eeg_df)

        if ieeg_dict["filename"] == []:
            message_str = "No iEEG data to summarize."
            click.echo(message_str)
            click.echo(_linebreak(message_str))
        else:
            message_str = "iEEG data summary."
            click.echo(message_str)
            click.echo(_linebreak(message_str))
            ieeg_df = pd.DataFrame.from_dict(ieeg_dict)
            click.echo(ieeg_df)
Пример #8
0
def run(
    ctx,
    subject_id,
    session_id,
    task_id,
    acquisition_id,
    run_id,
    reference,
    overwrite,
    colorblind,
    clear=True,
):
    """
    Run the fragility analysis on a given subject's EEG snapshot.

    subject_id, session_id, and run_id are required.

    Command Format:
    ez run --subject_id <subject_id> --session_id <session_id> --run_id <run_id>

    """
    # Prompt for arguments not passed
    if clear:
        clear_screen()

    # perform a dynamic check on parameters passed in
    # to give user an insightful feedback
    run_params = {
        "subject_id": subject_id,
        "session_id": session_id,
        "acquisition_id": acquisition_id,
        "run_id": run_id,
    }
    _check_runfunc_params(run_params, "run")
    logger.info(
        f"ez run with subject_id: {subject_id}, session_id: {session_id}, task_id: {task_id},"
        f"acquisition_id: {acquisition_id}, run_id: {run_id}, reference: {reference}, "
        f"overwrite: {overwrite}, colorblind: {colorblind}")

    # determine kind from acquisition.
    if acquisition_id in ["ecog", "seeg"]:
        kind = "ieeg"
    elif acquisition_id == "eeg":
        kind = "eeg"
    ext = "vhdr"  # files need to be in BV format

    # initialize bids_root and derivatives directories
    bids_root = _get_bidsroot_path()
    # output_path = _get_derivatives_path

    # find the bids run file
    bids_root, bids_fname, datapath = find_bids_run_file(
        subject_id=subject_id,
        session_id=session_id,
        task_id=task_id,
        acquisition_id=acquisition_id,
        run_id=run_id,
        kind=kind,
        datadir=bids_root,
        ext=ext,
    )

    # run fragility analysis
    click.echo(f"Analyzing fragility for the file {datapath}...")
    deriv_path = setup_fragility(
        subject_id,
        acquisition_id,
        run_id,
        kind,
        Path(bids_root),
        reference,
        overwrite=overwrite,
    )
    # click.echo(f"Analysis complete. Results saved in {deriv_path}...")
    logger.info("Analysis complete. Plotting.")
    click.echo("Analysis complete. Plotting.")
    plot_params = {
        "subject_id": subject_id,
        "session_id": session_id,
        "task_id": task_id,
        "acquisition_id": acquisition_id,
        "run_id": run_id,
        "kind": kind,
        "datadir": deriv_path,
    }

    ch_names = find_run_channels(**plot_params)

    # Make sure the channels is in a list
    if isinstance(ch_names, str):
        channels = ch_names.split(",")

    # Find the output .npz file
    output_fname = find_bids_file(
        deriv_path,
        subject_id,
        session_id,
        task_id,
        acquisition_id,
        run_id,
        kind,
        "output",
        True,
    )
    # Create the filename
    fig_name = output_fname.replace(".npz", "_heatmap.pdf")
    results = np.load(output_fname)
    pertmats = results["pertmats"]
    # TODO: remove once version is stable
    if pertmats.shape[1] == len(ch_names):
        pertmats = pertmats.T

    # Normalize the perturbation matrix
    fragmat = Normalize.compute_fragilitymetric(pertmats)

    # Plot the heatmap with save on
    plotter = PlotFragilityHeatmap(figure_dir=os.path.dirname(output_fname))
    plotter.plot_fragility_heatmap(fragmat,
                                   ch_names,
                                   colorblind=colorblind,
                                   output_fpath=fig_name,
                                   fontsize=16)
    logger.info("Plotting finished")
    click.echo("Plotting finished!")
def update_channels_file(subject_id, session_id, task_id, acquisition_id,
                         run_id, new_data):
    """

    Parameters
    ----------
    subject_id :
    session_id :
    task_id :
    acquisition_id :
    run_id :
    new_data :

    """
    colnames = new_data.columns
    for col in colnames:
        new_data[col] = new_data[col].str.lower()

    bids_fname = make_bids_basename(
        subject=subject_id,
        session=session_id,
        task=task_id,
        acquisition=acquisition_id,
        run=run_id,
    )
    bidsRun = BidsRun(_get_bidsroot_path(), bids_fname)
    current_run_channels = bidsRun.get_channels_metadata()
    for ind, row in new_data.iterrows():
        for sind, val in row.items():
            if "bad" in val:
                bad_channel = ind + sind
                bidsRun.modify_channel_info("status", bad_channel, "bad")
                if "description" not in current_run_channels[0].keys():
                    bidsRun.append_channel_info("description", bad_channel,
                                                "bad")
                else:
                    bidsRun.modify_channel_info("description", bad_channel,
                                                "bad")
            elif "wm" in val:
                bad_channel = ind + sind
                bidsRun.modify_channel_info("status", bad_channel, "bad")
                if "description" not in current_run_channels[0].keys():
                    bidsRun.append_channel_info("description", bad_channel,
                                                "WM")
                else:
                    bidsRun.modify_channel_info("description", bad_channel,
                                                "WM")
            elif "out" in val:
                bad_channel = ind + sind
                bidsRun.modify_channel_info("status", bad_channel, "bad")
                if "description" not in current_run_channels[0].keys():
                    bidsRun.append_channel_info("description", bad_channel,
                                                "out")
                else:
                    bidsRun.modify_channel_info("description", bad_channel,
                                                "out")
            elif "csf" in val:
                bad_channel = ind + sind
                bidsRun.modify_channel_info("status", bad_channel, "bad")
                if "description" not in current_run_channels[0].keys():
                    bidsRun.append_channel_info("description", bad_channel,
                                                "csf")
                else:
                    bidsRun.modify_channel_info("description", bad_channel,
                                                "csf")
            elif "ventricle" in val:
                bad_channel = ind + sind
                bidsRun.modify_channel_info("status", bad_channel, "bad")
                if "description" not in current_run_channels[0].keys():
                    bidsRun.append_channel_info("description", bad_channel,
                                                "ventricle")
                else:
                    bidsRun.modify_channel_info("description", bad_channel,
                                                "ventricle")
            else:
                channel = ind + sind
                if "description" not in current_run_channels[0].keys():
                    bidsRun.append_channel_info("description", channel, val)
                else:
                    bidsRun.modify_channel_info("description", channel, val)
Пример #10
0
def _check_runfunc_params(run_params, function):
    """Check run or plot parameters and outputs user-friendly text."""
    bids_root = Path(_get_bidsroot_path())

    subject_id = run_params["subject_id"]
    session_id = run_params["session_id"]
    acquisition_id = run_params["acquisition_id"]
    run_id = run_params["run_id"]

    if any(x is None for x in [subject_id, session_id, run_id]):
        logger.error(
            f"ez {function} with subject_id: {subject_id}, session_id: {session_id}, "
            f"acquisition_id: {acquisition_id}, and run_id: {run_id}. \n"
            f"subject_id, session_id, and run_id must be set.")
        counted("CLI_exceptions")
        raise CLI_Input_Error(
            "\n\nEZTrack run requires subject_id, session_id, and run_id to be "
            "specified to analyze a specifically uploaded dataset. "
            "To see all subjects available, use the 'pat-summary' command. "
            "To see all datasets available, use the 'metadata' command. \n\n"
            "An example command looks like: "
            f"'ez {function} --subject_id <sub_id> --session_id <session_id> --run_id <run_id>'"
            "\n\nReplace words in brackets with your desired data identifiers."
        )
    subject_ids = [
        os.path.basename(x).split("-")[-1] for x in bids_root.glob("*")
        if x.is_dir() if "sub" in x.as_posix()
    ]

    if subject_id not in subject_ids:
        message_str = (f"Subject {subject_id} you passed in "
                       f"is not a valid subject in your subject pool. "
                       f"Here are the current subjects {subject_ids}.")
        error_msg = f"ez {function}: Subject {subject_id} is not a valid subject"
        logger.error(error_msg)
        counted("CLI_exceptions")
        raise CLI_Input_Error(message_str)

    acquisitions = ["ecog", "seeg", "eeg"]
    if acquisition_id not in acquisitions:
        error_msg = f"ez {function}: Acquisition {acquisition_id} is not supported yet."
        logger.error(error_msg)
        counted("CLI_exceptions")
        raise CLI_Input_Error(f"{acquisition_id} is not supported yet... "
                              f"Pass 'ecog', 'seeg', or 'eeg'.")

    subj_bids_parser = BidsParser(bids_root, subject_id)
    # get the file paths to datasets
    _fpaths = [
        x.as_posix()
        for x in Path(bids_root / f"sub-{subject_id}").rglob("*.vhdr")
    ]
    _fpaths = [x for x in _fpaths if acquisition_id in x]

    # extract run
    run_ids = [_extract_run(x) for x in _fpaths]
    run_ids = ",".join(run_ids)
    if run_id not in run_ids:
        message_str = (
            f"Run {run_id} you passed in "
            f"is not a valid run for subject {subject_id} and acquisition {acquisition_id}. "
            f"Here are the current run ids {run_ids}.")
        error_msg = f"ez {function}: Run {run_id} is not a valid run"
        logger.error(error_msg)
        counted("CLI_exceptions")
        raise CLI_Input_Error(message_str)

    if function == "plot":
        colorblind = run_params["colorblind"]
        cmap = run_params["cmap"]
        # If colorblind is turned on, cmap should be None
        if colorblind and cmap is not None:
            logger.error(
                f"ez {function} with colorblind: True and cmap: {cmap}. Only one option can"
                f" be turned on.")
            counted("CLI_exceptions")
            raise CLI_Input_Error(
                "The colorblind option was turned on. You cannot also set a colormap."
            )

        # Check if a valid colormap was entered
        cmap_opts = plt.colormaps()
        if cmap not in cmap_opts and cmap is not None:
            logger.error(
                f"ez {function} with cmap: {cmap}. This is not a valid matplotlib cmap."
            )
            counted("CLI_exceptions")
            raise CLI_Input_Error(
                f"{cmap} is not a value colormap. Options are: {cmap_opts}")