Brain-behaviour association simulation example

A multiverse is defined as a dictionary of decision/option pairs. For example, consider a hypothetical analysis pipeline which correlates global efficiency of static brain networks with general cognitive abiity. Such an analysis pipeline involves many decision points, for example the software package for preprocessing, parametrs for data cleaning, or different methods for the connectivity estimation.

We here illustrate this with a hypothetical multiverse with the following forking paths:

  • 3 software packages (fMRIprep, CONN, or SPM) for preprocessing

  • 2 options for global signal regression (True/False)

  • 4 parcellation schemes (AAL, Schaefer 200, or Glasser MMP atlas)

  • 3 connectivity measures (pearson correlation, partial correlation, or mutual information)

  • 3 density values for thresholding the connectivity matrix (10%, 30%, and 50%)

The total number of possible combinations is \(3 \times 2 \times 4 \times 3 \times 3 = 216\), which means our multiverse will contain 216 universes.

[1]:
from comet.multiverse import Multiverse

forking_paths = {
    "software": ["fMRIprep", "CONN", "SPM"],
    "gsr": [True, False],
    "parcellation": ["AAL", "Power 160", "Schaefer 200", "Glasser MMP"],
    "connectivity": ["pearson", "partial", "mutual info"],
    "threshold": [0.1, 0.3, 0.5],
}

We then simulate some results by adding systematic differences (depending on the analytical choices) and noise to a baseline value:

[2]:
def analysis_template():
    import comet
    import numpy as np
    from scipy.stats import ttest_1samp

    # Base correlation between global efficiency and cognitive ability
    base_correlation = 0.15

    # Global signal regression reduces the association by 0.1
    if {{gsr}} == True:
        base_correlation -= 0.1
    # More detailed parcellations increase the association by 0.1
    if {{parcellation}} in ["Schaefer 200", "Glasser MMP"]:
        base_correlation += 0.1
    # Pearson correlation increases the association by 0.03
    if {{connectivity}} == "pearson":
        base_correlation += 0.03
    # Higher threshold increase the association
    base_correlation += {{threshold}} * 0.1

    # Simulate 100 participants with some added variability
    brain_behaviour = [base_correlation + np.random.normal(0, 1) for _ in range(100)]
    _, p_val = ttest_1samp(brain_behaviour, popmean=0)

    result = {
        "brain_behaviour": [round(association, 3) for association in brain_behaviour],
        "p_val": round(p_val, 3),
    }

    comet.utils.save_universe_results(result)

With the forking paths and the analysis template defined, we can create and visualize the multiverse:

[3]:
mverse = Multiverse(name="example_mv_features")
mverse.create(analysis_template, forking_paths)
mverse.summary()
mverse.visualize(universe=42, figsize=(10, 4))
Universe Decision 1 Value 1 Decision 2 Value 2 Decision 3 Value 3 Decision 4 Value 4 Decision 5 Value 5
0 Universe_1 software fMRIprep gsr True parcellation AAL connectivity pearson threshold 0.1
1 Universe_2 software fMRIprep gsr True parcellation AAL connectivity pearson threshold 0.3
2 Universe_3 software fMRIprep gsr True parcellation AAL connectivity pearson threshold 0.5
3 Universe_4 software fMRIprep gsr True parcellation AAL connectivity partial threshold 0.1
4 Universe_5 software fMRIprep gsr True parcellation AAL connectivity partial threshold 0.3
... ... ... ... ... ... ... ... ... ... ... ...
211 Universe_212 software SPM gsr False parcellation Glasser MMP connectivity partial threshold 0.3
212 Universe_213 software SPM gsr False parcellation Glasser MMP connectivity partial threshold 0.5
213 Universe_214 software SPM gsr False parcellation Glasser MMP connectivity mutual info threshold 0.1
214 Universe_215 software SPM gsr False parcellation Glasser MMP connectivity mutual info threshold 0.3
215 Universe_216 software SPM gsr False parcellation Glasser MMP connectivity mutual info threshold 0.5

216 rows × 11 columns

../../_images/sections_notebooks_example_mv_features_5_1.png

And subsequently run it:

[ ]:
mverse.run(parallel=8)

Once completed, the results can be visualised in a specification curve:

[5]:
mverse.specification_curve(measure="brain_behaviour", baseline=0, p_value=0.05, ci=95, smooth_ci=True,
                           cmap="Set3", figsize=(10,9), fontsize=10, height_ratio=(1,1), line_pad=0.1, ftype="pdf")
../../_images/sections_notebooks_example_mv_features_9_0.png

Or as a multiverse plot:

Note: Both specification curves and multiverse plots can be provided with a name map for nicer visualisation

[6]:
name_map = {
    "brain_behaviour": "Correlation between global efficiency and general cognitive ability",
    "software": "Analysis Software",
    "gsr": "Global Signal Regression",
    "parcellation": "Parcellation scheme",
    "connectivity": "Connectivity measure",
    "threshold": "Network density"
}

