|
1 | 1 | # emacs: -*- mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
|
2 | 2 | # vi: set ft=python sts=4 ts=4 sw=4 et:
|
3 |
| -""" This example is now out of date - please remind us to fix it |
| 3 | +""" Example of more than one run in the same model |
4 | 4 | """
|
5 | 5 | import numpy as np
|
6 |
| -import sympy |
7 | 6 |
|
8 | 7 | from nipy.algorithms.statistics.api import Term, Formula, Factor
|
9 | 8 | from nipy.modalities.fmri import utils, hrf
|
10 | 9 |
|
11 |
| -# hrf |
| 10 | +# hrf models we will use for each run. Just to show it can be done, use a |
| 11 | +# different hrf model for each run |
| 12 | +h1 = hrf.glover |
| 13 | +h2 = hrf.afni |
12 | 14 |
|
13 |
| -h1 = sympy.Function('hrf1') |
14 |
| -h2 = sympy.Function('hrf2') |
| 15 | +# Symbol for time in general. The 'events' function below will return models in |
| 16 | +# terms of 't', but we'll want models in terms of 't1' and 't2'. We need 't' |
| 17 | +# here so we can substitute. |
15 | 18 | t = Term('t')
|
16 | 19 |
|
17 |
| -# Session 1 |
18 |
| - |
19 |
| -t1 = Term('t1') |
20 |
| -c11 = utils.events([3,7,10], f=h1); c11 = c11.subs(t, t1) |
21 |
| -c21 = utils.events([1,3,9], f=h1); c21 = c21.subs(t, t1) |
22 |
| -c31 = utils.events([2,4,8], f=h1); c31 = c31.subs(t, t1) |
23 |
| -d1 = utils.fourier_basis([0.3,0.5,0.7]); d1 = d1.subs(t, t1) |
24 |
| -tval1 = np.linspace(0,20,101) |
25 |
| - |
| 20 | +# run 1 |
| 21 | +t1 = Term('t1') # Time within run 1 |
| 22 | +c11 = utils.events([3, 7, 10], f=h1) # Condition 1, run 1 |
| 23 | +# The events utility returns a formula in terms of 't' - general time |
| 24 | +c11 = c11.subs(t, t1) # Now make it in terms of time in run 1 |
| 25 | +# Same for conditions 2 and 3 |
| 26 | +c21 = utils.events([1, 3, 9], f=h1); c21 = c21.subs(t, t1) |
| 27 | +c31 = utils.events([2, 4, 8], f=h1); c31 = c31.subs(t, t1) |
| 28 | +# Add also a fourier basis set for drift with frequencies 0.3, 0.5, 0.7 |
| 29 | +d1 = utils.fourier_basis([0.3, 0.5, 0.7]); d1 = d1.subs(t, t1) |
| 30 | + |
| 31 | +# Here's our formula for run 1 signal terms of time in run 1 (t1) |
26 | 32 | f1 = Formula([c11,c21,c31]) + d1
|
27 | 33 |
|
28 |
| -# Session 2 |
29 |
| - |
30 |
| -t2 = Term('t2') |
31 |
| -c12 = utils.events([3.3,7,10], f=h2); c12 = c12.subs(t, t2) |
32 |
| -c22 = utils.events([1,3.2,9], f=h2); c22 = c22.subs(t, t2) |
33 |
| -c32 = utils.events([2,4.2,8], f=h2); c32 = c32.subs(t, t2) |
34 |
| -d2 = utils.fourier_basis([0.3,0.5,0.7]); d2 = d2.subs(t, t2) |
35 |
| -tval2 = np.linspace(0,10,51) |
36 |
| - |
37 |
| -f2 = Formula([c12,c22,c32]) + d2 |
38 |
| - |
39 |
| -sess_factor = Factor('sess', [1,2]) |
40 |
| - |
41 |
| -# The multi session formula |
42 |
| - |
43 |
| -f = Formula([sess_factor.terms[0]]) * f1 + Formula([sess_factor.terms[1]]) * f2 |
44 |
| - |
45 |
| -# Now, we evaluate the formula |
46 |
| -# the arrays now have 152=101+51 rows... |
47 |
| - |
| 34 | +# run 2 |
| 35 | +t2 = Term('t2') # Time within run 2 |
| 36 | +# Conditions 1 through 3 in run 2 |
| 37 | +c12 = utils.events([3.3, 7, 10], f=h2); c12 = c12.subs(t, t2) |
| 38 | +c22 = utils.events([1, 3.2, 9], f=h2); c22 = c22.subs(t, t2) |
| 39 | +c32 = utils.events([2, 4.2, 8], f=h2); c32 = c32.subs(t, t2) |
| 40 | +d2 = utils.fourier_basis([0.3, 0.5, 0.7]); d2 = d2.subs(t, t2) |
| 41 | + |
| 42 | +# Formula for run 2 signal in terms of time in run 2 (t2) |
| 43 | +f2 = Formula([c12, c22, c32]) + d2 |
| 44 | + |
| 45 | +# Factor giving constant for run. The [1, 2] means that there are two levels to |
| 46 | +# this factor, and that when we get to pass in values for this factor, |
| 47 | +# instantiating an actual design matrix (see below), a value of 1 means level |
| 48 | +# 1 and a value of 2 means level 2. |
| 49 | +run_factor = Factor('run', [1, 2]) |
| 50 | +run_1_coder = run_factor.get_term(1) # Term coding for level 1 |
| 51 | +run_2_coder = run_factor.get_term(2) # Term coding for level 2 |
| 52 | + |
| 53 | +# The multi run formula will combine the indicator (dummy value) terms from the |
| 54 | +# run factor with the formulae for the runs (which are functions of (run1, run2) |
| 55 | +# time. The run_factor terms are step functions that are zero when not in the |
| 56 | +# run, 1 when in the run. |
| 57 | +f = Formula([run_1_coder]) * f1 + Formula([run_2_coder]) * f2 + run_factor |
| 58 | + |
| 59 | +# Now, we evaluate the formula. So far we've been entirely symbolic. Now we |
| 60 | +# start to think about the values at which we want to evaluate our symbolic |
| 61 | +# formula. |
| 62 | + |
| 63 | +# We'll use these values for time within run 1. The times are in seconds from |
| 64 | +# the beginning of run 1. In our case run 1 was 20 seconds long. 101 below |
| 65 | +# gives 101 values from 0 to 20 including the endpoints, giving a dt of 0.2. |
| 66 | +tval1 = np.linspace(0, 20, 101) |
| 67 | +# run 2 lasts 10 seconds. These are the times in terms of the start of run 2. |
| 68 | +tval2 = np.linspace(0, 10, 51) |
| 69 | + |
| 70 | +# We pad out the tval1 / tval2 time vectors with zeros corresponding to the |
| 71 | +# TRs in run 2 / run 1. |
48 | 72 | ttval1 = np.hstack([tval1, np.zeros(tval2.shape)])
|
49 | 73 | ttval2 = np.hstack([np.zeros(tval1.shape), tval2])
|
50 |
| -session = np.array([1]*tval1.shape[0] + [2]*tval2.shape[0]) |
51 |
| - |
52 |
| -f = f.subs(h1, hrf.glover) |
53 |
| -f = f.subs(h2, hrf.glover) |
| 74 | +# The arrays above now have 152=101+51 rows... |
54 | 75 |
|
55 |
| -# Create the recarray that will be used to create the design matrix |
| 76 | +# Vector of run numbers for each time point (with values 1 or 2) |
| 77 | +run_no = np.array([1]*tval1.shape[0] + [2]*tval2.shape[0]) |
56 | 78 |
|
57 |
| -rec = np.array([(t1,t2, s) for t1, t2, s in zip(ttval1, ttval2, session)], |
| 79 | +# Create the recarray that will be used to create the design matrix. The |
| 80 | +# recarray gives the actual values for the symbolic terms in the formulae. In |
| 81 | +# our case the terms are t1, t2, and the (indicator coding) terms from the run |
| 82 | +# factor. |
| 83 | +rec = np.array([(tv1, tv2, s) for tv1, tv2, s in zip(ttval1, ttval2, run_no)], |
58 | 84 | np.dtype([('t1', np.float),
|
59 | 85 | ('t2', np.float),
|
60 |
| - ('sess', np.int)])) |
| 86 | + ('run', np.int)])) |
61 | 87 |
|
62 | 88 | # The contrast we care about
|
63 |
| - |
64 |
| -# It would be a good idea to be able to create |
65 |
| -# the contrast from the Formula, "f" above, |
66 |
| -# applying all of f's aliases to it.... |
67 |
| - |
68 |
| -sess_1 = sess_factor.get_term(1) |
69 |
| -sess_2 = sess_factor.get_term(2) |
70 |
| -contrast = Formula([sess_1*c11-sess_2*c12]) |
71 |
| -contrast = contrast.subs(h1, hrf.glover) |
72 |
| -contrast = contrast.subs(h2, hrf.glover) |
| 89 | +contrast = Formula([run_1_coder * c11 - run_2_coder * c12]) |
73 | 90 |
|
74 | 91 | # # Create the design matrix
|
75 |
| - |
76 | 92 | X = f.design(rec, return_float=True)
|
77 | 93 |
|
| 94 | +# Show ourselves the design space covered by the contrast, and the corresponding |
| 95 | +# contrast matrix |
78 | 96 | preC = contrast.design(rec, return_float=True)
|
79 |
| - |
| 97 | +# C is the matrix such that preC = X.dot(C.T) |
80 | 98 | C = np.dot(np.linalg.pinv(X), preC)
|
81 | 99 | print C
|
82 | 100 |
|
83 |
| -nonzero = np.nonzero(np.fabs(C) >= 0.1)[0] |
| 101 | +# We can also get this by passing the contrast into the design creation. |
| 102 | +X, c = f.design(rec, return_float=True, contrasts=dict(C=contrast)) |
| 103 | +assert np.allclose(C, c['C']) |
| 104 | + |
| 105 | +# Show the names of the non-trivial elements of the contrast |
| 106 | +nonzero = np.nonzero(np.fabs(C) >= 1e-5)[0] |
84 | 107 | print (f.dtype.names[nonzero[0]], f.dtype.names[nonzero[1]])
|
85 |
| -print ((sess_2*c12).subs(h2, hrf.glover), (sess_1*c11).subs(h1, hrf.glover)) |
| 108 | +print ((run_1_coder * c11), (run_2_coder * c12)) |
0 commit comments