From f7acb04e243669b7bac8168f2f12a909a5204514 Mon Sep 17 00:00:00 2001 From: Beliy Nikita <beliy.nikita@outlook.com> Date: Sun, 22 Mar 2020 17:49:19 +0100 Subject: [PATCH] adapted prepare --- example1/resources/Appariement.xlsx | Bin 5784 -> 5471 bytes example1/resources/participants_add.json | 73 +++++ example1/resources/participants_remove.json | 64 +++++ example1/resources/plugins/bidsify_plugin.py | 23 +- example1/resources/plugins/rename_plugin.py | 287 ++++++++++++++----- 5 files changed, 361 insertions(+), 86 deletions(-) create mode 100644 example1/resources/participants_add.json create mode 100644 example1/resources/participants_remove.json diff --git a/example1/resources/Appariement.xlsx b/example1/resources/Appariement.xlsx index 5c3c88a613e7a47fc3e84301ccf2230783fcbdfa..29036a340230af04b8190f9691673a48c4f9b02d 100644 GIT binary patch delta 3549 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 delta 3862 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 KlidJadj1b;U+p3Q diff --git a/example1/resources/participants_add.json b/example1/resources/participants_add.json new file mode 100644 index 0000000..5c5bae2 --- /dev/null +++ b/example1/resources/participants_add.json @@ -0,0 +1,73 @@ +{ + "participant_id":{ + "LongName":"Participant Id", + "Description":"label identifying a particular subject" + }, + "age":{ + "LongName":"Age", + "Description":"Age of a subject", + "Units":"year" + }, + "sex":{ + "LongName":"Sex", + "Description":"Sex of a subject", + "Levels":{ + "F":"Female", + "M":"Male" + } + }, + "education":{ + "LongName":"Education level", + "Description":"Education level" + }, + "group":{ + "LongName":"group", + "Description":"Group subject belongs", + "Levels":{ + "patient":"patient", + "control":"control" + } + }, + "handiness":{ + "LongName":"Handiness of subject", + "Levels":{ + "r":"right-handed", + "l":"left-handed" + } + }, + "paired":{ + "LongName":"Paired Id", + "Description":"Subject Id paired with this subject" + }, + "ses_1":{ + "LongName":"First session", + "Description":"Id of the first session taken by subject", + "Levels":{ + "ses-LCL":"Low charge level", + "ses-HCL":"High charge level", + "ses-STROOP":"Multiparametric scan" + } + }, + "ses_2":{ + "LongName":"Second session", + "Description":"Id of the second session taken by subject", + "Levels":{ + "ses-LCL":"Low charge level", + "ses-HCL":"High charge level", + "ses-STROOP":"Multiparametric scan" + } + }, + "ses_3":{ + "LongName":"Third session", + "Description":"Id of the second session taken by subject", + "Levels":{ + "ses-LCL":"Low charge level", + "ses-HCL":"High charge level", + "ses-STROOP":"Multiparametric scan" + } + }, + "random":{ + "LongName":"Rundom number between 0 and 1", + "Description":"Test of adding columns in process/bidsify step" + } +} diff --git a/example1/resources/participants_remove.json b/example1/resources/participants_remove.json new file mode 100644 index 0000000..317371d --- /dev/null +++ b/example1/resources/participants_remove.json @@ -0,0 +1,64 @@ +{ + "participant_id":{ + "LongName":"Participant Id", + "Description":"label identifying a particular subject" + }, + "sex":{ + "LongName":"Sex", + "Description":"Sex of a subject", + "Levels":{ + "F":"Female", + "M":"Male" + } + }, + "education":{ + "LongName":"Education level", + "Description":"Education level" + }, + "group":{ + "LongName":"group", + "Description":"Group subject belongs", + "Levels":{ + "patient":"patient", + "control":"control" + } + }, + "handiness":{ + "LongName":"Handiness of subject", + "Levels":{ + "r":"right-handed", + "l":"left-handed" + } + }, + "paired":{ + "LongName":"Paired Id", + "Description":"Subject Id paired with this subject" + }, + "ses_1":{ + "LongName":"First session", + "Description":"Id of the first session taken by subject", + "Levels":{ + "ses-LCL":"Low charge level", + "ses-HCL":"High charge level", + "ses-STROOP":"Multiparametric scan" + } + }, + "ses_2":{ + "LongName":"Second session", + "Description":"Id of the second session taken by subject", + "Levels":{ + "ses-LCL":"Low charge level", + "ses-HCL":"High charge level", + "ses-STROOP":"Multiparametric scan" + } + }, + "ses_3":{ + "LongName":"Third session", + "Description":"Id of the second session taken by subject", + "Levels":{ + "ses-LCL":"Low charge level", + "ses-HCL":"High charge level", + "ses-STROOP":"Multiparametric scan" + } + } +} diff --git a/example1/resources/plugins/bidsify_plugin.py b/example1/resources/plugins/bidsify_plugin.py index 7cfafc4..6703e2b 100644 --- a/example1/resources/plugins/bidsify_plugin.py +++ b/example1/resources/plugins/bidsify_plugin.py @@ -1,6 +1,7 @@ import os import shutil import logging +import random from definitions import checkSeries @@ -26,27 +27,35 @@ def InitEP(source: str, destination: str, dry: bool) -> int: dry_run = dry +def SubjectEP(scan): + sub_id = int(scan.subject[4:]) + scan.subject = "sub-{:03d}".format(sub_id + 1) + scan.sub_values["sex"] = None + scan.sub_values["random"] = random.random() + + def SessionEP(scan): global series global sid sub = scan.subject ses = scan.session - path = os.path.join(rawfolder, - sub, ses, - "MRI") + # path = os.path.join(rawfolder, + # sub, ses, + # "MRI") + path = os.path.join(scan.in_path, "MRI") series = sorted(os.listdir(path)) - series = [s.split("-",1)[1] for s in series] + series = [s.split("-", 1)[1] for s in series] sid = -1 checkSeries(path, sub, ses, False) # copytng behevioral data - aux_input = os.path.join(rawfolder, sub, ses, "auxiliary") + aux_input = os.path.join(scan.in_path, "auxiliary") if ses in ("ses-LCL", "ses-HCL"): if not os.path.isdir(aux_input): logger.error("Session {}/{} do not contain auxiliary folder" .format(sub, ses)) raise FileNotFoundError("folder {} not found" .format(aux_input)) - beh = os.path.join(bidsfolder, sub, ses, "beh") + beh = os.path.join(scan.in_path, "beh") if not dry_run: os.makedirs(beh, exist_ok=True) for old, new in (("FCsepNBack.tsv", "task-rest_events.tsv"), @@ -81,7 +90,7 @@ def SequenceEP(recording): global Intended Intended = "" sid += 1 - recid = series[sid] + recid = series[sid] if recid != recording.recId(): logger.warning("{}: Id mismatch folder {}" .format(recording.recIdentity(False), diff --git a/example1/resources/plugins/rename_plugin.py b/example1/resources/plugins/rename_plugin.py index bafe39b..7264928 100644 --- a/example1/resources/plugins/rename_plugin.py +++ b/example1/resources/plugins/rename_plugin.py @@ -3,126 +3,229 @@ import pandas import logging import shutil -from tools import tools -from bidsMeta import BIDSfieldLibrary +from tools import tools from bids import BidsSession -from definitions import Series, checkSeries +from definitions import Series, checkSeries, plugin_root + +# defining logger this way will prefix plugin messages +# with plugin name logger = logging.getLogger(__name__) -# global variables -rawfolder = "" -bidsfolder = "" -dry_run = False +############################# +# global bidscoin variables # +############################# -resources = os.path.join(os.path.dirname(__file__), "..") +# Folder with source dataset +rawfolder = None +# folder with prepared dataset +preparedfolder = None +# switch if is a dry-run (test run) +dry_run = False -df_subjects = None +########################### +# global plugin variables # +########################### + +# map of individual sessions +# key: source folde session (s01234) +# value: bidsified session (ses-HCL) scans_map = {} # scale to convert ms in log-files to seconds time_scale = 1e-3 -# list of subjects exel-file columns -excel_col_list = {'Patient' : 'pat', - 'S_A_E' : "pat_sae", +# subject balck-list +# subject folders in this list will be skipped +# by plugin +sub_black_list = [] + +# subject xls table columns and their renaiming +excel_col_list = {"Patient": "pat", + "Sex": "pat_sex", + "Age": "pat_age", + "Education": "pat_edu", 1: "pat_1", 2: "pat_2", 3: "pat_3", - 'Control' : "cnt", - 'S_A_E.1': "cnt_sae", - '1.1': "cnt_1", '2.1': "cnt_2", '3.1': "cnt_3", + 'Control': "cnt", + "Sex.1": "cnt_sex", + "Age.1": "cnt_age", + "Education.1": "cnt_edu", + "1.1": "cnt_1", "2.1": "cnt_2", "3.1": "cnt_3" } -# sub_columns = BIDSfieldLibrary() + +# columns prefixes for patient and control subjects +# 0 == False == Control +# 1 == True == Patient +sub_prefix = ["cnt", "pat"] + +# pandas dataframe with list of subjects +df_subjects = None def InitEP(source: str, destination: str, dry: bool, - subjects: str="") -> int: + subjects: str = "") -> int: + """ + Initialisation of plugin + + 1. Saves source/destination folders and dry_run switch + 2. Loads subjects xls table + + Parameters + ---------- + source: str + path to source dataset + destination: + path to prepared dataset + subjects: str + path to subjects xls file, if empty is looked + in source dataset folder + """ + global rawfolder - global bidsfolder + global preparefolder global dry_run - global subject_file rawfolder = source - bidsfolder = destination + preparefolder = destination dry_run = dry + + ######################### + # Loading subjects list # + ######################### if subjects: subject_file = subjects else: - subject_file = os.path.join(resources, "Appariement.xlsx") - logger.info(subject_file) - + subject_file = os.path.join(plugin_root, "Appariement.xlsx") if not os.path.isfile(subject_file): raise FileNotFoundError("Subject file '{}' not found" .format(subject_file)) - # creating df for subjects + # creating dataframe for subjects global df_subjects df_subjects = pandas.read_excel(subject_file, sheet_name=0, header=0, - usecols=[0,1,2,3,4,5,6,7,8,9,10]) - df_subjects.rename(index=str, columns=excel_col_list,inplace=True) + usecols="A:N" + ) + df_subjects.rename(index=str, columns=excel_col_list, inplace=True) df_subjects = df_subjects[df_subjects['pat'].notnull() | df_subjects['cnt'].notnull()] def SubjectEP(session: BidsSession) -> int: - sub_id = int(session.subject) - index = df_subjects.loc[df_subjects["pat"] == sub_id].index - status = 0 + """ + Subject determination and initialisation + + 1. Checks if subject not in balck list + 2. Loads demographics from subject table + 3. Creates session parcing dictionary + + Parameters + ---------- + session: BidsSession + + Returns + ------- + int: + if 0, plugin succesfull + if > 0, plugin failed, an exception will be raised + if < 0, plugin failed, and subject will be skipped + """ + + ################################# + # Skipping if in the black list # + ################################# + if session.subject in sub_black_list: + logger.info("Subject '{}' is in black_list" + .format(session.subject)) + return -1 + + ################################ + # Retriving subject from table # + ################################ + try: + # in case if folder name in source dataset + # cannot be converted to integer + sub_id = int(session.subject) + except ValueError as e: + logger.error("Subject {}: Can't determine subject Id for: {}" + .format(session.subject, e)) + return -1 + + # storing bidsified subject id into session object + # optional, but can be easely retrieved + session.sub_values["participant_id"] = "sub-" + session.subject + # looking for subject in dataframe prefix = "pat" + index = df_subjects.loc[df_subjects[prefix] == sub_id].index + # storing participant group in session + session.sub_values["group"] = "patient" + if len(index) == 0: # Subject not in patient list, looking in control - index = df_subjects.loc[df_subjects["cnt"] == sub_id].index + prefix = "cnt" + index = df_subjects.loc[df_subjects[prefix] == sub_id].index + session.sub_values["group"] = "control" if len(index) == 0: raise KeyError("Subject {} not found in table" .format(sub_id)) - status = 1 - prefix = "cnt" + if len(index) > 1: + logger.warning("Subject {}: several column entries present" + .format(sub_id)) index = index[0] # retrieving demographics - # <sex>_<age>_<education> - line = df_subjects.loc[index, prefix + "_sae"].split("_") - sex = line[0] - age = int(line[1]) - education = int(line[2]) - session.sub_values["participant_id"] = "sub-" + session.subject - session.sub_values["sex"] = sex - session.sub_values["age"] = age - session.sub_values["education"] = education + sex = df_subjects.loc[index, prefix + "_sex"] + age = df_subjects.loc[index, prefix + "_age"] + education = df_subjects.loc[index, prefix + "_edu"] + + # session initialised values are Null + # fill them only if they are retrieved from table + if pandas.notna(sex): + session.sub_values["sex"] = sex + if pandas.notna(age): + session.sub_values["age"] = float(age) + if pandas.notna(education): + session.sub_values["education"] = float(education) # looking for pairing - if status == 0: - session.sub_values["group"] = "patient" - session.sub_values["paired"] = "sub-{:03}".format(int(df_subjects - .loc[index, "cnt"])) - else: - session.sub_values["group"] = "control" - session.sub_values["paired"] = "sub-{:03}".format(int(df_subjects - .loc[index, "pat"])) + paired = df_subjects.loc[index, sub_prefix[prefix == "cnt"]] + if pandas.notna(paired): + session.sub_values["paired"] = "sub-{:03}".format(int(paired)) # looking for order of sessions - global scans_map - scans_map = {} + scans_map.clear() scans_order = sorted([os.path.basename(s) for s in tools.lsdirs(os.path.join(rawfolder, session.subject), "s*") ]) + # looping over session defined in columns for ind, s in enumerate(("_1", "_2", "_3")): v = "ses-" + str(df_subjects.loc[index, prefix + s]).strip() ses = "ses" + s if v == "ses-nan": + # Session not defined in table, but existing + # in source dataset session.sub_values[ses] = "" logger.warning("Subject {}({}): missing {} value" .format(session.sub_values["participant_id"], session.sub_values["group"], ses) ) + elif v == "ses-OUT": + # participant left study + logger.warning("Subject {}({}): seems to be abandoned study" + .format(session.sub_values["participant_id"], + session.sub_values["group"], + ses) + ) + return -1 elif v not in Series: + # invalid session name logger.critical("Subject {}({}): Invalid {}: {}" .format(session.sub_values["participant_id"], session.sub_values["group"], @@ -132,10 +235,13 @@ def SubjectEP(session: BidsSession) -> int: raise KeyError("Invalid {}: {}" .format(ses, v)) else: + # session retrieved, storing values session.sub_values[ses] = v - scans_map[scans_order[ind]] = session.sub_values[ses] + scans_map[scans_order[ind]] = v # checking if all scans are identifyable + # if not, additional scans will be stored + # with original names for scan in scans_order: if scan not in scans_map: logger.error("Subject {}({}): Can't identify session {}" @@ -144,45 +250,68 @@ def SubjectEP(session: BidsSession) -> int: scan)) scans_map[scan] = scan + # opional, the sub- prefix added automatically + # if not present session.subject = "sub-" + session.subject - return 0 def SessionEP(session: BidsSession) -> int: - # retrieving correct session name + """ + 1. Set-up session name + + Parameters + ---------- + session: BidsSession + """ + # Setting session name from map session.session = scans_map[session.session] - return 0 def SessionEndEP(session: BidsSession): - path = os.path.join(bidsfolder, + """ + 1. Checks the series in the prepared folder + 2. Extract KSS/VAS data from kss_dict to tsv file + 3. Parces in-scan nBack and KSS/VAS log files + """ + # path contain destination folder, where + # all data files are placed + path = os.path.join(preparefolder, session.getPath(True)) out_path = os.path.join(path, "MRI") - checkSeries(out_path, - session.subject, session.session, - False) - # parcing log files + # checking if session contains correct series + if not dry_run: + checkSeries(out_path, + session.subject, session.session, + False) + + ############################################ + # Retrieving in-scan task and KSS/VAS data # + ############################################ if session.session == "ses-STROOP": return 0 + # where tsv files are + inp_dir = os.path.join(session.in_path, "inp") + # where tsv files should be + aux_dir = os.path.join(path, "auxiliary") + if not os.path.isdir(inp_dir): + raise NotADirectoryError(inp_dir) + + # do not copy if we are in dry mode + if not dry_run: + os.makedirs(aux_dir, exist_ok=True) + # just copy file, in real life application + # you may parce files + for file in ("FCsepNBack.tsv", "VAS.tsv"): + file = os.path.join(inp_dir, file) + if not os.path.isfile(file): + raise FileNotFoundError(file) + shutil.copy2(file, aux_dir) - logs = os.path.join(session.in_path, "inp") - aux_d = os.path.join(path, "auxiliary") - if not os.path.isdir(logs): - raise NotADirectoryError(logs) - - os.makedirs(aux_d, exist_ok=True) - for file in ("FCsepNBack.tsv", "VAS.tsv"): - file = os.path.join(logs, file) - if not os.path.isfile(file): - raise FileNotFoundError(file) - shutil.copy2(file, aux_d) - - for file in ("FCsepNBack.json", "VAS.json"): - file = os.path.join(resources, file) - if not os.path.isfile(file): - raise FileNotFoundError(file) - shutil.copy2(file, aux_d) - - return 0 + # copiyng correspondent json files + for file in ("FCsepNBack.json", "VAS.json"): + file = os.path.join(plugin_root, file) + if not os.path.isfile(file): + raise FileNotFoundError(file) + shutil.copy2(file, aux_dir) -- GitLab