mverse.multiverse_plot(measure="brain_behaviour", n_bins=20, sig_col="p_val", name_map=name_map, baseline=0)
../../_images/sections_notebooks_example_mv_features_11_0.png

Results can be accessed in multiple ways:

[7]:
# Get all results as a nested dictionary
results = mverse.get_results()
print("Universe 1 results:", results["universe_1"]["brain_behaviour"])

# Get single universe results
results = mverse.get_results(universe=1)
print("Universe 1 results:", results["brain_behaviour"])

# Get all results as a pandas DataFrame
df_results = mverse.get_results(as_df=True)
df_results
Universe 1 results: [-0.856, 0.494, 0.3, 0.354, -1.421, 1.165, 0.705, -2.198, 0.046, -0.618, 0.096, 0.138, 0.711, -1.252, 0.008, 1.021, -1.858, 1.11, -0.83, 0.69, 0.602, -0.484, -1.375, -0.085, -0.647, -2.876, 0.013, -0.178, 0.309, 0.38, -0.259, 0.769, 1.452, 0.338, -0.571, -0.308, 0.067, 1.369, -1.191, 0.591, 1.49, 0.652, 0.212, -0.422, 0.449, -0.971, 0.863, 0.264, -0.205, 0.466, -0.479, -0.41, -1.397, 0.849, -0.822, -1.567, 0.543, 1.708, 0.249, 2.123, 0.786, 0.636, -0.903, 0.099, 2.435, 2.165, -0.028, 2.523, -0.774, 0.345, -0.634, -0.607, -0.782, 0.748, 0.205, -0.275, 1.268, 0.45, 0.087, 0.692, 0.493, -0.278, 1.09, 1.176, -0.028, 0.378, -1.191, 1.295, -0.029, -0.136, -0.654, 0.821, -0.209, 0.446, 2.072, -0.479, -0.259, -2.544, 0.922, -0.142]
Universe 1 results: [-0.856, 0.494, 0.3, 0.354, -1.421, 1.165, 0.705, -2.198, 0.046, -0.618, 0.096, 0.138, 0.711, -1.252, 0.008, 1.021, -1.858, 1.11, -0.83, 0.69, 0.602, -0.484, -1.375, -0.085, -0.647, -2.876, 0.013, -0.178, 0.309, 0.38, -0.259, 0.769, 1.452, 0.338, -0.571, -0.308, 0.067, 1.369, -1.191, 0.591, 1.49, 0.652, 0.212, -0.422, 0.449, -0.971, 0.863, 0.264, -0.205, 0.466, -0.479, -0.41, -1.397, 0.849, -0.822, -1.567, 0.543, 1.708, 0.249, 2.123, 0.786, 0.636, -0.903, 0.099, 2.435, 2.165, -0.028, 2.523, -0.774, 0.345, -0.634, -0.607, -0.782, 0.748, 0.205, -0.275, 1.268, 0.45, 0.087, 0.692, 0.493, -0.278, 1.09, 1.176, -0.028, 0.378, -1.191, 1.295, -0.029, -0.136, -0.654, 0.821, -0.209, 0.446, 2.072, -0.479, -0.259, -2.544, 0.922, -0.142]
[7]:
universe brain_behaviour p_val decisions
1 universe_1 [-0.856, 0.494, 0.3, 0.354, -1.421, 1.165, 0.7... 0.296 {'Decision 1': 'software', 'Value 1': 'fMRIpre...
2 universe_2 [0.387, -1.957, -0.59, -0.708, 0.863, -0.817, ... 0.827 {'Decision 1': 'software', 'Value 1': 'fMRIpre...
3 universe_3 [-0.332, -0.036, -0.766, 2.475, 0.499, 0.224, ... 0.645 {'Decision 1': 'software', 'Value 1': 'fMRIpre...
4 universe_4 [-0.13, -0.246, 0.65, -2.288, 0.201, -0.883, -... 0.968 {'Decision 1': 'software', 'Value 1': 'fMRIpre...
5 universe_5 [1.564, -0.337, 0.25, 0.642, 0.762, 0.625, 0.2... 0.768 {'Decision 1': 'software', 'Value 1': 'fMRIpre...
... ... ... ... ...
212 universe_212 [0.575, 0.409, -0.434, 2.854, 0.657, 0.085, 0.... 0.003 {'Decision 1': 'software', 'Value 1': 'SPM', '...
213 universe_213 [1.41, 1.325, -0.57, 0.711, 0.266, 0.516, 1.54... 0.000 {'Decision 1': 'software', 'Value 1': 'SPM', '...
214 universe_214 [-1.01, 0.975, 0.556, 2.215, 1.199, 0.502, 0.2... 0.000 {'Decision 1': 'software', 'Value 1': 'SPM', '...
215 universe_215 [0.436, 0.755, -0.568, 2.722, -0.013, 0.679, 1... 0.000 {'Decision 1': 'software', 'Value 1': 'SPM', '...
216 universe_216 [-0.765, 1.346, 0.881, -1.109, 0.543, -1.718, ... 0.000 {'Decision 1': 'software', 'Value 1': 'SPM', '...

216 rows × 4 columns