diff --git a/docs/_static/decision_tree_kundu.png b/docs/_static/decision_tree_kundu.png deleted file mode 100644 index 76b646374..000000000 Binary files a/docs/_static/decision_tree_kundu.png and /dev/null differ diff --git a/docs/_static/decision_tree_meica.png b/docs/_static/decision_tree_meica.png new file mode 100644 index 000000000..d63b09bc3 Binary files /dev/null and b/docs/_static/decision_tree_meica.png differ diff --git a/docs/_static/decision_tree_meica.tex b/docs/_static/decision_tree_meica.tex new file mode 100644 index 000000000..649be73c8 --- /dev/null +++ b/docs/_static/decision_tree_meica.tex @@ -0,0 +1,163 @@ +\documentclass[border=2pt]{standalone} +\usepackage[utf8]{inputenc} % Required for inserting images +\usepackage{tikz} +\usepackage{helvet} +\usetikzlibrary{shapes.geometric, arrows} +\pagecolor{white} + +%-------------------------defining colorblind friendly colors +% Using pale color scheme in Figure 6 +% by Paul Tol https://personal.sron.nl/~pault/ +\definecolor{cbblue}{HTML}{BBCCEE} +\definecolor{cbcyan}{HTML}{CCEEFF} +\definecolor{cbgreen}{HTML}{CCDDAA} +\definecolor{cbyellow}{HTML}{EEEEBB} +\definecolor{cbred}{HTML}{FFCCCC} +\definecolor{cbgrey}{HTML}{DDDDDD} + +% -------------------------defining nodes +\tikzstyle{input} = [trapezium, trapezium left angle =80, trapezium right angle = 100, +minimum width= 3cm, minimum height=0.5cm, text centered, draw=black, fill=cbblue] +\tikzstyle{process} = [rectangle, minimum width = 3cm, minimum height = 1cm, +text centered, , text width=4cm,draw=black, fill=cbgrey] +\tikzstyle{decision} = [diamond, minimum width = 3cm, minimum height = 1cm, +text centered, , text width=3.5cm, draw=black, fill=cbcyan] +\tikzstyle{changeclass} = [rectangle, rounded corners, minimum width=3cm, minimum height=1cm, +text centered, draw = black, fill=cbyellow] +\tikzstyle{reject} = [trapezium, trapezium left angle =80, trapezium right angle = 100, +minimum width= 1cm, minimum height=0.5cm, text centered, draw=black, fill=cbred] +\tikzstyle{accept} = [trapezium, trapezium left angle =80, trapezium right angle = 100, +minimum width= 1cm, minimum height=0.5cm, text centered, draw=black, fill=cbgreen] + +% -------------------------defining connectors +\tikzstyle{arrow} = [thick,->, >=stealth] +\tikzstyle{line} = [thick,-,>=stealth] +\begin{document} + +% ------------------------- tikz image (flow chart) +\begin{tikzpicture}[node distance = 2cm] + +% ------------------------- nodes ------------------------- + +% ----- node: 0 +\node(0)[input, label={90:\textbf{MEICA Decision Tree}}, label={180:$node\ 0$}] {Set all components to unclassified}; +% ----- node: 1 +\node(1)[decision, below of=0,label={180:$node\ 1$}, yshift=-1.0cm]{$\rho$ $>$ $\kappa$}; +\node(rej0)[reject, right of=1, xshift=3cm, align=center]{Unlikely BOLD\\$\rightarrow$ Reject}; +% ----- node: 2 +\node(2)[decision, below of=1,label={180:$node\ 2$} ,label={[align=center] 315: voxel counts for signif fit\\of multi-echo data\\to $T_2$ or $S_0$ decay models}, yshift=-3.0cm]{$n \, FS_0 \, > \, n \, FT_2$ \& $n \,FT_2$ $>$ 0}; +\node(rej1)[reject, right of=2, xshift=3cm, align=center]{Unlikely BOLD\\$\rightarrow$ Reject}; +% ----- node: 3 +\node(3)[process, below of=2, label={180:$node\ 3$}, label={[align=center] 315: varex: variance explained\\by each component}, yshift=-1.5cm]{Calculate median(varex) across all components}; +% ----- node: 4 +\node(4)[decision, below of=3,label={180:$node\ 4$},label={[align=center] 315:DICE overlap between $T_2$ or $S_0$\\decay models and ICA component\\peak clusters}, yshift=-1.5cm]{dice $FS_0$ $>$ dice $FT_2$ \& varex $>$ median(varex) +}; +\node(rej2)[reject, right of=4, xshift=3cm, align=center]{Unlikely BOLD\\$\rightarrow$ Reject}; +% ----- node: 5 +\node(5)[decision, below of=4,label={180:$node\ 5$}, label={[align=center] 315: $t-statistic$ of $FT_2$ values\\in component peak clusters vs\\peak voxels outside of clusters}, yshift=-3.5cm]{ $0 \, >$ signal-noise \& varex $>$ median(varex)}; +\node(rej3)[reject, right of=5, xshift=3cm, align=center]{Unlikely BOLD\\$\rightarrow$ Reject}; +% ----- node: 6 +\node(6)[process, below of=5, label={180:$node\ 6$}, label={0: Uses all components}, yshift=-1.5cm]{Calculate $\kappa$ elbow}; +% ----- node: 7 +\node(7)[process, below of=6, label={180:$node\ 7$}, yshift=-0.0cm]{Identify and exclude $\leq$3 highest variance unclassified components from some $\rho$ elbow calculations}; +% ----- node: 8 +\node(8)[process, below of=7, label={180:$node\ 8$}, label={[align=center] 0: Uses all components and subset\\of unclassified components}]{Calculate $\rho$ elbow\\(kundu method)}; +% ----- node: 9 +\node(9)[decision, below of=8,label={180:$node\ 9$}, yshift=-1.5cm]{$\kappa \geq \kappa$ elbow}; +\node(rej4)[changeclass, right of=9, xshift=3cm]{Provisional accept}; +% ----- node: 10 +\node(10)[decision, below of=9,label={180:$node\ 10$}, yshift=-3.0cm]{$\rho > \rho$ elbow }; +\node(rej5)[changeclass, right of=10, xshift=3cm]{Unclassified}; +% ----- node: 11 +\node(11)[decision, below of=10, label={180:$node\ 11$}, yshift=-3.5cm]{ \textit{n} classified as $Provisional\ accept < 2$}; +\node(rej6)[input, right of=11, xshift=4cm, align=center]{Rerun ICA, metric calcs,\\\& component selection.\\If max restarts reached,\\accept everything\\not already rejected}; +% ----- node: 12 +\node(12)[process, below of=11,label={180:$node\ 12$},label={0: $90^{th}$ percentile threshold}, yshift=-1.7cm]{Calculate upper varex on provionally accepted components}; +% ----- node: 13 +\node(13)[process, below of=12,label={180:$node\ 13$}, label={0: $25^{th}$ percentile threshold},]{Calculate lower varex on provionally accepted components}; +% ----- node: 14 +\node(14)[process, below of=13,label={180:$node\ 14$}, label={[align=center] 0:$\lceil 2:3 \rceil$ depending on the\\number of fMRI volumes}]{Calculate extend factor}; +% ----- node: 15 +\node(15)[process, below of=14,label={180:$node\ 15$},label={[align=center] 0: \textit{n} Provisional accept\\$*$ extend factor}]{Calculate max good mean metric rank}; +% ----- node: 16 +\node(16)[process, below of=15, label={180:$node\ 16$}, label={[align=center] 0: $\frac{(max-min \, \kappa) \, \div \kappa}{(max-min \, varex) \div varex}$}]{Calculate $\kappa$ ratio on provionally accepted components}; +% ----- node: 17 +\node(17)[decision, below of=16,label={180:$node\ 17$},label={315:variance \& mean metric rank are high}, yshift=-2.5cm]{mean metric rank $>$ max good mean metric rank \& varex$>$extend factor * upper varex}; +\node(rej7)[reject, right of=17, xshift=4cm, align=center]{Less likely BOLD\\$\rightarrow$ Reject}; +% ----- node: 18 +\node(18)[decision, below of=17,label={180:$node\ 18$},label={[align=center] 315: Accept if remaining component\\is less likely to be BOLD,\\but varex is low \& not worth\\losing a degree of freedom for}, yshift=-4.5cm]{mean metric rank $>$ \textit{n} max good mean metric rank \& varex $\leq$ lower varex \& $\kappa$ $\leq \, \kappa$ elbow }; +\node(rej8)[accept, right of=18, xshift=4cm, align=center]{Low variance\\$\rightarrow$ Accept}; +% ----- node: 19 +\node(19)[decision, below of=18,label={180:$node\ 19$},label={315: Nothing unclassified remains}, yshift=-4.0cm]{\textit{n} Unclassified $==0$}; +\node(rej9)[accept, right of=19, xshift=3cm, align=center]{Provisional accept\\$\rightarrow$ Accept}; +% ----- node: 20 +\node(20)[process, below of=19, label={180:$node\ 20$},yshift=-2.0cm, label={[align=center] 315: \textit{n} accepted guess =\\$\frac{\sum(\kappa > \kappa\, elbow\, \&\, \rho > \rho\, elbow)+ \sum(\kappa > \kappa\, elbow)}{2}$}]{Calculate new mean metric ranks and \textit{n} accepted guess on remaining unclassified and provisionally accepted components}; +% ----- node: 21 +\node(21)[decision, below of=20,label={180:$node\ 21$}, yshift=-3.5cm]{new mean metric rank $>$ (\textit{n} accepted guess)/2 \& varex $\kappa$ ratio $>$ 2 entend factor \& varex $>$ 2 upper varex}; +\node(rej10)[changeclass, right of=21, xshift=4cm, align=center]{Less likely BOLD\\$\rightarrow$ Provisional Reject}; +% ----- node: 22 +\node(22)[decision, below of=21,label={180:$node\ 22$}, yshift=-5cm]{new mean metric rank $>$ 0.9*\textit{n} accepted guess \& varex $>$ (lower varex * extend factor)}; +\node(rej11)[changeclass, right of=22, xshift=4cm, align=center]{Less likely BOLD\\$\rightarrow$ Provisional Reject}; +% ----- node: 23 +\node(23)[process, below of=22,label={180:$node\ 23$}, label={[align=center] 220: $25^{th}$ percentile variance\\explained from remaining\\non-rejected components},yshift=-2cm]{Calculate new lower varex}; +% ----- node: 24 +\node(24)[decision, below of=23,label={180:$node\ 24$}, yshift=-2.5cm]{new mean metric rank $>$ \textit{n} accepted guess \& varex $>$ new lower varex}; +\node(rej12)[accept, right of= 24, xshift=4cm, align=center]{Accept borderline\\$\rightarrow$Accept}; +% ----- node: 25 +\node(25)[decision, below of=24,label={180:$node\ 25$}, yshift=-3.5cm]{ $\kappa$ $>$ $\kappa$ elbow \& varex $>$ new lower varex}; +\node(rej13)[accept, right of=25, xshift=3cm, align=center]{Accept borderline\\$\rightarrow$Accept}; +% ----- node: 26 +\node(26)[reject, below of=25,label={180:$node\ 26$}, yshift=-1cm, align=center]{Remaining Provisional Reject $\rightarrow$ Reject}; +% ----- node: 27 +\node(27)[accept, below of=26,label={180:$node\ 27$}, yshift=0.8cm, align=center]{Remaining Unclassified \& Provisional accept\\$\rightarrow$ Likely BOLD $\rightarrow$ Accept}; + +% ------------------------- connections ------------------------- +% draw[x](origin)--node[anchor=position]{text}(destination); +\draw[arrow](0)--(1); +\draw[arrow](1)--(2); +\draw[arrow](2)--(3); +\draw[arrow](3)--(4); +\draw[arrow](4)--(5); +\draw[arrow](5)--(6); +\draw[arrow](6)--(7); +\draw[arrow](7)--(8); +\draw[arrow](8)--(9); +\draw[arrow](9)--(10); +\draw[arrow](10)--(11); +\draw[arrow](11)--(12); +\draw[arrow](12)--(13); +\draw[arrow](13)--(14); +\draw[arrow](14)--(15); +\draw[arrow](15)--(16); +\draw[arrow](16)--(17); +\draw[arrow](17)--(18); +\draw[arrow](18)--(19); +\draw[arrow](19)--(20); +\draw[arrow](20)--(21); +\draw[arrow](21)--(22); +\draw[arrow](22)--(23); +\draw[arrow](rej10)--(22); +\draw[arrow](23)--(24); +\draw[arrow](rej11)--(23); +\draw[arrow](rej11)--(24); +\draw[arrow](24)--(25); +\draw[arrow](25)--(26); +\draw[arrow](26)--(27); +\draw[arrow](1)--node[anchor=south] {yes} (rej0); +\draw[arrow](2)--node[anchor=south] {yes} (rej1); +\draw[arrow](4)--node[anchor=south] {yes} (rej2); +\draw[arrow](5)--node[anchor=south] {yes} (rej3); +\draw[arrow](9)--node[anchor=south] {yes} (rej4); +\draw[arrow](rej4)--(10); +\draw[arrow](10)--node[anchor=south] {yes} (rej5); +\draw[arrow](rej5)--(11); +\draw[arrow](11)--node[anchor=south] {yes} (rej6); +\draw[arrow](17)--node[anchor=south] {yes} (rej7); +\draw[arrow](18)--node[anchor=south] {yes} (rej8); +\draw[arrow](19)--node[anchor=south] {yes} (rej9); +\draw[arrow](21)--node[anchor=south] {yes} (rej10); +\draw[arrow](22)--node[anchor=south] {yes} (rej11); +\draw[arrow](24)--node[anchor=south] {yes} (rej12); +\draw[arrow](25)--node[anchor=south] {yes} (rej13); +\end{tikzpicture} +\end{document} diff --git a/docs/_static/decision_tree_tedana_orig.png b/docs/_static/decision_tree_tedana_orig.png new file mode 100644 index 000000000..16942ae7c Binary files /dev/null and b/docs/_static/decision_tree_tedana_orig.png differ diff --git a/docs/_static/decision_tree_kundu.tex b/docs/_static/decision_tree_tedana_orig.tex similarity index 98% rename from docs/_static/decision_tree_kundu.tex rename to docs/_static/decision_tree_tedana_orig.tex index 72b2052bb..9c359e329 100644 --- a/docs/_static/decision_tree_kundu.tex +++ b/docs/_static/decision_tree_tedana_orig.tex @@ -40,7 +40,7 @@ % ------------------------- nodes ------------------------- % ----- node: 0 -\node(0)[input, label={90:\textbf{Kundu Decision Tree (Tedana implementation)}}, label={180:$node\ 0$}] {Set all components to unclassified}; +\node(0)[input, label={90:\textbf{tedana v0.013 Decision Tree}}, label={180:$node\ 0$}] {Set all components to unclassified}; % ----- node: 1 \node(1)[decision, below of=0,label={180:$node\ 1$}, yshift=-1.5cm]{$\rho$ $>$ $\kappa$}; \node(rej0)[reject, right of=1, xshift=3cm, align=center]{Unlikely BOLD\\$\rightarrow$ Reject}; diff --git a/docs/approach.rst b/docs/approach.rst index 158d3d285..d290df3b1 100644 --- a/docs/approach.rst +++ b/docs/approach.rst @@ -348,8 +348,8 @@ classify ICA components as TE-dependent (BOLD signal), TE-independent (non-BOLD noise), or neither (to be ignored). These classifications are saved in **desc-tedana_metrics.tsv**. The actual decision tree is dependent on the component selection algorithm employed. -``tedana`` includes two options `kundu` and `minimal` (which uses hardcoded thresholds -applied to each of the metrics). `These decision trees are detailed here`_. +``tedana`` includes three options `tedana_orig`, `meica` and `minimal` (which uses hardcoded +thresholds applied to each of the metrics). `These decision trees are detailed here`_. Components that are classified as noise are projected out of the optimally combined data, yielding a denoised timeseries, which is saved as **desc-optcomDenoised_bold.nii.gz**. diff --git a/docs/building_decision_trees.rst b/docs/building_decision_trees.rst index cefce7ef6..7b0e2e479 100644 --- a/docs/building_decision_trees.rst +++ b/docs/building_decision_trees.rst @@ -139,8 +139,8 @@ Each outputs field includes: It is possible to add a new metric to the component table during the selection process. This is useful if a metric is to be calculated on a subset of components based on what happened during previous steps in the selection process. This is **not** recommended, - but since it was done as part of the original kundu decision tree process defined in - meica it is possible. + but, since it was done as part of the original decision tree process used in the + meica and tedana_orig, it is possible. ************************************** @@ -160,7 +160,7 @@ Defining a custom decision tree Decision trees are stored in json files. The default trees are stored as part of the tedana code repository in `resources/decision_trees`_. The minimal tree, minimal.json, is a good example highlighting the structure and steps in a tree. It -may be helpful to look at that tree while reading this section. kundu.json replicates +may be helpful to look at that tree while reading this section. meica.json replicates the decision tree used in MEICA version 2.5, the predecessor to tedana. It is more complex, but also highlights additional possible functionality in decision trees. @@ -221,7 +221,7 @@ that is used to check whether results are plausible & can help avoid mistakes. an error when these metrics are not found. One might want to calculate a new metric if the metric uses only a subset of the components based on previous classifications. This does make interpretation of results more confusing, but, since - this functionality was part of the kundu decision tree, it is included. + this functionality is part of the tedana_orig and meica decision trees, it is included. - intermediate_classifications A list of intermediate classifications (i.e. "provisionalaccept", diff --git a/docs/faq.rst b/docs/faq.rst index 07e8131c4..7a8cd79f8 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -114,18 +114,29 @@ make sure to output the denoised time series into a separate directory. .. _RICA: https://github.com/ME-ICA/rica -************************************************************************************* -[tedana] What is the difference between the kundu and minimal decision trees? -************************************************************************************* +.. _tree differences: + +********************************************************************************************* +[tedana] What are the differences between the tedana_orig, meica, and minimal decision trees? +********************************************************************************************* The decision tree is the series of conditions through which each component is -classified as accepted or rejected. The kundu tree (`--tree kundu`), used in Prantik -Kundu's MEICA v2.5, is the classification process that has long -been used by ``tedana`` and users have been generally content with the results. The -kundu tree used multiple intersecting metrics and rankings to classify components. -How these steps may interact on specific datasets is opaque. While there is a kappa -(T2*-weighted) elbow threshold and a rho (S0-weighted) elbow threshold, as discussed -in publications, no component is accepted or rejected because of those thresholds. +classified as accepted or rejected. The meica tree (`--tree meica`) was created by Prantik +Kundu for ``MEICA v2.5``, the predecessor to ``tedana``. Tedana's decision tree was based +on this method, but we noticed a difference that affected edge-case components. There were +components that were re-evalued multiple times late in the decision tree in ``meica``, +but, once ``tedana`` rejected them, they were excluded from additional steps. This means +that ``meica`` may accept a few components that ``tedana`` was rejects. When examining +the effects of this divergance, we saw that ``meica`` sometimes accepted high variance +components. While those additionally accepted components often looked like noise, we wanted +to make sure users were aware of this difference. We include options to run the ``meica`` +tree and the ``tedana_orig`` tree which has been successfully used for years. +``tedana_orig`` will always remove the same or more components. + +Both of the above trees use multiple intersecting metrics and rankings to classify +components. How these steps may interact on specific datasets is opaque. While there is +a kappa (T2*-weighted) elbow threshold and a rho (S0-weighted) elbow threshold, as +discussed in publications, no component is accepted or rejected because of those thresholds. Users sometimes notice rejected components that clearly should have been accepted. For example, a component that included a clear T2*-weighted V1 response to a block design flashing checkerboard was sometimes rejected because the relatively large variance of @@ -136,22 +147,20 @@ likely to reject T2* weighted components. There are a few other criteria, but co with `kappa>kappa elbow` and `rho=v3.7) so -that this version of the selection process would again be possible to run. +methods. In 2022, Prantik made `MEICA v3.3`_ which runs on for python >=v3.7. It is not +under active development, but it should be possible to run. .. _shared code on bitbucket: https://bitbucket.org/prantikk/me-ica/src/experimental .. _distributed with AFNI as MEICA v2.5 beta 11: https://github.com/afni/afni/tree/master/src/pkundu diff --git a/docs/included_decision_trees.rst b/docs/included_decision_trees.rst index 262f4fe31..c0d3f4e9a 100644 --- a/docs/included_decision_trees.rst +++ b/docs/included_decision_trees.rst @@ -2,11 +2,13 @@ Included Decision Trees ####################### -Two decision trees are currently distributed with ``tedana``. +Three decision trees are currently distributed with ``tedana``. -``kundu`` is the decision tree that is based on MEICA version 2.5 -and has been included with ``tedana`` since the start of this project. -While multiple publications have used and benefits from this decision, +``meica`` is the decision tree that is based on MEICA version 2.5 and +``tedana_orig`` is very similar and has been included with ``tedana`` +since the start of this project. An explanation of why these both exist +is `in the FAQ`_ +While multiple publications have used and benefited from this decision, tree, but it includes many steps with arbitrary thresholds and, when components seem misclassified, it's often hard to understand why. @@ -42,9 +44,10 @@ to see when in the process a component's classifiation changed. Legend for Decision Tree Flow Charts +.. _in the FAQ: faq.html#tree-differences ******************* -Kundu decision tree +meica decision tree ******************* Nodes 1-5 reject components that are very unlikely to be BOLD. @@ -65,19 +68,40 @@ like the `accepted` components. This was widely confusing to many users so they are now classified as `accepted` but with classification tags `low variance` (node 18) or `accept borderline` (nodes 24 & 25). -.. image:: _static/decision_tree_kundu.png +.. image:: _static/decision_tree_meica.png :width: 400 - :alt: Kundu Decision Tree Flow Chart + :alt: MEICA Decision Tree Flow Chart -`LaTeX file to generate the kundu decision tree flow chart`_ +`LaTeX file to generate the meica decision tree flow chart`_ -.. _LaTeX file to generate the kundu decision tree flow chart: _static/decision_tree_kundu.tex +.. _LaTeX file to generate the meica decision tree flow chart: _static/decision_tree_meica.tex + +*************************** +tedana_orig decision tree +*************************** + +Identical to the meica decision tree until node 21. In the tedana tree, +components that cross the threshold criteria in nodes 21 and 22 are +rejected and not included in the calculation for a new variance explained +threshold in node 23. In the meica tree, those components are provisionally +rejected, but still included in the new variance explained calculation and +can be potentially accepted in nodes 24 and 25. This means tedana will give +the same results as meica or reject additional components base on those two +final decision criteria. + +.. image:: _static/decision_tree_tedana_orig.png + :width: 400 + :alt: tedana_orig Decision Tree Flow Chart + +`LaTeX file to generate the tedana_orig decision tree flow chart`_ + +.. _LaTeX file to generate the tedana_orig decision tree flow chart: _static/decision_tree_tedana_orig.tex ********************* Minimal decision tree ********************* -The minimal tree starts similarly to the kundu tree by rejecting components +The minimal tree starts similarly to the other trees by rejecting components that are very unlikely to be BOLD (nodes 1-5). Then all components where :math:`\kappa` > :math:`\kappa` elbow and :math:`\rho` < :math:`\rho` elbow are `provisional accept` and otherwise are `provisional reject` (nodes 8 & 10). diff --git a/docs/outputs.rst b/docs/outputs.rst index 8a8efae02..55369191b 100644 --- a/docs/outputs.rst +++ b/docs/outputs.rst @@ -258,23 +258,24 @@ component and **rejected** components are be removed through denoising. ``classification_tags`` provide more information on why components received a specific classification. Each component can receive more than one tag. -The following tags are included depending if ``--tree`` is "minimal", "kundu", -or if ``ica_reclassify`` is run. +The following tags are included depending if ``--tree`` is "minimal", "meica", +"tedana_orig" or if ``ica_reclassify`` is run. The same tags are included +for "meica" and "tedana_orig" ===================== ================ ======================================== Tag Included in Tree Explanation ===================== ================ ======================================== -Likely BOLD minimal,kundu Accepted because likely to include some +Likely BOLD minimal,meica Accepted because likely to include some BOLD signal -Unlikely BOLD minimal,kundu Rejected because likely to include a +Unlikely BOLD minimal,meica Rejected because likely to include a lot of non-BOLD signal -Low variance minimal,kundu Accepted because too low variance to +Low variance minimal,meica Accepted because too low variance to lose a degree-of-freedom by rejecting -Less likely BOLD kundu Rejected based on some edge criteria +Less likely BOLD meica Rejected based on some edge criteria based on relative rankings of components -Accept borderline kundu Accepted based on some edge criteria +Accept borderline meica Accepted based on some edge criteria based on relative rankings of components -No provisional accept kundu Accepted because because kundu tree did +No provisional accept meica Accepted because because meica tree did not find any components to consider accepting so the conservative "failure" case is accept everything rather than diff --git a/tedana/resources/decision_trees/kundu.json b/tedana/resources/decision_trees/meica.json similarity index 78% rename from tedana/resources/decision_trees/kundu.json rename to tedana/resources/decision_trees/meica.json index 308dc826c..adc9e3edd 100644 --- a/tedana/resources/decision_trees/kundu.json +++ b/tedana/resources/decision_trees/meica.json @@ -1,7 +1,7 @@ { - "tree_id": "kundu_MEICA27_decision_tree", - "info": "Following the decision tree close to one designed by Prantik Kundu", - "report": "This is the kundu tree \\citep{tedana_decision_trees} based on the criteria of the MEICA v2.5 decision tree \\citep{kundu2013integrated}. For a description of the decision tree steps, with the rationale for each step, see \\citep{olafsson2015enhanced}.", + "tree_id": "MEICA_decision_tree", + "info": "Following the full decision tree designed by Prantik Kundu for use in MEICA v2.5", + "report": "This is the MEICA decision tree in \\citep{tedana_decision_trees} uses the criteria of the MEICA v2.5 decision tree \\citep{kundu2013integrated}. For a description of the decision tree steps, with the rationale for each step, see \\citep{olafsson2015enhanced}.", "necessary_metrics": [ "kappa", "rho", @@ -14,14 +14,8 @@ "d_table_score", "countnoise" ], - "generated_metrics": [ - "d_table_score_node20", - "varex kappa ratio" - ], - "intermediate_classifications": [ - "provisionalaccept", - "unclass_highvar" - ], + "generated_metrics": ["d_table_score_node20", "varex kappa ratio"], + "intermediate_classifications": ["provisionalaccept", "provisionalreject", "unclass_highvar"], "classification_tags": [ "Likely BOLD", "Unlikely BOLD", @@ -30,19 +24,12 @@ "Accept borderline", "No provisional accept" ], - "_comment": "More information on the kundu decision tree and how it differs from other options is at https://tedana.readthedocs.io/en/stable/included_decision_trees.html. Descriptions of the metrics used are in desc-tedana_metrics.json, which is ouputted when this tree is run", + "_comment": "More information on the meica decision tree and how it differs from other options is at https://tedana.readthedocs.io/en/stable/included_decision_trees.html. Descriptions of the metrics used are in desc-tedana_metrics.json, which is ouputted when this tree is run", "nodes": [ { "functionname": "manual_classify", - "parameters": { - "new_classification": "unclassified", - "decide_comps": "all" - }, - "kwargs": { - "clear_classification_tags": true, - "dont_warn_reclassify": true, - "log_extra_info": "" - }, + "parameters": {"new_classification": "unclassified", "decide_comps": "all"}, + "kwargs": {"clear_classification_tags": true, "dont_warn_reclassify": true}, "_comment": "All components are initially labeled as 'unclassified'." }, { @@ -55,10 +42,7 @@ "left": "rho", "right": "kappa" }, - "kwargs": { - "tag_if_true": "Unlikely BOLD", - "log_extra_info": "" - }, + "kwargs": {"tag_if_true": "Unlikely BOLD"}, "_comment": "The first four steps are for rejecting components that very unlikely to have substantial T2* signal. Any components with rho greater than kappa are rejected (Code I002 in premodularized tedana). Higher rho than kappa means that the component better fits the TE-independence (S0) model than the TE-dependence (T2*) model." }, { @@ -75,8 +59,7 @@ "left2": "countsigFT2", "op2": ">", "right2": 0, - "tag_if_true": "Unlikely BOLD", - "log_extra_info": "" + "tag_if_true": "Unlikely BOLD" }, "_comment": "Any components with more voxels that are significant based on the S0 model's F-statistics than the T2* model's are rejected, as long as there is at least one significant voxel for the T2 model (Code I003 in premodularized tedana)" }, @@ -87,9 +70,6 @@ "metric_name": "variance explained", "median_label": "varex" }, - "kwargs": { - "log_extra_info": "" - }, "_comment": "The median variance explained is calculated across all components, for use in later steps." }, { @@ -106,10 +86,9 @@ "left2": "variance explained", "op2": ">", "right2": "median_varex", - "tag_if_true": "Unlikely BOLD", - "log_extra_info": "" + "tag_if_true": "Unlikely BOLD" }, - "_comment": "Any components with higher S0 model beta map-F-statistic map Dice similarity index than T2 model beta map-F-statistic map Dice similarity index and greater than median variance explained are rejected. In slightly plainer English, this step rejects any high-variance components where significant voxels in the F-stat map overlap more with highly S0-associated voxels than T2*-associated voxels. (Code I004 in premodularized tedana)" + "_comment": "Any components with higher S0 model beta map-F-statistic map Dice similarity index than T2 model beta map-F-statistic map Dice similarity index and greater than median variance explained are rejected. In slightly plainer English, this step rejects any high-variance components where significant voxels in the F-stat map overlap more with highly S0-associated voxels than T2*-associated voxels. (Code I004 in premodularized tedana)" }, { "functionname": "dec_left_op_right", @@ -125,19 +104,13 @@ "left2": "variance explained", "op2": ">", "right2": "median_varex", - "tag_if_true": "Unlikely BOLD", - "log_extra_info": "" + "tag_if_true": "Unlikely BOLD" }, "_comment": "Any components with a negative t-statistic comparing the distribution of T2* model F-statistics from voxels in clusters to those of voxels not in clusters and variance explained greater than median are rejected. That is reject any high-variance components exhibiting more 'speckled' T2*-associated voxels than 'clustered' ones. (Code I005 in premodularized tedana)" }, { "functionname": "calc_kappa_elbow", - "parameters": { - "decide_comps": "all" - }, - "kwargs": { - "log_extra_info": "" - }, + "parameters": {"decide_comps": "all"}, "_comment": "The kappa elbow is calculated from all components, for use in later steps." }, { @@ -146,21 +119,12 @@ "decide_comps": "unclassified", "new_classification": "unclass_highvar" }, - "kwargs": { - "log_extra_info": "" - }, "_comment": "Unclassified components exhibiting a large step down in variance explained are classified as 'unclassified high-variance' and excluded or partially excluded from several steps below." }, { "functionname": "calc_rho_elbow", - "parameters": { - "decide_comps": "all" - }, - "kwargs": { - "subset_decide_comps": "unclassified", - "rho_elbow_type": "kundu", - "log_extra_info": "" - }, + "parameters": {"decide_comps": "all"}, + "kwargs": {"subset_decide_comps": "unclassified", "rho_elbow_type": "kundu"}, "_comment": "This step determines the 'rho elbow' based on the rho values for all of the components, as well as just the unclassified components (excluding unclass_highvar). It calculates the elbow for each set of components, as well as the F-statistic threshold associated with p < 0.05 given the number of echoes, and then takes the mean of the three values." }, { @@ -173,9 +137,6 @@ "left": "kappa", "right": "kappa_elbow_kundu" }, - "kwargs": { - "log_extra_info": "" - }, "_comment": "Any unclassified components with kappa greater than or equal to the kappa elbow are provisionally accepted." }, { @@ -183,27 +144,18 @@ "parameters": { "if_true": "unclassified", "if_false": "nochange", - "decide_comps": [ - "provisionalaccept" - ], + "decide_comps": ["provisionalaccept"], "op": ">", "left": "rho", "right": "rho_elbow_kundu" }, - "kwargs": { - "log_extra_info": "" - }, "_comment": "Any provisionally accepted components with rho greater than the rho elbow are reset to 'unclassified'." }, { "functionname": "dec_classification_doesnt_exist", "parameters": { "new_classification": "accepted", - "decide_comps": [ - "provisionalaccept", - "unclassified", - "unclass_highvar" - ], + "decide_comps": ["provisionalaccept", "unclassified", "unclass_highvar"], "class_comp_exists": "provisionalaccept" }, "kwargs": { @@ -220,9 +172,6 @@ "thresh_label": "upper", "percentile_thresh": 90 }, - "kwargs": { - "log_extra_info": "" - }, "_comment": "The variance explained upper threshold is calculated as the 90th percentile of variance explained from provisionally accepted components." }, { @@ -232,37 +181,21 @@ "thresh_label": "lower", "percentile_thresh": 25 }, - "kwargs": { - "log_extra_info": "" - }, "_comment": "The variance explained lower threshold is calculated as the 25th percentile of variance explained from provisionally accepted components." }, { "functionname": "calc_extend_factor", "parameters": {}, - "kwargs": { - "log_extra_info": "" - }, "_comment": "'extend factor' is a scaling number that is used for a few thresholds. 2 if fewer than 90 fMRI volumes, 3 if more than 110 and linear in-between. In the original MEICA, this was discrete with no linear slope between 90 & 110 so this might result in a small difference in results from MEICA for runs with 91-109 volumes." }, { "functionname": "calc_max_good_meanmetricrank", - "parameters": { - "decide_comps": "provisionalaccept" - }, - "kwargs": { - "log_extra_info": "" - }, + "parameters": {"decide_comps": "provisionalaccept"}, "_comment": "'max_good_meanmetricrank' is the number of provisionalaccept components * extend_factor" }, { "functionname": "calc_varex_kappa_ratio", - "parameters": { - "decide_comps": "provisionalaccept" - }, - "kwargs": { - "log_extra_info": "" - }, + "parameters": {"decide_comps": "provisionalaccept"}, "_comment": "'varex kappa ratio' is a new column in the component table. It's calcualted from the provisionally accepted components and is the maximum kappa minus the minimum kappa, divided by the maximum variance explained minus the minimum variance explained." }, { @@ -270,11 +203,7 @@ "parameters": { "if_true": "rejected", "if_false": "nochange", - "decide_comps": [ - "provisionalaccept", - "unclassified", - "unclass_highvar" - ], + "decide_comps": ["provisionalaccept", "unclassified", "unclass_highvar"], "op": ">", "left": "d_table_score", "right": "max_good_meanmetricrank" @@ -294,11 +223,7 @@ "parameters": { "if_true": "accepted", "if_false": "nochange", - "decide_comps": [ - "provisionalaccept", - "unclassified", - "unclass_highvar" - ], + "decide_comps": ["provisionalaccept", "unclassified", "unclass_highvar"], "op": ">", "left": "d_table_score", "right": "max_good_meanmetricrank" @@ -319,15 +244,8 @@ "functionname": "dec_classification_doesnt_exist", "parameters": { "new_classification": "accepted", - "decide_comps": [ - "provisionalaccept", - "unclassified", - "unclass_highvar" - ], - "class_comp_exists": [ - "unclassified", - "unclass_highvar" - ] + "decide_comps": ["provisionalaccept", "unclassified", "unclass_highvar"], + "class_comp_exists": ["unclassified", "unclass_highvar"] }, "kwargs": { "tag": "Likely BOLD", @@ -338,27 +256,16 @@ { "functionname": "calc_revised_meanmetricrank_guesses", "parameters": { - "decide_comps": [ - "provisionalaccept", - "unclassified", - "unclass_highvar" - ] - }, - "kwargs": { - "log_extra_info": "" + "decide_comps": ["provisionalaccept", "unclassified", "unclass_highvar"] }, "_comment": "If any components are still labeled as unclassified or unclassified high-variance, then a revised decision table score is calculated from the provisionally accepted, unclassified, and unclassified high-variance components." }, { "functionname": "dec_left_op_right", "parameters": { - "if_true": "rejected", + "if_true": "provisionalreject", "if_false": "nochange", - "decide_comps": [ - "provisionalaccept", - "unclassified", - "unclass_highvar" - ], + "decide_comps": ["provisionalaccept", "unclassified", "unclass_highvar"], "op": ">", "left": "d_table_score_node20", "right": "conservative_guess" @@ -380,10 +287,11 @@ { "functionname": "dec_left_op_right", "parameters": { - "if_true": "rejected", + "if_true": "provisionalreject", "if_false": "nochange", "decide_comps": [ "provisionalaccept", + "provisionalreject", "unclassified", "unclass_highvar" ], @@ -407,16 +315,14 @@ "parameters": { "decide_comps": [ "provisionalaccept", + "provisionalreject", "unclassified", "unclass_highvar" ], "thresh_label": "new_lower", "percentile_thresh": 25 }, - "kwargs": { - "num_highest_var_comps": "num_acc_guess", - "log_extra_info": "" - }, + "kwargs": {"num_highest_var_comps": "num_acc_guess"}, "_comment": "An updated variance explained lower threshold (25th percentile) is calculated from the 'num_acc_guess' highest variance explained components among the remaining provisionally accepted, unclassified, and unclassified high-variance components." }, { @@ -426,6 +332,7 @@ "if_false": "nochange", "decide_comps": [ "provisionalaccept", + "provisionalreject", "unclassified", "unclass_highvar" ], @@ -449,6 +356,7 @@ "if_false": "nochange", "decide_comps": [ "provisionalaccept", + "provisionalreject", "unclassified", "unclass_highvar" ], @@ -465,19 +373,21 @@ }, "_comment": "Another quirky criterion to keep components. Any provisionally accepted, unclassified, or unclassified high-variance components with kappa less than or equal to the kappa elbow and variance explained greater than the new variance explained lower threshold are accepted and labeled as 'borderline'. Prior to tedana vs 23.0.1 a mistake meant varex_new_lower_thresh would be lower than it is here and that might cause different results.(Code I012 in premodularized tedana)" }, + { + "functionname": "manual_classify", + "parameters": {"new_classification": "rejected", "decide_comps": "provisionalreject"}, + "kwargs": {"log_extra_info": "Reject anything that is provisionalreject"}, + "_comment": "Rejecting components that used to be midK. These were already assigned tags when given the provisionalreject label" + }, { "functionname": "manual_classify", "parameters": { "new_classification": "accepted", - "decide_comps": [ - "provisionalaccept", - "unclassified", - "unclass_highvar" - ] + "decide_comps": ["provisionalaccept", "unclassified", "unclass_highvar"] }, "kwargs": { "tag": "Likely BOLD", - "log_extra_info": "Anything still provisional (accepted or rejected) should be accepted" + "log_extra_info": "Anything still provisional accepted or unclassified should be accepted" }, "_comment": "All remaining unclassified, unclassified high-variance, or provisionally accepted components are accepted." } diff --git a/tedana/resources/decision_trees/minimal.json b/tedana/resources/decision_trees/minimal.json index fc65a2966..9594a5a4b 100644 --- a/tedana/resources/decision_trees/minimal.json +++ b/tedana/resources/decision_trees/minimal.json @@ -12,27 +12,16 @@ "signal-noise_t", "variance explained" ], - "intermediate_classifications": [ - "provisionalaccept", - "provisionalreject" - ], - "classification_tags": [ - "Likely BOLD", - "Unlikely BOLD", - "Low variance" - ], + "intermediate_classifications": ["provisionalaccept", "provisionalreject"], + "classification_tags": ["Likely BOLD", "Unlikely BOLD", "Low variance"], "_comment": "More information on the minimial decision tree and how it differs from other options is at https://tedana.readthedocs.io/en/stable/included_decision_trees.html. Descriptions of the metrics used are in desc-tedana.metrics.json, which is ouputted when this tree is run", "nodes": [ { "functionname": "manual_classify", - "parameters": { - "new_classification": "unclassified", - "decide_comps": "all" - }, + "parameters": {"new_classification": "unclassified", "decide_comps": "all"}, "kwargs": { "clear_classification_tags": true, - "dont_warn_reclassify": true, - "log_extra_info": "" + "dont_warn_reclassify": true }, "_comment": "All components are initially labeled as 'unclassified'." }, @@ -46,10 +35,7 @@ "left": "rho", "right": "kappa" }, - "kwargs": { - "tag_if_true": "Unlikely BOLD", - "log_extra_info": "" - }, + "kwargs": {"tag_if_true": "Unlikely BOLD"}, "_comment": "The first four steps are for rejecting components that very unlikely to have substantial T2* signal. Any components with rho greater than kappa are rejected. Higher rho than kappa means that the component better fits the TE-independence (S0) model than the TE-dependence (T2*) model." }, { @@ -66,8 +52,7 @@ "left2": "countsigFT2", "op2": ">", "right2": 0, - "tag_if_true": "Unlikely BOLD", - "log_extra_info": "" + "tag_if_true": "Unlikely BOLD" }, "_comment": "Any components with more voxels that are significant based on the S0 model's F-statistics than the T2* model's are rejected, as long as there is at least one significant voxel for the T2 model." }, @@ -78,9 +63,6 @@ "metric_name": "variance explained", "median_label": "varex" }, - "kwargs": { - "log_extra_info": "" - }, "_comment": "The median variance explained is calculated across all components, for use in later steps." }, { @@ -97,8 +79,7 @@ "left2": "variance explained", "op2": ">", "right2": "median_varex", - "tag_if_true": "Unlikely BOLD", - "log_extra_info": "" + "tag_if_true": "Unlikely BOLD" }, "_comment": "Any components with higher S0 model beta map-F-statistic map Dice similarity index than T2 model beta map-F-statistic map Dice similarity index and greater than median variance explained are rejected. In slightly plainer English, this step rejects any high-variance components where significant voxels in the F-stat map overlap more with highly S0-associated voxels than T2*-associated voxels." }, @@ -116,30 +97,21 @@ "left2": "variance explained", "op2": ">", "right2": "median_varex", - "tag_if_true": "Unlikely BOLD", - "log_extra_info": "" + "tag_if_true": "Unlikely BOLD" }, "_comment": "Any components with a negative t-statistic comparing the distribution of T2* model F-statistics from voxels in clusters to those of voxels not in clusters and variance explained greater than median are rejected. That is reject any high-variance components exhibiting more 'speckled' T2*-associated voxels than 'clustered' ones." }, { "functionname": "calc_kappa_elbow", - "parameters": { - "decide_comps": "all" - }, - "kwargs": { - "log_extra_info": "" - }, + "parameters": {"decide_comps": "all"}, "_comment": "The kappa elbow is calculated from all components, for use in later steps." }, { "functionname": "calc_rho_elbow", - "parameters": { - "decide_comps": "all" - }, + "parameters": {"decide_comps": "all"}, "kwargs": { "subset_decide_comps": "unclassified", - "rho_elbow_type": "liberal", - "log_extra_info": "" + "rho_elbow_type": "liberal" }, "_comment": "This step determines the 'rho elbow' based on the rho values for all of the components, as well as just the unclassified components. It calculates the elbow for each set of components and then takes the maximum of the two." }, @@ -153,9 +125,6 @@ "left": "kappa", "right": "kappa_elbow_kundu" }, - "kwargs": { - "log_extra_info": "" - }, "_comment": "Any unclassified components with kappa greater than or equal to the kappa elbow are provisionally accepted. Any remaining unclassified components are provisionally rejected. Nothing is left 'unclassified'" }, { @@ -168,11 +137,7 @@ "left": "kappa", "right": "rho" }, - "kwargs": { - "right_scale": 2, - "tag_if_true": "Likely BOLD", - "log_extra_info": "" - }, + "kwargs": {"right_scale": 2, "tag_if_true": "Likely BOLD"}, "_comment": "Any provisionally accepted components with kappa greater than two times rho are accepted. That is, even if a component has a high rho value, if kappa above threshold and substantially higher, assume it as something work keeping and accept it" }, { @@ -180,17 +145,11 @@ "parameters": { "if_true": "provisionalreject", "if_false": "nochange", - "decide_comps": [ - "provisionalreject", - "provisionalaccept" - ], + "decide_comps": ["provisionalreject", "provisionalaccept"], "op": ">", "left": "rho", "right": "rho_elbow_liberal" }, - "kwargs": { - "log_extra_info": "" - }, "_comment": "Any provisionally accepted or provisionally rejected components with rho values greater than the liberal rho elbow are provisionally rejected." }, { @@ -204,36 +163,23 @@ "var_metric": "variance explained", "single_comp_threshold": 0.1, "all_comp_threshold": 1.0, - "tag_if_true": "Low variance", - "log_extra_info": "" + "tag_if_true": "Low variance" }, "_comment": "This step flags remaining low-variance components (less than 0.1%) and accepts up to 1% cumulative variance across these components. This is done because these components don't explain enough variance to be worth further reducing the degrees of freedom of the denoised data." }, { "functionname": "manual_classify", - "parameters": { - "new_classification": "accepted", - "decide_comps": "provisionalaccept" - }, - "kwargs": { - "tag": "Likely BOLD", - "log_extra_info": "" - }, + "parameters": {"new_classification": "accepted", "decide_comps": "provisionalaccept"}, + "kwargs": {"tag": "Likely BOLD"}, "_comment": "All remaining provisionally accepted components are accepted." }, { "functionname": "manual_classify", "parameters": { "new_classification": "rejected", - "decide_comps": [ - "provisionalreject", - "unclassified" - ] - }, - "kwargs": { - "tag": "Unlikely BOLD", - "log_extra_info": "" + "decide_comps": ["provisionalreject", "unclassified"] }, + "kwargs": {"tag": "Unlikely BOLD"}, "_comment": "All remaining unclassified (nothing should be unclassified) or provisionally rejected components are rejected." } ] diff --git a/tedana/resources/decision_trees/tedana_orig.json b/tedana/resources/decision_trees/tedana_orig.json new file mode 100644 index 000000000..772c7a36b --- /dev/null +++ b/tedana/resources/decision_trees/tedana_orig.json @@ -0,0 +1,369 @@ +{ + "tree_id": "tedana_orig_decision_tree", + "info": "Very similar to the decision tree designed by Prantik Kundu", + "report": "This is the tedana_orig tree \\citep{tedana_decision_trees}, which is very similar to the criteria of the MEICA v2.5 decision tree \\citep{kundu2013integrated}. For a description of the decision tree steps, with the rationale for each step, see \\citep{olafsson2015enhanced}.", + "necessary_metrics": [ + "kappa", + "rho", + "countsigFS0", + "countsigFT2", + "dice_FS0", + "dice_FT2", + "signal-noise_t", + "variance explained", + "d_table_score", + "countnoise" + ], + "generated_metrics": ["d_table_score_node20", "varex kappa ratio"], + "intermediate_classifications": ["provisionalaccept", "unclass_highvar"], + "classification_tags": [ + "Likely BOLD", + "Unlikely BOLD", + "Less likely BOLD", + "Low variance", + "Accept borderline", + "No provisional accept" + ], + "_comment": "More information on the tedana_orig decision tree and how it differs from other options is at https://tedana.readthedocs.io/en/stable/included_decision_trees.html. Descriptions of the metrics used are in desc-tedana_metrics.json, which is ouputted when this tree is run", + "nodes": [ + { + "functionname": "manual_classify", + "parameters": {"new_classification": "unclassified", "decide_comps": "all"}, + "kwargs": {"clear_classification_tags": true, "dont_warn_reclassify": true}, + "_comment": "All components are initially labeled as 'unclassified'." + }, + { + "functionname": "dec_left_op_right", + "parameters": { + "if_true": "rejected", + "if_false": "nochange", + "decide_comps": "all", + "op": ">", + "left": "rho", + "right": "kappa" + }, + "kwargs": {"tag_if_true": "Unlikely BOLD"}, + "_comment": "The first four steps are for rejecting components that very unlikely to have substantial T2* signal. Any components with rho greater than kappa are rejected (Code I002 in premodularized tedana). Higher rho than kappa means that the component better fits the TE-independence (S0) model than the TE-dependence (T2*) model." + }, + { + "functionname": "dec_left_op_right", + "parameters": { + "if_true": "rejected", + "if_false": "nochange", + "decide_comps": "all", + "op": ">", + "left": "countsigFS0", + "right": "countsigFT2" + }, + "kwargs": { + "left2": "countsigFT2", + "op2": ">", + "right2": 0, + "tag_if_true": "Unlikely BOLD" + }, + "_comment": "Any components with more voxels that are significant based on the S0 model's F-statistics than the T2* model's are rejected, as long as there is at least one significant voxel for the T2 model (Code I003 in premodularized tedana)" + }, + { + "functionname": "calc_median", + "parameters": { + "decide_comps": "all", + "metric_name": "variance explained", + "median_label": "varex" + }, + "_comment": "The median variance explained is calculated across all components, for use in later steps." + }, + { + "functionname": "dec_left_op_right", + "parameters": { + "if_true": "rejected", + "if_false": "nochange", + "decide_comps": "all", + "op": ">", + "left": "dice_FS0", + "right": "dice_FT2" + }, + "kwargs": { + "left2": "variance explained", + "op2": ">", + "right2": "median_varex", + "tag_if_true": "Unlikely BOLD" + }, + "_comment": "Any components with higher S0 model beta map-F-statistic map Dice similarity index than T2 model beta map-F-statistic map Dice similarity index and greater than median variance explained are rejected. In slightly plainer English, this step rejects any high-variance components where significant voxels in the F-stat map overlap more with highly S0-associated voxels than T2*-associated voxels. (Code I004 in premodularized tedana)" + }, + { + "functionname": "dec_left_op_right", + "parameters": { + "if_true": "rejected", + "if_false": "nochange", + "decide_comps": "all", + "op": ">", + "left": 0, + "right": "signal-noise_t" + }, + "kwargs": { + "left2": "variance explained", + "op2": ">", + "right2": "median_varex", + "tag_if_true": "Unlikely BOLD" + }, + "_comment": "Any components with a negative t-statistic comparing the distribution of T2* model F-statistics from voxels in clusters to those of voxels not in clusters and variance explained greater than median are rejected. That is reject any high-variance components exhibiting more 'speckled' T2*-associated voxels than 'clustered' ones. (Code I005 in premodularized tedana)" + }, + { + "functionname": "calc_kappa_elbow", + "parameters": {"decide_comps": "all"}, + "_comment": "The kappa elbow is calculated from all components, for use in later steps." + }, + { + "functionname": "dec_reclassify_high_var_comps", + "parameters": { + "decide_comps": "unclassified", + "new_classification": "unclass_highvar" + }, + "_comment": "Unclassified components exhibiting a large step down in variance explained are classified as 'unclassified high-variance' and excluded or partially excluded from several steps below." + }, + { + "functionname": "calc_rho_elbow", + "parameters": {"decide_comps": "all"}, + "kwargs": {"subset_decide_comps": "unclassified", "rho_elbow_type": "kundu"}, + "_comment": "This step determines the 'rho elbow' based on the rho values for all of the components, as well as just the unclassified components (excluding unclass_highvar). It calculates the elbow for each set of components, as well as the F-statistic threshold associated with p < 0.05 given the number of echoes, and then takes the mean of the three values." + }, + { + "functionname": "dec_left_op_right", + "parameters": { + "if_true": "provisionalaccept", + "if_false": "nochange", + "decide_comps": "unclassified", + "op": ">=", + "left": "kappa", + "right": "kappa_elbow_kundu" + }, + "_comment": "Any unclassified components with kappa greater than or equal to the kappa elbow are provisionally accepted." + }, + { + "functionname": "dec_left_op_right", + "parameters": { + "if_true": "unclassified", + "if_false": "nochange", + "decide_comps": ["provisionalaccept"], + "op": ">", + "left": "rho", + "right": "rho_elbow_kundu" + }, + "_comment": "Any provisionally accepted components with rho greater than the rho elbow are reset to 'unclassified'." + }, + { + "functionname": "dec_classification_doesnt_exist", + "parameters": { + "new_classification": "accepted", + "decide_comps": ["provisionalaccept", "unclassified", "unclass_highvar"], + "class_comp_exists": "provisionalaccept" + }, + "kwargs": { + "at_least_num_exist": 2, + "tag": "No provisional accept", + "log_extra_info": "If nothing is provisionally accepted by this point, then rerun ICA & selection. If max iterations of rerunning done, then accept everything not already rejected" + }, + "_comment": "Code I006 in premodularized tedana" + }, + { + "functionname": "calc_varex_thresh", + "parameters": { + "decide_comps": "provisionalaccept", + "thresh_label": "upper", + "percentile_thresh": 90 + }, + "_comment": "The variance explained upper threshold is calculated as the 90th percentile of variance explained from provisionally accepted components." + }, + { + "functionname": "calc_varex_thresh", + "parameters": { + "decide_comps": "provisionalaccept", + "thresh_label": "lower", + "percentile_thresh": 25 + }, + "_comment": "The variance explained lower threshold is calculated as the 25th percentile of variance explained from provisionally accepted components." + }, + { + "functionname": "calc_extend_factor", + "parameters": {}, + "_comment": "'extend factor' is a scaling number that is used for a few thresholds. 2 if fewer than 90 fMRI volumes, 3 if more than 110 and linear in-between. In the original MEICA, this was discrete with no linear slope between 90 & 110 so this might result in a small difference in results from MEICA for runs with 91-109 volumes." + }, + { + "functionname": "calc_max_good_meanmetricrank", + "parameters": {"decide_comps": "provisionalaccept"}, + "_comment": "'max_good_meanmetricrank' is the number of provisionalaccept components * extend_factor" + }, + { + "functionname": "calc_varex_kappa_ratio", + "parameters": {"decide_comps": "provisionalaccept"}, + "_comment": "'varex kappa ratio' is a new column in the component table. It's calcualted from the provisionally accepted components and is the maximum kappa minus the minimum kappa, divided by the maximum variance explained minus the minimum variance explained." + }, + { + "functionname": "dec_left_op_right", + "parameters": { + "if_true": "rejected", + "if_false": "nochange", + "decide_comps": ["provisionalaccept", "unclassified", "unclass_highvar"], + "op": ">", + "left": "d_table_score", + "right": "max_good_meanmetricrank" + }, + "kwargs": { + "op2": ">", + "left2": "variance explained", + "right2": "varex_upper_thresh", + "right2_scale": "extend_factor", + "tag_if_true": "Less likely BOLD", + "log_extra_info": "If variance and d_table_scores are high, then reject" + }, + "_comment": "One of several steps that makes it more likely to reject high variance components. Any provisionally accepted, unclassified, or unclassified high-variance components with a decision table score greater than 'max_good_meanmetricrank' and variance explained greater than the variance explained upper threshold multiplied by the extend factor are rejected. (Code I007 in premodularized tedana.)" + }, + { + "functionname": "dec_left_op_right", + "parameters": { + "if_true": "accepted", + "if_false": "nochange", + "decide_comps": ["provisionalaccept", "unclassified", "unclass_highvar"], + "op": ">", + "left": "d_table_score", + "right": "max_good_meanmetricrank" + }, + "kwargs": { + "tag_if_true": "Low variance", + "op2": "<=", + "left2": "variance explained", + "right2": "varex_lower_thresh", + "op3": "<=", + "left3": "kappa", + "right3": "kappa_elbow_kundu", + "log_extra_info": "If low variance, accept even if bad kappa & d_table_scores" + }, + "_comment": "Any provisionally accepted, unclassified, or unclassified high-variance components with a decision table score greater than 'max_good_meanmetricrank', variance explained less than or equal to the variance explained lower threshold, and kappa less than or equal to the kappa elbow will be accepted and labeled as 'low variance'. (Code I008 in premodularized tedana)" + }, + { + "functionname": "dec_classification_doesnt_exist", + "parameters": { + "new_classification": "accepted", + "decide_comps": ["provisionalaccept", "unclassified", "unclass_highvar"], + "class_comp_exists": ["unclassified", "unclass_highvar"] + }, + "kwargs": { + "tag": "Likely BOLD", + "log_extra_info": "If nothing left is unclassified, then accept all" + }, + "_comment": "If no components are still labeled as unclassified or unclassified high-variance, then all remaining provisionally accepted components are accepted." + }, + { + "functionname": "calc_revised_meanmetricrank_guesses", + "parameters": { + "decide_comps": ["provisionalaccept", "unclassified", "unclass_highvar"] + }, + "_comment": "If any components are still labeled as unclassified or unclassified high-variance, then a revised decision table score is calculated from the provisionally accepted, unclassified, and unclassified high-variance components." + }, + { + "functionname": "dec_left_op_right", + "parameters": { + "if_true": "rejected", + "if_false": "nochange", + "decide_comps": ["provisionalaccept", "unclassified", "unclass_highvar"], + "op": ">", + "left": "d_table_score_node20", + "right": "conservative_guess" + }, + "kwargs": { + "tag_if_true": "Less likely BOLD", + "op2": ">", + "left2": "varex kappa ratio", + "right2": "extend_factor", + "right2_scale": 2, + "op3": ">", + "left3": "variance explained", + "right3": "varex_upper_thresh", + "right3_scale": "extend_factor", + "log_extra_info": "Reject if a combination of kappa, variance, and other factors are ranked worse than others" + }, + "_comment": "A quirky combination of a bunch of metrics that deal with rejecting some edge cases. Any provisionally accepted, unclassified, or unclassified high-variance components with a revised decision tree score greater than the 'conservative_guess', variance explained-kappa ratio greater than the extend factor times two, and variance explained greater than the variance explained upper threshold times the extend factor are rejected. In meica v2.5 if_true is provisionalreject and these are potentially accepted later. (Code I009 in premodularized tedana)" + }, + { + "functionname": "dec_left_op_right", + "parameters": { + "if_true": "rejected", + "if_false": "nochange", + "decide_comps": ["provisionalaccept", "unclassified", "unclass_highvar"], + "op": ">", + "left": "d_table_score_node20", + "right": "num_acc_guess" + }, + "kwargs": { + "tag_if_true": "Less likely BOLD", + "right_scale": 0.9, + "op2": ">", + "left2": "variance explained", + "right2": "varex_lower_thresh", + "right2_scale": "extend_factor", + "log_extra_info": "Reject if a combination of variance and ranks of other metrics are worse than others" + }, + "_comment": "A quirky combination of a bunch of metrics that deal with rejecting some edge cases. Any provisionally accepted, unclassified, or unclassified high-variance components with a revised decision table score greater than 'num_acc_guess' times 0.9 and variance explained greater than variance explained lower threshold times the extend factor are rejected. In meica v2.5 if_true is provisionalreject and these are potentially accepted later. (Code I010 in premodularized tedana)" + }, + { + "functionname": "calc_varex_thresh", + "parameters": { + "decide_comps": ["provisionalaccept", "unclassified", "unclass_highvar"], + "thresh_label": "new_lower", + "percentile_thresh": 25 + }, + "kwargs": {"num_highest_var_comps": "num_acc_guess"}, + "_comment": "An updated variance explained lower threshold (25th percentile) is calculated from the 'num_acc_guess' highest variance explained components among the remaining provisionally accepted, unclassified, and unclassified high-variance components. In meica v2.5 additional components classified as provisionalreject are includes in this calculation. " + }, + { + "functionname": "dec_left_op_right", + "parameters": { + "if_true": "accepted", + "if_false": "nochange", + "decide_comps": ["provisionalaccept", "unclassified", "unclass_highvar"], + "op": ">", + "left": "d_table_score_node20", + "right": "num_acc_guess" + }, + "kwargs": { + "tag_if_true": "Accept borderline", + "op2": ">", + "left2": "variance explained", + "right2": "varex_new_lower_thresh", + "log_extra_info": "Accept components with a bad d_table_score, but are at the higher end of the remaining variance so more cautious to not remove" + }, + "_comment": "Another quirky criterion, but this one to keep components. Any provisionally accepted, unclassified, or unclassified high-variance components with a revised decision table score greater than 'num_acc_guess' and variance explained greater than the new variance explained lower threshold are accepted and labeled as 'borderline'. Prior to tedana vs 23.0.1 a mistake meant varex_new_lower_thresh would be lower than it is here and that might cause different results. In meica v2.5 components rejecte here could be provisionalreject and then accepted in this step. (Code I011 in premodularized tedana)." + }, + { + "functionname": "dec_left_op_right", + "parameters": { + "if_true": "accepted", + "if_false": "nochange", + "decide_comps": ["provisionalaccept", "unclassified", "unclass_highvar"], + "op": "<=", + "left": "kappa", + "right": "kappa_elbow_kundu" + }, + "kwargs": { + "tag_if_true": "Accept borderline", + "op2": ">", + "left2": "variance explained", + "right2": "varex_new_lower_thresh", + "log_extra_info": "For not already rejected components, accept ones below the kappa elbow, but at the higher end of the remaining variance so more cautious to not remove" + }, + "_comment": "Another quirky criterion to keep components. Any provisionally accepted, unclassified, or unclassified high-variance components with kappa less than or equal to the kappa elbow and variance explained greater than the new variance explained lower threshold are accepted and labeled as 'borderline'. Prior to tedana vs 23.0.1 a mistake meant varex_new_lower_thresh would be lower than it is here and that might cause different results. In meica v2.5 components rejecte here could be provisionalreject and then accepted in this step. (Code I012 in premodularized tedana)" + }, + { + "functionname": "manual_classify", + "parameters": { + "new_classification": "accepted", + "decide_comps": ["provisionalaccept", "unclassified", "unclass_highvar"] + }, + "kwargs": { + "tag": "Likely BOLD", + "log_extra_info": "Anything still provisional (accepted or rejected) or unclassified should be accepted" + }, + "_comment": "All remaining unclassified, unclassified high-variance, or provisionally accepted components are accepted." + } + ] +} diff --git a/tedana/resources/references.bib b/tedana/resources/references.bib index fddbafad9..366fd3e63 100644 --- a/tedana/resources/references.bib +++ b/tedana/resources/references.bib @@ -331,5 +331,5 @@ @article{tedana_decision_trees author = {tedana community}, journal = {figshare}, year = {2024}, - doi = {10.6084/m9.figshare.25251433.v1} + doi = {10.6084/m9.figshare.25251433.v2} } diff --git a/tedana/selection/component_selector.py b/tedana/selection/component_selector.py index a29183416..f78477f92 100644 --- a/tedana/selection/component_selector.py +++ b/tedana/selection/component_selector.py @@ -23,7 +23,7 @@ # A user can run the desision tree either using one of these # names or by giving the full path to a tree in a different # location -DEFAULT_TREES = ["minimal", "kundu"] +DEFAULT_TREES = ["minimal", "meica", "tedana_orig"] class TreeError(Exception): @@ -47,6 +47,13 @@ def load_config(tree): """ if tree in DEFAULT_TREES: fname = op.join(get_resource_path(), "decision_trees", tree + ".json") + elif tree == "kundu": + LGR.warning( + "The decision tree that used to be called kundu is now called tedana_orig. " + "This will be run using tedana_orig" + ) + tree = "tedana_orig" + fname = op.join(get_resource_path(), "decision_trees", tree + ".json") else: fname = tree @@ -287,10 +294,10 @@ def select(self, component_table, cross_component_metrics={}, status_table=None) n_echos : :obj:`int` Number of echos in multi-echo fMRI data. - Required for kundu and minimal trees + Required for tedana_orig, meica, and minimal trees n_vols : :obj:`int` Number of volumes (time points) in the fMRI data - Required for kundu tree + Required for tedana_orig and meica trees An example initialization with these options would look like ``selector = selector.select(comptable, n_echos=n_echos, n_vols=n_vols)`` diff --git a/tedana/selection/tedica.py b/tedana/selection/tedica.py index d757a6fc6..c12d0870f 100644 --- a/tedana/selection/tedica.py +++ b/tedana/selection/tedica.py @@ -15,10 +15,8 @@ def automatic_selection(component_table, selector, **kwargs): ---------- component_table : :obj:`pd.DataFrame` The component table to classify - n_echos : :obj:`int` - The number of echoes in this dataset - tree : :obj:`str` - The type of tree to use for the ComponentSelector object. Default="kundu" + selector : :obj:`tedana.selection.component_selector.ComponentSelector` + A selector object initialized with a decision tree Returns ------- @@ -28,10 +26,12 @@ def automatic_selection(component_table, selector, **kwargs): Notes ----- - If tree=kundu, the selection algorithm used in this function was originated in ME-ICA - by Prantik Kundu, and his original implementation is available at: - https://github.com/ME-ICA/me-ica/blob/\ + If selector.tree=meica, the selection algorithm used in this function was + originated in ME-ICA by Prantik Kundu, and his original implementation is + available at: https://github.com/ME-ICA/me-ica/blob/\ b2781dd087ab9de99a2ec3925f04f02ce84f0adc/meica.libs/select_model.py + The tedana_orig tree is very similar to meica, but might accept fewer + edge-case components. The appropriate citation is :footcite:t:`kundu2013integrated`. @@ -43,8 +43,8 @@ def automatic_selection(component_table, selector, **kwargs): components, a hypercommented version of this attempt is available at: https://gist.github.com/emdupre/ca92d52d345d08ee85e104093b81482e - If tree=="minimal", a selection algorithm based on the "kundu" tree will be used. - The differences between the "minimal" and "kundu" trees are described in the `FAQ`_. + If tree=="minimal", a selection algorithm based on the "meica" tree will be used. + The differences between the "minimal" and "meica" trees are described in the `FAQ`_. References ---------- @@ -52,7 +52,8 @@ def automatic_selection(component_table, selector, **kwargs): .. _FAQ: faq.html """ - LGR.info("Performing ICA component selection with Kundu decision tree v2.5") + LGR.info("Performing ICA component selection") + RepLGR.info( "\n\nNext, component selection was performed to identify BOLD (TE-dependent) and " "non-BOLD (TE-independent) components using a decision tree." diff --git a/tedana/tests/test_component_selector.py b/tedana/tests/test_component_selector.py index f191df39f..a8ed26231 100644 --- a/tedana/tests/test_component_selector.py +++ b/tedana/tests/test_component_selector.py @@ -9,6 +9,7 @@ import pytest from tedana.selection import component_selector +from tedana.utils import get_resource_path THIS_DIR = os.path.dirname(os.path.abspath(__file__)) @@ -168,6 +169,15 @@ def test_load_config_succeeds(): tree = component_selector.load_config("minimal") assert tree["tree_id"] == "minimal_decision_tree" + # Load the meica tree as a json file rather than just the label + fname = op.join(get_resource_path(), "decision_trees", "meica.json") + tree = component_selector.load_config(fname) + assert tree["tree_id"] == "MEICA_decision_tree" + + # If "kundu" is used as a tree, it should log a warning and output the tedana_orig tree + tree = component_selector.load_config("kundu") + assert tree["tree_id"] == "tedana_orig_decision_tree" + def test_minimal(): """Smoke test for constructor for ComponentSelector using minimal tree.""" @@ -188,7 +198,8 @@ def test_minimal(): def test_validate_tree_succeeds(): - """Test to make sure validate_tree suceeds for all default trees. + """ + Tests to make sure validate_tree suceeds for all default decision trees. Tested on all default trees in ./tedana/resources/decision_trees Note: If there is a tree in the default trees directory that diff --git a/tedana/workflows/tedana.py b/tedana/workflows/tedana.py index dab1352bd..ed494465f 100644 --- a/tedana/workflows/tedana.py +++ b/tedana/workflows/tedana.py @@ -157,12 +157,12 @@ def _get_parser(): dest="tree", help=( "Decision tree to use. You may use a " - "packaged tree (kundu, minimal) or supply a JSON " + "packaged tree (tedana_orig, meica, minimal) or supply a JSON " "file which matches the decision tree file " "specification. Minimal still being tested with more" "details in docs" ), - default="kundu", + default="tedana_orig", ) optional.add_argument( "--seed", @@ -322,7 +322,7 @@ def tedana_workflow( prefix="", fittype="loglin", combmode="t2s", - tree="kundu", + tree="tedana_orig", tedpca="aic", fixed_seed=42, maxit=500, @@ -374,14 +374,18 @@ def tedana_workflow( Default is 'loglin'. combmode : {'t2s'}, optional Combination scheme for TEs: 't2s' (Posse 1999, default). - tree : {'kundu', 'minimal', 'json file'}, optional + tree : {'tedana_orig', 'meica', 'minimal', 'json file'}, optional Decision tree to use for component selection. Can be a - packaged tree (kundu, minimal) or a user-supplied JSON file that - matches the decision tree file specification. Minimal is intented - to be a simpler process that is a bit more conservative, but it - accepts and rejects some distinct components compared to kundu. - Testing to better understand the effects of the differences is ongoing. - Default is 'kundu'. + packaged tree (tedana_orig, meica, minimal) or a user-supplied JSON file that + matches the decision tree file specification. tedana_orig is the tree that has + been distributed with tedana from the beginning and was designed to match the + process in MEICA. A difference between that tree and the older MEICA was + identified so the original meica tree is also included. meica will always + accept the same or more components, but those accepted components are sometimes + high variance so the differences can be non-trivial. Minimal is intented + to be a simpler process, but it accepts and rejects some distinct components + compared to the others. Testing to better understand the effects of the + differences is ongoing. Default is 'tedana_orig'. tedpca : {'mdl', 'aic', 'kic', 'kundu', 'kundu-stabilize', float, int}, optional Method with which to select components in TEDPCA. If a float is provided, then it is assumed to represent percentage of variance