{"cells":[{"cell_type":"markdown","metadata":{},"source":["# TenetoBIDS\n","\n","TenetoBIDS allows use of Teneto functions to analyse entire datasets in just a few lines of code.\n","The output from Teneto is then ready to be placed in statistical models, machine learning algorithms and/or plotted.\n","\n","## Prerequisites\n","\n","To use *TenetoBIDS* you need preprocessied fMRI data in the [BIDS format](https://github.com/bids-standard/bids-specification).\n","It is tested and optimized for [fMRIPrep](https://fmriprep.readthedocs.io/en/stable/) but other preprocessing software following BIDS should (in theory) work too.\n","For fMRIPrep V1.4 or later is requiresd.\n","This preprocessed data should be in the ~BIDS_dir/derivatives/ directory.\n","The output from teneto will always be found in .../BIDS_dir/derivatives/ in directories that begin with teneto- (depending on the function you use).\n","\n","## Contents of this tutorial\n","\n","This tutorial will run a complete analysis on some test data.\n","\n","For this tutorial, we will use some dummy data which is included with teneto.\n","This section details what is in this data.\n",""]},{"cell_type":"code","execution_count":1,"metadata":{},"outputs":[{"output_type":"stream","name":"stdout","text":"['participants.tsv', 'dataset_description.json', 'sub-001', 'derivatives', 'sub-002']\n['teneto-censor-timepoints', 'teneto-derive-temporalnetwork', 'teneto-volatility', 'teneto-exclude-runs', 'teneto-tests', 'teneto-make-parcellation', 'fmriprep', 'teneto-binarize', 'teneto-remove-confounds']\n"}],"source":["import teneto\n","import os\n","dataset_path = teneto.__path__[0] + '/data/testdata/dummybids/'\n","print(os.listdir(dataset_path))\n","print(os.listdir(dataset_path + '/derivatives'))\n",""]},{"cell_type":"markdown","metadata":{},"source":["From the above we can see that there are two subjects in our dataset,\n","and there is a fMRIPrep folder in the derivatives section.\n","Only subject 1 has any dummy data, so we will have to select subject 1."]},{"cell_type":"markdown","metadata":{},"source":["\n","## A complete analysis\n","\n","Below is a complete analysis of this test data. We will go through each step after it."]},{"cell_type":"code","execution_count":2,"metadata":{},"outputs":[{"output_type":"stream","name":"stdout","text":"{'sub-001_run-1_task-a_vol.tsv': 0\n0 0.103733}\n"}],"source":["\n","#Imports.\n","from teneto import TenetoBIDS\n","from teneto import __path__ as tenetopath\n","import numpy as np\n","#Set the path of the dataset.\n","datdir = tenetopath[0] + '/data/testdata/dummybids/'\n","\n","# Step 1:\n","bids_filter = {'subject': '001',\n"," 'run': 1,\n"," 'task': 'a'}\n","tnet = TenetoBIDS(datdir, selected_pipeline='fmriprep', bids_filter=bids_filter, exist_ok=True)\n","\n","# Step 2: create a parcellation\n","parcellation_params = {'atlas': 'Schaefer2018',\n"," 'atlas_desc': '100Parcels7Networks',\n"," 'parc_params': {'detrend': True}}\n","tnet.run('make_parcellation', parcellation_params)\n","\n","# Step 3: Regress out confounds\n","remove_params = {'confound_selection': ['confound1']}\n","tnet.run('remove_confounds', remove_params)\n","\n","# Step 4: Additonal preprocessing\n","exclude_params = {'confound_name': 'confound1',\n"," 'exclusion_criteria': '<-0.99'}\n","tnet.run('exclude_runs', exclude_params)\n","censor_params = {'confound_name': 'confound1',\n"," 'exclusion_criteria': '<-0.99',\n"," 'replace_with': 'cubicspline',\n"," 'tol': 0.25}\n","tnet.run('censor_timepoints', censor_params)\n","\n","# Step 5: Calculats time-varying connectivity\n","derive_params = {'params': {'method': 'jackknife',\n"," 'postpro': 'standardize'}}\n","tnet.run('derive_temporalnetwork', derive_params)\n","\n","# Step 6: Performs a binarization of the network\n","binaraize_params = {'threshold_type': 'percent',\n"," 'threshold_level': 0.1}\n","tnet.run('binarize', binaraize_params)\n","\n","# Step 7: Calculate a network measure\n","measure_params = {'distance_func': 'hamming'}\n","tnet.run('volatility', measure_params)\n","\n","# Step 8: load data\n","vol = tnet.load_data()\n","print(vol)\n",""]},{"cell_type":"markdown","metadata":{},"source":["\n","## Big Picture\n","\n","While the above code may seem overwhelming at first.\n","It is quite little code for what it does.\n","It starts with nifti images and ends with a single measure about\n","a time-varying connectivity estimate of the network.\n","\n","There is one recurring theme used in the code above:\n","\n","`tnet.run(function_name, function_parameters)`\n","\n","`function_name` is a string and function_parameters is a dictionary\n","`function_name` can be most functions in teneto if the data is in the correct format.\n","`function_parameters` are the inputs to that function.\n","You never need to pass the input data (e.g. time series or network), or any functions that have a `sidecar` input.\n","\n","TenetoBIDS will also automatically try and find a confounds file in the derivatives when needed,\n","so, this does not need to be specified either.\n","\n","Once you have grabbed the above, the rest is pretty straight forward. But we will go through each step in turn.\n",""]},{"cell_type":"markdown","metadata":{},"source":["\n","## Step 1 - defining the TenetoBIDS object.\n",""]},{"cell_type":"code","execution_count":3,"metadata":{},"outputs":[],"source":["#Set the path of the dataset.\n","datdir = tenetopath[0] + '/data/testdata/dummybids/'\n","# Step 1:\n","bids_filter = {'subject': '001',\n"," 'run': 1,\n"," 'task': 'a'}\n","tnet = TenetoBIDS(datdir, selected_pipeline='fmriprep', bids_filter=bids_filter, exist_ok=True)\n",""]},{"cell_type":"markdown","metadata":{},"source":["### selected_pipeline\n","\n","**This states where teneto will go looking for files. This example shows it should look in the fMRIPrep derivative directory.\n","(i.e. in: datadir + '/derivatives/fmriprep/').\n","\n","### bids_filter\n","\n","teneto uses [pybids](https://github.com/bids-standard/pybids/) to select different files.\n","The `bids_filter` argument is a dictionary of arguments that get passed into the `BIDSLayout.get`.\n","In the example above, we are saying we want subject 001, run 1 and task a.\n","If you do not provide any arguments for `bids_filter`, all data found within the derivatives folder gets selected for analyses.\n","\n","### exist_ok (default: False)\n","\n","This checks that it is ok to overwrite any previous calculations.\n","The output data is saved in a new directory.\n","If the new output directory already exists, the teneto step has previously been run, and an error will be returned because otherwise data may be overwritten. To overrule this error, set `exists_ok` to True."]},{"cell_type":"markdown","metadata":{},"source":["We can now look at what files are selected that will be run on the next step."]},{"cell_type":"code","execution_count":4,"metadata":{},"outputs":[{"output_type":"execute_result","data":{"text/plain":"[,\n ]"},"metadata":{},"execution_count":4}],"source":["tnet.get_selected_files()\n",""]},{"cell_type":"markdown","metadata":{},"source":["If there are files here you do not want, you can add to the bids filter with `tnet.update_bids_filter`\n","Or, you can set tnet.bids_filter to a new dictionary if you want."]},{"cell_type":"markdown","metadata":{},"source":["Next, you might want to see what functions you can run on these selected files.\n","The following will specify what functions can be run specifically on the selected data.\n","If you want all options, you can add the `for_selected=False`."]},{"cell_type":"code","execution_count":5,"metadata":{},"outputs":[{"output_type":"execute_result","data":{"text/plain":"'make_parcellation, exclude_runs'"},"metadata":{},"execution_count":5}],"source":["tnet.get_run_options()\n",""]},{"cell_type":"markdown","metadata":{},"source":["The output here (exclude_runs and make_parcellation) says which functions that, with the selected files, can be called in tnet.run.\n","Once different functions have been called, the options change."]},{"cell_type":"markdown","metadata":{},"source":["## Step 2 Calling the run function to make a parcellation.\n","\n","When selecting preprocessed files, these will often be nifti images.\n","From these images, we want to make time-series of regions of interests. This can be done with :py:func:`.make_parcellation`.\n","This function uses [TemplateFlow](https://github.com/templateflow/templateflow/) atlases to make the parcellation."]},{"cell_type":"code","execution_count":6,"metadata":{},"outputs":[],"source":["parcellation_params = {'atlas': 'Schaefer2018',\n"," 'atlas_desc': '100Parcels7Networks',\n"," 'parc_params': {'detrend': True}}\n","tnet.run('make_parcellation', parcellation_params)\n",""]},{"cell_type":"markdown","metadata":{},"source":["The `atlas` and `atlas_desc` are used to identify TemplateFlow atlases.\n","\n","Teneto uses nilearn's [NiftiLabelsMasker](https://nilearn.github.io/modules/generated/nilearn.input_data.NiftiLabelsMasker.html)\n","to mark the parcellation.\n","Any arguments to this function (e.g. preprocessing steps) can be passed in the argument using 'parc_params' (here detrend is used)."]},{"cell_type":"markdown","metadata":{},"source":["## Step 3 Regress out confounds"]},{"cell_type":"code","execution_count":7,"metadata":{},"outputs":[],"source":["remove_params = {'confound_selection': ['confound1']}\n","tnet.run('remove_confounds', remove_params)\n",""]},{"cell_type":"markdown","metadata":{},"source":["Confounds can be removed by calling :py:func:`.remove_confounds`.\n","\n","The confounds tsv file is automatically located as long as it is in a derivatives folder and that there is only one\n","\n","Here 'confound1' is a column namn in the confounds tsv file.\n","\n","Similarly to make parcellation, it uses nilearn ([nilean.signal.clean](https://nilearn.github.io/modules/generated/nilearn.signal.clean.html).\n","`clean_params` is a possible argument, like `parc_params` these are inputs to the nilearn function.\n",""]},{"cell_type":"markdown","metadata":{},"source":["## Step 4: Additonal preprocessing"]},{"cell_type":"code","execution_count":8,"metadata":{},"outputs":[],"source":["exclude_params = {'confound_name': 'confound1',\n"," 'exclusion_criteria': '<-0.99'}\n","tnet.run('exclude_runs', exclude_params)\n","censor_params = {'confound_name': 'confound1',\n"," 'exclusion_criteria': '<-0.99',\n"," 'replace_with': 'cubicspline',\n"," 'tol': 0.25}\n","tnet.run('censor_timepoints', censor_params)\n","\n",""]},{"cell_type":"markdown","metadata":{},"source":["These two calls to tnet.run exclude both time-points and runs, which are problematic.\n","The first, exclude_runs, rejects any run where the mean of confound1 is less than 0.99.\n","Excluded runs will no longer be part of the loaded data in later calls of tnet.run().\n","\n","Centoring time-points here says that whenever there is a time-point that is less than 0.99, it will be \"censored\" (set to not a number).\n","We have also set argument replace_with to 'cubicspline'. This argument means that the values that have censored now get simulated using a cubic spline.\n","The parameter _tol_ says what percentage of time-points are allowed to be censored before the run gets ignored."]},{"cell_type":"markdown","metadata":{},"source":["## Step 5: Calculats time-varying connectivity\n","\n","The code below now derives time-varying connectivity matrices.\n","There are multiple different methods that can be called.\n","See teneto.timeseries.derive_temporalnetwork for more options.\n",""]},{"cell_type":"code","execution_count":9,"metadata":{},"outputs":[],"source":["derive_params = {'params': {'method': 'jackknife',\n"," 'postpro': 'standardize'}}\n","tnet.run('derive_temporalnetwork', derive_params)\n",""]},{"cell_type":"markdown","metadata":{},"source":["## Step 6: Performs a binarization of the network\n","\n","Once you have a network representation,\n","there are multiple ways this can be transformed.\n","One example, is to binarize the network so all values are 0 or 1.\n","The code below converts the top 10% of edges to 1s, the rest 0."]},{"cell_type":"code","execution_count":10,"metadata":{},"outputs":[],"source":["binaraize_params = {'threshold_type': 'percent',\n"," 'threshold_level': 0.1}\n","tnet.run('binarize', binaraize_params)\n",""]},{"cell_type":"markdown","metadata":{},"source":["## Step 7: Calculate a network measure\n","\n","We are now ready to calculate a property of the temproal network.\n","Here we calculate volatility (i.e. how much the network changes per time-point).\n","This generates one value per subject."]},{"cell_type":"code","execution_count":11,"metadata":{},"outputs":[],"source":["measure_params = {'distance_func': 'hamming'}\n","tnet.run('volatility', measure_params)\n",""]},{"cell_type":"markdown","metadata":{},"source":["## Step 8: load data"]},{"cell_type":"code","execution_count":12,"metadata":{},"outputs":[{"output_type":"stream","name":"stdout","text":"{'sub-001_run-1_task-a_vol.tsv': 0\n0 0.103733}\n"}],"source":["vol = tnet.load_data()\n","print(vol)\n",""]},{"cell_type":"markdown","metadata":{},"source":["Now that we have a measure of volatility for the network.\n","We can now load it and view the measure."]}],"nbformat":4,"nbformat_minor":2,"metadata":{"language_info":{"name":"python","codemirror_mode":{"name":"ipython","version":3}},"orig_nbformat":2,"file_extension":".py","mimetype":"text/x-python","name":"python","npconvert_exporter":"python","pygments_lexer":"ipython3","version":3}}