def test_volumetric_input(): mask_image = nib.load( tflow.get("MNI152Lin", resolution="02", desc="brain", suffix="mask")) n_voxels = (mask_image.get_fdata() != 0).sum() n_subjects = 3 data = np.random.rand(n_subjects, n_voxels) model = FixedEffect(1) contrast = np.ones(3) slm = SLM(model, contrast, surf=mask_image) slm.fit(data)
def test_SLM(): """Tests the SLM model using a grid of parameters Raises ------ Exception First exception that occurs in computing the SLM. """ samples = 10 predictors = 3 grid = list(create_parameter_grid(samples, predictors)) Y = np.random.rand(samples, 10242, predictors) for i in range(len(grid)): # Skip exceptions that we know error. if grid[i]["surf"] is None: if grid[i]["correction"] is not None and "rft" in grid[i][ "correction"]: continue if grid[i]["Y_idx"] > 1 and grid[i]["two_tailed"] is False: continue try: slm = SLM( model=grid[i]["model"], contrast=grid[i]["contrast"], surf=grid[i]["surf"], mask=grid[i]["mask"], correction=grid[i]["correction"], two_tailed=grid[i]["two_tailed"], ) slm.fit(Y[:, :, 0:grid[i]["Y_idx"]]) except Exception as e: print("Error on run:", i) print("SLM failed with the following parameters:") print("Model: ", grid[i]["model"]) print("Contrast: ", grid[i]["contrast"]) print("Surface: ", grid[i]["surf"]) print("Mask: ", grid[i]["mask"]) print("Correction: ", grid[i]["correction"]) print("Two_tailed: ", grid[i]["two_tailed"]) print("Y_idx: ", grid[i]["Y_idx"]) raise e
model = term_intercept + term_age + term_iq ################################################################### # We can also add interaction effects to the model by multiplying terms. model_interaction = term_intercept + term_age + term_iq + term_age * term_iq ################################################################### # Now, lets imagine we have some cortical marker (e.g. cortical thickness) for # each subject and we want to evaluate whether this marker changes with age # whilst correcting for effects of sex and age-sex interactions. from brainstat.stats.SLM import SLM slm = SLM(model_interaction, -age, surf=pial_left, correction="rft", mask=mask) slm.fit(thickness) print(slm.t.shape) # These are the t-values of the model. print(slm.P["pval"]["P"]) # These are the random field theory derived p-values. ################################################################### # By default BrainStat uses a two-tailed test. If you want to get a one-tailed # test, simply specify it in the SLM model as follows: slm_two_tails = SLM( model_interaction, -age, surf=pial_left, correction="rft", two_tailed=False ) slm_two_tails.fit(thickness) ################################################################### # Now, imagine that instead of using a fixed effects model, you would prefer a # mixed effects model wherein handedness is a random variable. This is simple to
term_age = FixedEffect(demographics.AGE_AT_SCAN) term_sex = FixedEffect(demographics.SEX) term_subject = MixedEffect(demographics.SUB_ID) model = term_age + term_sex + term_age * term_sex + term_subject contrast_age = -model.mean.AGE_AT_SCAN slm = SLM( model, contrast_age, surf="fsaverage5", mask=mask, correction=["fdr", "rft"], two_tailed=False, cluster_threshold=0.01, ) slm.fit(thickness) ################################################################### # Genetics # -------- # # For genetic decoding we use the Allen Human Brain Atlas through the abagen # toolbox. Note that abagen only accepts parcellated data. Here is a minimal # example of how we use abagen to get the genetic expression of the 100 regions # of the Schaefer atlas and how to plot this expression to a matrix. Please note # that downloading the dataset and running this analysis can take several # minutes. import copy import matplotlib.pyplot as plt
def generate_test_data(): pial, mask, age, iq, thickness = load_training_data(n=20) fixed_model = FixedEffect(1) + FixedEffect(age, "age") mixed_model = ( FixedEffect(1) + FixedEffect(age, "age") + MixedEffect(iq, name_ran="iq") + MixedEffect(1, name_ran="Identity") ) variates_2 = np.concatenate( (thickness[:, :, None], np.random.random_sample(thickness.shape)[:, :, None]), axis=2, ) variates_3 = np.concatenate( ( thickness[:, :, None], np.random.rand(thickness.shape[0], thickness.shape[1], 2), ), axis=2, ) # Params 1: No surface, fixed effect. # Params 2: One-tailed mixed with theta/dr changes. # Params 3: With surface. and RFT correction. parameters = [ { "Y": [thickness, variates_2, variates_3], "model": [fixed_model], "contrast": [-age], "correction": [None, "fdr"], "surf": [None], "mask": [mask], "niter": [1], "thetalim": [0.01], "drlim": [0.1], "two_tailed": [True], "cluster_threshold": [0.001], }, { "Y": [thickness], "model": [mixed_model], "contrast": [-age], "correction": ["fdr"], "surf": [None, pial], "mask": [mask], "niter": [1], "thetalim": [0.01, 0.05], "drlim": [0.1, 0.2], "two_tailed": [False], "cluster_threshold": [0.001], }, { "Y": [thickness], "model": [fixed_model, mixed_model], "contrast": [-age], "surf": [pial], "mask": [mask], "correction": [None, ["fdr", "rft"]], "niter": [1], "thetalim": [0.01], "drlim": [0.1], "two_tailed": [True], "cluster_threshold": [0.001, 1.2], }, ] test_num = 0 for params in ParameterGrid(parameters): test_num += 1 slm = SLM( params["model"], params["contrast"], params["surf"], params["mask"], correction=params["correction"], niter=params["niter"], thetalim=params["thetalim"], drlim=params["drlim"], two_tailed=params["two_tailed"], cluster_threshold=params["cluster_threshold"], ) slm.fit(params["Y"]) # Save input/output if isinstance(params["model"], FixedEffect): params["model"] = age[:, None] else: params["model"] = np.concatenate((age[:, None], iq[:, None]), axis=1) dict2pkl(params, "slm", test_num, input=True) slm2files(slm, "slm", test_num, input=False)
def dummy_test(infile, expfile): # load input test data ifile = open(infile, "br") idic = pickle.load(ifile) ifile.close() slm = SLM(FixedEffect(1), FixedEffect(1)) # Data are saved a little differently from the actual input due to compatibility with MATLAB. # Data wrangle a bit to bring it back into the Python input format. for key in idic.keys(): if key == "Y": # Y is input for slm.fit(), not a property. continue if key == "model": # Model is saved as a matrix rather than a Fixed/MixedEffect if idic[key].shape[1] == 1: idic[key] = FixedEffect(1) + FixedEffect(idic[key]) else: idic[key] = (FixedEffect(1) + FixedEffect(idic[key][:, 0]) + MixedEffect(idic[key][:, 1]) + MixedEffect(1)) setattr(slm, key, idic[key]) if key == "surf" and slm.surf is not None: slm.surf["tri"] += 1 slm.fit(idic["Y"]) # load expected outout data efile = open(expfile, "br") out = pickle.load(efile) efile.close() # Format of self.P changed since files were created -- alter out to match some changes. # Combine the list outputs, sort with pandas, and return to list. if "P" in out: out["P"]["pval"]["C"] = _onetailed_to_twotailed( out["P"]["pval"]["C"][0], out["P"]["pval"]["C"][1]) for key1 in ["peak", "clus"]: P_tmp = [] none_squeeze = lambda x: np.squeeze(x) if x is not None else None for i in range(len(out["P"][key1]["P"])): tail_dict = { key: none_squeeze(value[i]) for key, value in out["P"][key1].items() } if tail_dict["P"] is not None: if tail_dict["P"].size == 1: P_tmp.append(pd.DataFrame.from_dict([tail_dict])) else: P_tmp.append(pd.DataFrame.from_dict(tail_dict)) P_tmp[i].sort_values(by="P", ascending=True) else: P_tmp.append(pd.DataFrame(columns=tail_dict.keys())) out["P"][key1] = P_tmp testout = [] skip_keys = ["model", "correction", "_tri", "surf"] for key in out.keys(): if key in skip_keys: continue if key == "P": testout.append(recursive_comparison(out[key], getattr(slm, key))) elif out[key] is not None: comp = np.allclose(out[key], getattr(slm, key), rtol=1e-05, equal_nan=True) testout.append(comp) assert all(flag == True for (flag) in testout)
# the lifespan. To do this, we can use the model we defined before, and a # contrast in observations (here: age). Then we simply initialize an SLM model # and fit it to the cortical thickness data. from brainstat.stats.SLM import SLM contrast_age = demographics.AGE_AT_SCAN slm_age = SLM( model, contrast_age, surf="fsaverage5", mask=mask, correction=["fdr", "rft"], cluster_threshold=0.01, ) slm_age.fit(thickness) ################################################################### # The resulting model, slm_age, will contain the t-statistic map, p-values # derived with the requested corrections, and a myriad of other properties (see # the API for more details). Lets plot the t-values and p-values on the surface. # We'll do this a few times throughout the tutorial so lets define a function to # do this. def plot_slm_results(slm, plot_peak=False, plot_fdr=False): handles = [local_plot_hemispheres(slm.t, ["t-values"], (-4, 4), "bwr")] plot_pvalues = [np.copy(slm.P["pval"]["C"])] labels = ["Cluster p-values"]