Getting started

Johan Medrano edited this page May 22, 2025 · 1 revision

0. Importing SPM Python

importspmimportnumpyasnp

1. Getting help on a function

To get help on a function or class, you can use the help function as you would in Matlab. For instance,

help(spm.spm_dcm_erp)
Help on function spm_dcm_erp in module spm.__toolbox.__dcm_meeg.spm_dcm_erp:
spm_dcm_erp(*args, **kwargs)
Estimate parameters of a DCM model (Variational Lapalce)
FORMAT [DCM,dipfit] = spm_dcm_erp(DCM)
DCM
name: name string
Lpos: Source locations
xY: data [1x1 struct]
xU: design [1x1 struct]
Sname: cell of source name strings
A: {[nr x nr double] [nr x nr double] [nr x nr double]}
B: {[nr x nr double], ...} Connection constraints
C: [nr x 1 double]
options.trials - indices of trials
options.Tdcm - [start end] time window in ms
options.D - time bin decimation (usually 1 or 2)
options.h - number of DCT drift terms (usually 1 or 2)
options.Nmodes - number of spatial models to invert
options.analysis - 'ERP', 'SSR' or 'IND'
options.model - 'ERP', 'SEP', 'CMC', 'CMM', 'NMM' or 'MFM'
options.spatial - 'ECD', 'LFP' or 'IMG'
options.onset - stimulus onset (ms)
options.dur - and dispersion (sd)
options.CVA - use CVA for spatial modes [default = 0]
options.Nmax - maxiumum number of iterations [default = 64]
dipfit - Dipole structure (for electromagnetic forward model)
See spm_dcm_erp_dipfit: this field is removed from DCM.M to save
memory - and is offered as an output argument if needed
The scheme can be initialised with parameters for the neuronal model
and spatial (observer) model by specifying the fields DCM.P and DCM.Q,
respectively. If previous priors (DCM.M.pE and pC or DCM.M.gE and gC or
DCM.M.hE and hC) are specified, they will be used. Explicit priors can be
useful for Bayesian parameter averaging - but would not normally be
called upon - because prior constraints are specified by DCM.A, DCM.B,...
__________________________________________________________________________
[Matlab code]( https://github.com/spm/spm/blob/main/toolbox/dcm_meeg/spm_dcm_erp.m )
Copyright (C) 1995-2025 Functional Imaging Laboratory, Department of Imaging Neuroscience, UCL

It also works for classes, either to get more info on the class itself...

help(spm.meeg)
Help on class meeg in module spm.meeg:
class meeg(mpython.matlab_class.MatlabClass)
| meeg(*args, **kwargs)
|
| Method resolution order:
| meeg
| mpython.matlab_class.MatlabClass
| mpython.core.base_types.MatlabType
| builtins.object

... or learn how to construct an object from it:

help(spm.meeg.__init__)
Help on function __init__ in module spm.meeg:
__init__(self, *args, **kwargs)
Function for creating meeg objects.
FORMAT
D = meeg;
returns an empty object
D = meeg(D);
converts a D struct to object or does nothing if already
object

2. Using Matlab-like types

SPM Python provides you with a type system that allows to reuse Matlab syntax without too much worries. The following classes are available:

  1. spm.Cell, for cell arrays
  2. spm.Struct, for struct arrays
  3. spm.Array, for general arrays

2.1. Base types

We've got cell arrays:

# Create an empty 1D Cell array with a shape of (3,)c=spm.Cell(3)
# Populate the Cell array with datac[0] ="Hello"c[1] ="World"c[2] =42# Print the Cell arrayprint("Initial Cell array:", c.tolist())
# Add a new element in (undefined) index 4c[4] ="New Element"# Print the updated Cell arrayprint("Updated Cell array:", c.tolist())
Initial Cell array: ['Hello', 'World', 42]
Updated Cell array: ['Hello', 'World', 42, Array([]), 'New Element']

Some struct arrays as well:

# Create an empty Structexample_struct=spm.Struct()
example_struct.name="Example"example_struct.value=42print("Single Struct example:", example_struct)
# Create a 1D Struct arraystruct_array_1d=spm.Struct(3)
struct_array_1d[0].name="First"struct_array_1d[1].name="Second"struct_array_1d[2].name="Third"print("1D Struct array example:", struct_array_1d)
# Create a 2D Struct arraystruct_array_2d=spm.Struct(2, 2)
struct_array_2d[0, 0].name="Top Left"struct_array_2d[0, 1].name="Top Right"struct_array_2d[1, 0].name="Bottom Left"struct_array_2d[1, 1].name="Bottom Right"print("2D Struct array example:")
print(struct_array_2d)
# Add a new field to the Structexample_struct.new_field="New Field Value"print("Updated Struct with new field:", example_struct)
Single Struct example: {'name': 'Example', 'value': 42}
1D Struct array example: [{'name': 'First'}, {'name': 'Second'}, {'name': 'Third'}]
2D Struct array example:
[[{'name': 'Top Left'}, {'name': 'Top Right'}],
[{'name': 'Bottom Left'}, {'name': 'Bottom Right'}]]
Updated Struct with new field: {'name': 'Example', 'value': 42, 'new_field': 'New Field Value'}

And some generic arrays too:

# Create an empty array (scalar)a=spm.Array()
print("Empty array:", a, "Shape:", a.shape)
# Create a 1D array of length 3a1d=spm.Array(3)
print("1D array:", a1d)
# Create a 2D array (3 rows, 2 columns)a2d=spm.Array(3, 2)
print("2D array:\n", a2d)
Empty array: 0.0 Shape: ()
1D array: [0.0, 0.0, 0.0]
2D array:
[[0.0, 0.0],
[0.0, 0.0],
[0.0, 0.0]]

2.2. Array operations

All of these types are derived from np.ndarray (thank you, Yael), which makes them really nice to work with if you're confortable with Numpy.

# Create a 1D struct arrays=spm.Struct(4)
# Populate the struct array with dataforiinrange(4):
s[i].value=is[i].label=f"item{i}"print("Original Struct:", s)
# Reshape the struct array to 2x2s_reshaped=s.reshape((2, 2))
print("Reshaped Struct (2x2):\n", s_reshaped)
# Transpose the struct arrays_transposed=np.transpose(s_reshaped)
Original Struct: [{'value': 0, 'label': 'item0'}, {'value': 1, 'label': 'item1'},
{'value': 2, 'label': 'item2'}, {'value': 3, 'label': 'item3'}]
Reshaped Struct (2x2):
[[{'value': 0, 'label': 'item0'}, {'value': 1, 'label': 'item1'}],
[{'value': 2, 'label': 'item2'}, {'value': 3, 'label': 'item3'}]]
Concatenated Struct (1D): [{'value': 0, 'label': 'item0'} {'value': 1, 'label': 'item1'}
{'value': 2, 'label': 'item2'} {'value': 3, 'label': 'item3'}
{'value': 10, 'label': 'item10'} {'value': 11, 'label': 'item11'}
{'value': 12, 'label': 'item12'} {'value': 13, 'label': 'item13'}]
# Concatenate along the first axiss2=spm.Struct(4)
foriinrange(4):
s2[i].value=i+10s2[i].label=f"item{i+10}"s_concat=np.concatenate([s, s2])
print("Concatenated Struct (1D):", s_concat)
Concatenated Struct (1D): [{'value': 0, 'label': 'item0'} {'value': 1, 'label': 'item1'}
{'value': 2, 'label': 'item2'} {'value': 3, 'label': 'item3'}
{'value': 10, 'label': 'item10'} {'value': 11, 'label': 'item11'}
{'value': 12, 'label': 'item12'} {'value': 13, 'label': 'item13'}]
# Concatenate along the first axiss2=spm.Struct(4)
foriinrange(4):
s2[i].value=i+10s2[i].label=f"item{i+10}"s_concat=np.concatenate([s, s2])
print("Concatenated Struct (1D):", s_concat)
# Create a 1D Cell arrayc=spm.Cell(4)
c[:] = ["hello", 123, {"a": 1}, [1, 2, 3]]
print("Original Cell:", c)
# Create a 1D Cell arrayc_extra=spm.Cell(2)
c_extra[:] = ["more", "cells"]
# Concatenate the Cell arraysc_concat=np.concatenate([c, c_extra])
print("Concatenated Cell (1D):", c_concat.tolist())
Original Cell: [hello, 123, [a], [1, 2, 3]]
Concatenated Cell (1D): ['hello', 123, Cell(['a']), Cell([1, 2, 3]), 'more', 'cells']

2.3. Type inference

One of the nice feature these types have is type inference at construction time. This enables accessing undefined field of an array, as long as the indexing sequence ends up with an assignment. There are a few extra rules:

  1. .: Use dot indexing to create a new field, as you'd use . in Matlab,
  2. []: Use square brackets for array indexing, as you'd use () in Matlab,
  3. (): Use round brackets for cell indexing, as you'd use {} in Matlab,

For example, to create a struct array with a field containing a cell array with, in third position, a struct array with a 2-by-2 random matrix in fifth position (showcasing all three rules):

S=spm.Struct()
S.field(3).struct[5].elem=np.random.rand(2, 2)
S
{'field': Cell([Array([]), Array([]), Array([]),
{'struct': Struct([{'elem': Array([])}, {'elem': Array([])}, {'elem': Array([])},
{'elem': Array([])}, {'elem': Array([])},
{'elem': Array([[0.38339607, 0.55686198],
[0.53678757, 0.5828017 ]])} ])} ])}

There is one caveat though: elements of unfinalised cell arrays cannot be specified:

>>>S=spm.Struct()
>>>S.field(3) ='test'S.field(3) ='test'^SyntaxError: cannotassigntofunctioncallhere. Maybeyoumeant'=='insteadof'='?

This is why we need one additional rule:

  1. as_cell[]: Initialising an element of an unfinalised cell array needs to use as_cell.

Using as_cell solves exactly this problem:

S=spm.Struct()
S.field.as_cell[3] ="test"S
{'field': Cell([Array([]), Array([]), Array([]), 'test'])}
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Getting started

Johan Medrano edited this page May 22, 2025 · 1 revision

0. Importing SPM Python

importspmimportnumpyasnp

1. Getting help on a function

To get help on a function or class, you can use the help function as you would in Matlab. For instance,

help(spm.spm_dcm_erp)
Help on function spm_dcm_erp in module spm.__toolbox.__dcm_meeg.spm_dcm_erp:
spm_dcm_erp(*args, **kwargs)
Estimate parameters of a DCM model (Variational Lapalce)
FORMAT [DCM,dipfit] = spm_dcm_erp(DCM)
DCM
name: name string
Lpos: Source locations
xY: data [1x1 struct]
xU: design [1x1 struct]
Sname: cell of source name strings
A: {[nr x nr double] [nr x nr double] [nr x nr double]}
B: {[nr x nr double], ...} Connection constraints
C: [nr x 1 double]
options.trials - indices of trials
options.Tdcm - [start end] time window in ms
options.D - time bin decimation (usually 1 or 2)
options.h - number of DCT drift terms (usually 1 or 2)
options.Nmodes - number of spatial models to invert
options.analysis - 'ERP', 'SSR' or 'IND'
options.model - 'ERP', 'SEP', 'CMC', 'CMM', 'NMM' or 'MFM'
options.spatial - 'ECD', 'LFP' or 'IMG'
options.onset - stimulus onset (ms)
options.dur - and dispersion (sd)
options.CVA - use CVA for spatial modes [default = 0]
options.Nmax - maxiumum number of iterations [default = 64]
dipfit - Dipole structure (for electromagnetic forward model)
See spm_dcm_erp_dipfit: this field is removed from DCM.M to save
memory - and is offered as an output argument if needed
The scheme can be initialised with parameters for the neuronal model
and spatial (observer) model by specifying the fields DCM.P and DCM.Q,
respectively. If previous priors (DCM.M.pE and pC or DCM.M.gE and gC or
DCM.M.hE and hC) are specified, they will be used. Explicit priors can be
useful for Bayesian parameter averaging - but would not normally be
called upon - because prior constraints are specified by DCM.A, DCM.B,...
__________________________________________________________________________
[Matlab code]( https://github.com/spm/spm/blob/main/toolbox/dcm_meeg/spm_dcm_erp.m )
Copyright (C) 1995-2025 Functional Imaging Laboratory, Department of Imaging Neuroscience, UCL

It also works for classes, either to get more info on the class itself...

help(spm.meeg)
Help on class meeg in module spm.meeg:
class meeg(mpython.matlab_class.MatlabClass)
| meeg(*args, **kwargs)
|
| Method resolution order:
| meeg
| mpython.matlab_class.MatlabClass
| mpython.core.base_types.MatlabType
| builtins.object

... or learn how to construct an object from it:

help(spm.meeg.__init__)
Help on function __init__ in module spm.meeg:
__init__(self, *args, **kwargs)
Function for creating meeg objects.
FORMAT
D = meeg;
returns an empty object
D = meeg(D);
converts a D struct to object or does nothing if already
object

2. Using Matlab-like types

SPM Python provides you with a type system that allows to reuse Matlab syntax without too much worries. The following classes are available:

  1. spm.Cell, for cell arrays
  2. spm.Struct, for struct arrays
  3. spm.Array, for general arrays

2.1. Base types

We've got cell arrays:

# Create an empty 1D Cell array with a shape of (3,)c=spm.Cell(3)
# Populate the Cell array with datac[0] ="Hello"c[1] ="World"c[2] =42# Print the Cell arrayprint("Initial Cell array:", c.tolist())
# Add a new element in (undefined) index 4c[4] ="New Element"# Print the updated Cell arrayprint("Updated Cell array:", c.tolist())
Initial Cell array: ['Hello', 'World', 42]
Updated Cell array: ['Hello', 'World', 42, Array([]), 'New Element']

Some struct arrays as well:

# Create an empty Structexample_struct=spm.Struct()
example_struct.name="Example"example_struct.value=42print("Single Struct example:", example_struct)
# Create a 1D Struct arraystruct_array_1d=spm.Struct(3)
struct_array_1d[0].name="First"struct_array_1d[1].name="Second"struct_array_1d[2].name="Third"print("1D Struct array example:", struct_array_1d)
# Create a 2D Struct arraystruct_array_2d=spm.Struct(2, 2)
struct_array_2d[0, 0].name="Top Left"struct_array_2d[0, 1].name="Top Right"struct_array_2d[1, 0].name="Bottom Left"struct_array_2d[1, 1].name="Bottom Right"print("2D Struct array example:")
print(struct_array_2d)
# Add a new field to the Structexample_struct.new_field="New Field Value"print("Updated Struct with new field:", example_struct)
Single Struct example: {'name': 'Example', 'value': 42}
1D Struct array example: [{'name': 'First'}, {'name': 'Second'}, {'name': 'Third'}]
2D Struct array example:
[[{'name': 'Top Left'}, {'name': 'Top Right'}],
[{'name': 'Bottom Left'}, {'name': 'Bottom Right'}]]
Updated Struct with new field: {'name': 'Example', 'value': 42, 'new_field': 'New Field Value'}

And some generic arrays too:

# Create an empty array (scalar)a=spm.Array()
print("Empty array:", a, "Shape:", a.shape)
# Create a 1D array of length 3a1d=spm.Array(3)
print("1D array:", a1d)
# Create a 2D array (3 rows, 2 columns)a2d=spm.Array(3, 2)
print("2D array:\n", a2d)
Empty array: 0.0 Shape: ()
1D array: [0.0, 0.0, 0.0]
2D array:
[[0.0, 0.0],
[0.0, 0.0],
[0.0, 0.0]]

2.2. Array operations

All of these types are derived from np.ndarray (thank you, Yael), which makes them really nice to work with if you're confortable with Numpy.

# Create a 1D struct arrays=spm.Struct(4)
# Populate the struct array with dataforiinrange(4):
s[i].value=is[i].label=f"item{i}"print("Original Struct:", s)
# Reshape the struct array to 2x2s_reshaped=s.reshape((2, 2))
print("Reshaped Struct (2x2):\n", s_reshaped)
# Transpose the struct arrays_transposed=np.transpose(s_reshaped)
Original Struct: [{'value': 0, 'label': 'item0'}, {'value': 1, 'label': 'item1'},
{'value': 2, 'label': 'item2'}, {'value': 3, 'label': 'item3'}]
Reshaped Struct (2x2):
[[{'value': 0, 'label': 'item0'}, {'value': 1, 'label': 'item1'}],
[{'value': 2, 'label': 'item2'}, {'value': 3, 'label': 'item3'}]]
Concatenated Struct (1D): [{'value': 0, 'label': 'item0'} {'value': 1, 'label': 'item1'}
{'value': 2, 'label': 'item2'} {'value': 3, 'label': 'item3'}
{'value': 10, 'label': 'item10'} {'value': 11, 'label': 'item11'}
{'value': 12, 'label': 'item12'} {'value': 13, 'label': 'item13'}]
# Concatenate along the first axiss2=spm.Struct(4)
foriinrange(4):
s2[i].value=i+10s2[i].label=f"item{i+10}"s_concat=np.concatenate([s, s2])
print("Concatenated Struct (1D):", s_concat)
Concatenated Struct (1D): [{'value': 0, 'label': 'item0'} {'value': 1, 'label': 'item1'}
{'value': 2, 'label': 'item2'} {'value': 3, 'label': 'item3'}
{'value': 10, 'label': 'item10'} {'value': 11, 'label': 'item11'}
{'value': 12, 'label': 'item12'} {'value': 13, 'label': 'item13'}]
# Concatenate along the first axiss2=spm.Struct(4)
foriinrange(4):
s2[i].value=i+10s2[i].label=f"item{i+10}"s_concat=np.concatenate([s, s2])
print("Concatenated Struct (1D):", s_concat)
# Create a 1D Cell arrayc=spm.Cell(4)
c[:] = ["hello", 123, {"a": 1}, [1, 2, 3]]
print("Original Cell:", c)
# Create a 1D Cell arrayc_extra=spm.Cell(2)
c_extra[:] = ["more", "cells"]
# Concatenate the Cell arraysc_concat=np.concatenate([c, c_extra])
print("Concatenated Cell (1D):", c_concat.tolist())
Original Cell: [hello, 123, [a], [1, 2, 3]]
Concatenated Cell (1D): ['hello', 123, Cell(['a']), Cell([1, 2, 3]), 'more', 'cells']

2.3. Type inference

One of the nice feature these types have is type inference at construction time. This enables accessing undefined field of an array, as long as the indexing sequence ends up with an assignment. There are a few extra rules:

  1. .: Use dot indexing to create a new field, as you'd use . in Matlab,
  2. []: Use square brackets for array indexing, as you'd use () in Matlab,
  3. (): Use round brackets for cell indexing, as you'd use {} in Matlab,

For example, to create a struct array with a field containing a cell array with, in third position, a struct array with a 2-by-2 random matrix in fifth position (showcasing all three rules):

S=spm.Struct()
S.field(3).struct[5].elem=np.random.rand(2, 2)
S
{'field': Cell([Array([]), Array([]), Array([]),
{'struct': Struct([{'elem': Array([])}, {'elem': Array([])}, {'elem': Array([])},
{'elem': Array([])}, {'elem': Array([])},
{'elem': Array([[0.38339607, 0.55686198],
[0.53678757, 0.5828017 ]])} ])} ])}

There is one caveat though: elements of unfinalised cell arrays cannot be specified:

>>>S=spm.Struct()
>>>S.field(3) ='test'S.field(3) ='test'^SyntaxError: cannotassigntofunctioncallhere. Maybeyoumeant'=='insteadof'='?

This is why we need one additional rule:

  1. as_cell[]: Initialising an element of an unfinalised cell array needs to use as_cell.

Using as_cell solves exactly this problem:

S=spm.Struct()
S.field.as_cell[3] ="test"S
{'field': Cell([Array([]), Array([]), Array([]), 'test'])}
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Getting started

Johan Medrano edited this page May 22, 2025 · 1 revision

0. Importing SPM Python

importspmimportnumpyasnp

1. Getting help on a function

To get help on a function or class, you can use the help function as you would in Matlab. For instance,

help(spm.spm_dcm_erp)
Help on function spm_dcm_erp in module spm.__toolbox.__dcm_meeg.spm_dcm_erp:
spm_dcm_erp(*args, **kwargs)
Estimate parameters of a DCM model (Variational Lapalce)
FORMAT [DCM,dipfit] = spm_dcm_erp(DCM)
DCM
name: name string
Lpos: Source locations
xY: data [1x1 struct]
xU: design [1x1 struct]
Sname: cell of source name strings
A: {[nr x nr double] [nr x nr double] [nr x nr double]}
B: {[nr x nr double], ...} Connection constraints
C: [nr x 1 double]
options.trials - indices of trials
options.Tdcm - [start end] time window in ms
options.D - time bin decimation (usually 1 or 2)
options.h - number of DCT drift terms (usually 1 or 2)
options.Nmodes - number of spatial models to invert
options.analysis - 'ERP', 'SSR' or 'IND'
options.model - 'ERP', 'SEP', 'CMC', 'CMM', 'NMM' or 'MFM'
options.spatial - 'ECD', 'LFP' or 'IMG'
options.onset - stimulus onset (ms)
options.dur - and dispersion (sd)
options.CVA - use CVA for spatial modes [default = 0]
options.Nmax - maxiumum number of iterations [default = 64]
dipfit - Dipole structure (for electromagnetic forward model)
See spm_dcm_erp_dipfit: this field is removed from DCM.M to save
memory - and is offered as an output argument if needed
The scheme can be initialised with parameters for the neuronal model
and spatial (observer) model by specifying the fields DCM.P and DCM.Q,
respectively. If previous priors (DCM.M.pE and pC or DCM.M.gE and gC or
DCM.M.hE and hC) are specified, they will be used. Explicit priors can be
useful for Bayesian parameter averaging - but would not normally be
called upon - because prior constraints are specified by DCM.A, DCM.B,...
__________________________________________________________________________
[Matlab code]( https://github.com/spm/spm/blob/main/toolbox/dcm_meeg/spm_dcm_erp.m )
Copyright (C) 1995-2025 Functional Imaging Laboratory, Department of Imaging Neuroscience, UCL

It also works for classes, either to get more info on the class itself...

help(spm.meeg)
Help on class meeg in module spm.meeg:
class meeg(mpython.matlab_class.MatlabClass)
| meeg(*args, **kwargs)
|
| Method resolution order:
| meeg
| mpython.matlab_class.MatlabClass
| mpython.core.base_types.MatlabType
| builtins.object

... or learn how to construct an object from it:

help(spm.meeg.__init__)
Help on function __init__ in module spm.meeg:
__init__(self, *args, **kwargs)
Function for creating meeg objects.
FORMAT
D = meeg;
returns an empty object
D = meeg(D);
converts a D struct to object or does nothing if already
object

2. Using Matlab-like types

SPM Python provides you with a type system that allows to reuse Matlab syntax without too much worries. The following classes are available:

  1. spm.Cell, for cell arrays
  2. spm.Struct, for struct arrays
  3. spm.Array, for general arrays

2.1. Base types

We've got cell arrays:

# Create an empty 1D Cell array with a shape of (3,)c=spm.Cell(3)
# Populate the Cell array with datac[0] ="Hello"c[1] ="World"c[2] =42# Print the Cell arrayprint("Initial Cell array:", c.tolist())
# Add a new element in (undefined) index 4c[4] ="New Element"# Print the updated Cell arrayprint("Updated Cell array:", c.tolist())
Initial Cell array: ['Hello', 'World', 42]
Updated Cell array: ['Hello', 'World', 42, Array([]), 'New Element']

Some struct arrays as well:

# Create an empty Structexample_struct=spm.Struct()
example_struct.name="Example"example_struct.value=42print("Single Struct example:", example_struct)
# Create a 1D Struct arraystruct_array_1d=spm.Struct(3)
struct_array_1d[0].name="First"struct_array_1d[1].name="Second"struct_array_1d[2].name="Third"print("1D Struct array example:", struct_array_1d)
# Create a 2D Struct arraystruct_array_2d=spm.Struct(2, 2)
struct_array_2d[0, 0].name="Top Left"struct_array_2d[0, 1].name="Top Right"struct_array_2d[1, 0].name="Bottom Left"struct_array_2d[1, 1].name="Bottom Right"print("2D Struct array example:")
print(struct_array_2d)
# Add a new field to the Structexample_struct.new_field="New Field Value"print("Updated Struct with new field:", example_struct)
Single Struct example: {'name': 'Example', 'value': 42}
1D Struct array example: [{'name': 'First'}, {'name': 'Second'}, {'name': 'Third'}]
2D Struct array example:
[[{'name': 'Top Left'}, {'name': 'Top Right'}],
[{'name': 'Bottom Left'}, {'name': 'Bottom Right'}]]
Updated Struct with new field: {'name': 'Example', 'value': 42, 'new_field': 'New Field Value'}

And some generic arrays too:

# Create an empty array (scalar)a=spm.Array()
print("Empty array:", a, "Shape:", a.shape)
# Create a 1D array of length 3a1d=spm.Array(3)
print("1D array:", a1d)
# Create a 2D array (3 rows, 2 columns)a2d=spm.Array(3, 2)
print("2D array:\n", a2d)
Empty array: 0.0 Shape: ()
1D array: [0.0, 0.0, 0.0]
2D array:
[[0.0, 0.0],
[0.0, 0.0],
[0.0, 0.0]]

2.2. Array operations

All of these types are derived from np.ndarray (thank you, Yael), which makes them really nice to work with if you're confortable with Numpy.

# Create a 1D struct arrays=spm.Struct(4)
# Populate the struct array with dataforiinrange(4):
s[i].value=is[i].label=f"item{i}"print("Original Struct:", s)
# Reshape the struct array to 2x2s_reshaped=s.reshape((2, 2))
print("Reshaped Struct (2x2):\n", s_reshaped)
# Transpose the struct arrays_transposed=np.transpose(s_reshaped)
Original Struct: [{'value': 0, 'label': 'item0'}, {'value': 1, 'label': 'item1'},
{'value': 2, 'label': 'item2'}, {'value': 3, 'label': 'item3'}]
Reshaped Struct (2x2):
[[{'value': 0, 'label': 'item0'}, {'value': 1, 'label': 'item1'}],
[{'value': 2, 'label': 'item2'}, {'value': 3, 'label': 'item3'}]]
Concatenated Struct (1D): [{'value': 0, 'label': 'item0'} {'value': 1, 'label': 'item1'}
{'value': 2, 'label': 'item2'} {'value': 3, 'label': 'item3'}
{'value': 10, 'label': 'item10'} {'value': 11, 'label': 'item11'}
{'value': 12, 'label': 'item12'} {'value': 13, 'label': 'item13'}]
# Concatenate along the first axiss2=spm.Struct(4)
foriinrange(4):
s2[i].value=i+10s2[i].label=f"item{i+10}"s_concat=np.concatenate([s, s2])
print("Concatenated Struct (1D):", s_concat)
Concatenated Struct (1D): [{'value': 0, 'label': 'item0'} {'value': 1, 'label': 'item1'}
{'value': 2, 'label': 'item2'} {'value': 3, 'label': 'item3'}
{'value': 10, 'label': 'item10'} {'value': 11, 'label': 'item11'}
{'value': 12, 'label': 'item12'} {'value': 13, 'label': 'item13'}]
# Concatenate along the first axiss2=spm.Struct(4)
foriinrange(4):
s2[i].value=i+10s2[i].label=f"item{i+10}"s_concat=np.concatenate([s, s2])
print("Concatenated Struct (1D):", s_concat)
# Create a 1D Cell arrayc=spm.Cell(4)
c[:] = ["hello", 123, {"a": 1}, [1, 2, 3]]
print("Original Cell:", c)
# Create a 1D Cell arrayc_extra=spm.Cell(2)
c_extra[:] = ["more", "cells"]
# Concatenate the Cell arraysc_concat=np.concatenate([c, c_extra])
print("Concatenated Cell (1D):", c_concat.tolist())
Original Cell: [hello, 123, [a], [1, 2, 3]]
Concatenated Cell (1D): ['hello', 123, Cell(['a']), Cell([1, 2, 3]), 'more', 'cells']

2.3. Type inference

One of the nice feature these types have is type inference at construction time. This enables accessing undefined field of an array, as long as the indexing sequence ends up with an assignment. There are a few extra rules:

  1. .: Use dot indexing to create a new field, as you'd use . in Matlab,
  2. []: Use square brackets for array indexing, as you'd use () in Matlab,
  3. (): Use round brackets for cell indexing, as you'd use {} in Matlab,

For example, to create a struct array with a field containing a cell array with, in third position, a struct array with a 2-by-2 random matrix in fifth position (showcasing all three rules):

S=spm.Struct()
S.field(3).struct[5].elem=np.random.rand(2, 2)
S
{'field': Cell([Array([]), Array([]), Array([]),
{'struct': Struct([{'elem': Array([])}, {'elem': Array([])}, {'elem': Array([])},
{'elem': Array([])}, {'elem': Array([])},
{'elem': Array([[0.38339607, 0.55686198],
[0.53678757, 0.5828017 ]])} ])} ])}

There is one caveat though: elements of unfinalised cell arrays cannot be specified:

>>>S=spm.Struct()
>>>S.field(3) ='test'S.field(3) ='test'^SyntaxError: cannotassigntofunctioncallhere. Maybeyoumeant'=='insteadof'='?

This is why we need one additional rule:

  1. as_cell[]: Initialising an element of an unfinalised cell array needs to use as_cell.

Using as_cell solves exactly this problem:

S=spm.Struct()
S.field.as_cell[3] ="test"S
{'field': Cell([Array([]), Array([]), Array([]), 'test'])}
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Getting started

Johan Medrano edited this page May 22, 2025 · 1 revision

0. Importing SPM Python

importspmimportnumpyasnp

1. Getting help on a function

To get help on a function or class, you can use the help function as you would in Matlab. For instance,

help(spm.spm_dcm_erp)
Help on function spm_dcm_erp in module spm.__toolbox.__dcm_meeg.spm_dcm_erp:
spm_dcm_erp(*args, **kwargs)
Estimate parameters of a DCM model (Variational Lapalce)
FORMAT [DCM,dipfit] = spm_dcm_erp(DCM)
DCM
name: name string
Lpos: Source locations
xY: data [1x1 struct]
xU: design [1x1 struct]
Sname: cell of source name strings
A: {[nr x nr double] [nr x nr double] [nr x nr double]}
B: {[nr x nr double], ...} Connection constraints
C: [nr x 1 double]
options.trials - indices of trials
options.Tdcm - [start end] time window in ms
options.D - time bin decimation (usually 1 or 2)
options.h - number of DCT drift terms (usually 1 or 2)
options.Nmodes - number of spatial models to invert
options.analysis - 'ERP', 'SSR' or 'IND'
options.model - 'ERP', 'SEP', 'CMC', 'CMM', 'NMM' or 'MFM'
options.spatial - 'ECD', 'LFP' or 'IMG'
options.onset - stimulus onset (ms)
options.dur - and dispersion (sd)
options.CVA - use CVA for spatial modes [default = 0]
options.Nmax - maxiumum number of iterations [default = 64]
dipfit - Dipole structure (for electromagnetic forward model)
See spm_dcm_erp_dipfit: this field is removed from DCM.M to save
memory - and is offered as an output argument if needed
The scheme can be initialised with parameters for the neuronal model
and spatial (observer) model by specifying the fields DCM.P and DCM.Q,
respectively. If previous priors (DCM.M.pE and pC or DCM.M.gE and gC or
DCM.M.hE and hC) are specified, they will be used. Explicit priors can be
useful for Bayesian parameter averaging - but would not normally be
called upon - because prior constraints are specified by DCM.A, DCM.B,...
__________________________________________________________________________
[Matlab code]( https://github.com/spm/spm/blob/main/toolbox/dcm_meeg/spm_dcm_erp.m )
Copyright (C) 1995-2025 Functional Imaging Laboratory, Department of Imaging Neuroscience, UCL

It also works for classes, either to get more info on the class itself...

help(spm.meeg)
Help on class meeg in module spm.meeg:
class meeg(mpython.matlab_class.MatlabClass)
| meeg(*args, **kwargs)
|
| Method resolution order:
| meeg
| mpython.matlab_class.MatlabClass
| mpython.core.base_types.MatlabType
| builtins.object

... or learn how to construct an object from it:

help(spm.meeg.__init__)
Help on function __init__ in module spm.meeg:
__init__(self, *args, **kwargs)
Function for creating meeg objects.
FORMAT
D = meeg;
returns an empty object
D = meeg(D);
converts a D struct to object or does nothing if already
object

2. Using Matlab-like types

SPM Python provides you with a type system that allows to reuse Matlab syntax without too much worries. The following classes are available:

  1. spm.Cell, for cell arrays
  2. spm.Struct, for struct arrays
  3. spm.Array, for general arrays

2.1. Base types

We've got cell arrays:

# Create an empty 1D Cell array with a shape of (3,)c=spm.Cell(3)
# Populate the Cell array with datac[0] ="Hello"c[1] ="World"c[2] =42# Print the Cell arrayprint("Initial Cell array:", c.tolist())
# Add a new element in (undefined) index 4c[4] ="New Element"# Print the updated Cell arrayprint("Updated Cell array:", c.tolist())
Initial Cell array: ['Hello', 'World', 42]
Updated Cell array: ['Hello', 'World', 42, Array([]), 'New Element']

Some struct arrays as well:

# Create an empty Structexample_struct=spm.Struct()
example_struct.name="Example"example_struct.value=42print("Single Struct example:", example_struct)
# Create a 1D Struct arraystruct_array_1d=spm.Struct(3)
struct_array_1d[0].name="First"struct_array_1d[1].name="Second"struct_array_1d[2].name="Third"print("1D Struct array example:", struct_array_1d)
# Create a 2D Struct arraystruct_array_2d=spm.Struct(2, 2)
struct_array_2d[0, 0].name="Top Left"struct_array_2d[0, 1].name="Top Right"struct_array_2d[1, 0].name="Bottom Left"struct_array_2d[1, 1].name="Bottom Right"print("2D Struct array example:")
print(struct_array_2d)
# Add a new field to the Structexample_struct.new_field="New Field Value"print("Updated Struct with new field:", example_struct)
Single Struct example: {'name': 'Example', 'value': 42}
1D Struct array example: [{'name': 'First'}, {'name': 'Second'}, {'name': 'Third'}]
2D Struct array example:
[[{'name': 'Top Left'}, {'name': 'Top Right'}],
[{'name': 'Bottom Left'}, {'name': 'Bottom Right'}]]
Updated Struct with new field: {'name': 'Example', 'value': 42, 'new_field': 'New Field Value'}

And some generic arrays too:

# Create an empty array (scalar)a=spm.Array()
print("Empty array:", a, "Shape:", a.shape)
# Create a 1D array of length 3a1d=spm.Array(3)
print("1D array:", a1d)
# Create a 2D array (3 rows, 2 columns)a2d=spm.Array(3, 2)
print("2D array:\n", a2d)
Empty array: 0.0 Shape: ()
1D array: [0.0, 0.0, 0.0]
2D array:
[[0.0, 0.0],
[0.0, 0.0],
[0.0, 0.0]]

2.2. Array operations

All of these types are derived from np.ndarray (thank you, Yael), which makes them really nice to work with if you're confortable with Numpy.

# Create a 1D struct arrays=spm.Struct(4)
# Populate the struct array with dataforiinrange(4):
s[i].value=is[i].label=f"item{i}"print("Original Struct:", s)
# Reshape the struct array to 2x2s_reshaped=s.reshape((2, 2))
print("Reshaped Struct (2x2):\n", s_reshaped)
# Transpose the struct arrays_transposed=np.transpose(s_reshaped)
Original Struct: [{'value': 0, 'label': 'item0'}, {'value': 1, 'label': 'item1'},
{'value': 2, 'label': 'item2'}, {'value': 3, 'label': 'item3'}]
Reshaped Struct (2x2):
[[{'value': 0, 'label': 'item0'}, {'value': 1, 'label': 'item1'}],
[{'value': 2, 'label': 'item2'}, {'value': 3, 'label': 'item3'}]]
Concatenated Struct (1D): [{'value': 0, 'label': 'item0'} {'value': 1, 'label': 'item1'}
{'value': 2, 'label': 'item2'} {'value': 3, 'label': 'item3'}
{'value': 10, 'label': 'item10'} {'value': 11, 'label': 'item11'}
{'value': 12, 'label': 'item12'} {'value': 13, 'label': 'item13'}]
# Concatenate along the first axiss2=spm.Struct(4)
foriinrange(4):
s2[i].value=i+10s2[i].label=f"item{i+10}"s_concat=np.concatenate([s, s2])
print("Concatenated Struct (1D):", s_concat)
Concatenated Struct (1D): [{'value': 0, 'label': 'item0'} {'value': 1, 'label': 'item1'}
{'value': 2, 'label': 'item2'} {'value': 3, 'label': 'item3'}
{'value': 10, 'label': 'item10'} {'value': 11, 'label': 'item11'}
{'value': 12, 'label': 'item12'} {'value': 13, 'label': 'item13'}]
# Concatenate along the first axiss2=spm.Struct(4)
foriinrange(4):
s2[i].value=i+10s2[i].label=f"item{i+10}"s_concat=np.concatenate([s, s2])
print("Concatenated Struct (1D):", s_concat)
# Create a 1D Cell arrayc=spm.Cell(4)
c[:] = ["hello", 123, {"a": 1}, [1, 2, 3]]
print("Original Cell:", c)
# Create a 1D Cell arrayc_extra=spm.Cell(2)
c_extra[:] = ["more", "cells"]
# Concatenate the Cell arraysc_concat=np.concatenate([c, c_extra])
print("Concatenated Cell (1D):", c_concat.tolist())
Original Cell: [hello, 123, [a], [1, 2, 3]]
Concatenated Cell (1D): ['hello', 123, Cell(['a']), Cell([1, 2, 3]), 'more', 'cells']

2.3. Type inference

One of the nice feature these types have is type inference at construction time. This enables accessing undefined field of an array, as long as the indexing sequence ends up with an assignment. There are a few extra rules:

  1. .: Use dot indexing to create a new field, as you'd use . in Matlab,
  2. []: Use square brackets for array indexing, as you'd use () in Matlab,
  3. (): Use round brackets for cell indexing, as you'd use {} in Matlab,

For example, to create a struct array with a field containing a cell array with, in third position, a struct array with a 2-by-2 random matrix in fifth position (showcasing all three rules):

S=spm.Struct()
S.field(3).struct[5].elem=np.random.rand(2, 2)
S
{'field': Cell([Array([]), Array([]), Array([]),
{'struct': Struct([{'elem': Array([])}, {'elem': Array([])}, {'elem': Array([])},
{'elem': Array([])}, {'elem': Array([])},
{'elem': Array([[0.38339607, 0.55686198],
[0.53678757, 0.5828017 ]])} ])} ])}

There is one caveat though: elements of unfinalised cell arrays cannot be specified:

>>>S=spm.Struct()
>>>S.field(3) ='test'S.field(3) ='test'^SyntaxError: cannotassigntofunctioncallhere. Maybeyoumeant'=='insteadof'='?

This is why we need one additional rule:

  1. as_cell[]: Initialising an element of an unfinalised cell array needs to use as_cell.

Using as_cell solves exactly this problem:

S=spm.Struct()
S.field.as_cell[3] ="test"S
{'field': Cell([Array([]), Array([]), Array([]), 'test'])}
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Getting started

Johan Medrano edited this page May 22, 2025 · 1 revision

0. Importing SPM Python

importspmimportnumpyasnp

1. Getting help on a function

To get help on a function or class, you can use the help function as you would in Matlab. For instance,

help(spm.spm_dcm_erp)
Help on function spm_dcm_erp in module spm.__toolbox.__dcm_meeg.spm_dcm_erp:
spm_dcm_erp(*args, **kwargs)
Estimate parameters of a DCM model (Variational Lapalce)
FORMAT [DCM,dipfit] = spm_dcm_erp(DCM)
DCM
name: name string
Lpos: Source locations
xY: data [1x1 struct]
xU: design [1x1 struct]
Sname: cell of source name strings
A: {[nr x nr double] [nr x nr double] [nr x nr double]}
B: {[nr x nr double], ...} Connection constraints
C: [nr x 1 double]
options.trials - indices of trials
options.Tdcm - [start end] time window in ms
options.D - time bin decimation (usually 1 or 2)
options.h - number of DCT drift terms (usually 1 or 2)
options.Nmodes - number of spatial models to invert
options.analysis - 'ERP', 'SSR' or 'IND'
options.model - 'ERP', 'SEP', 'CMC', 'CMM', 'NMM' or 'MFM'
options.spatial - 'ECD', 'LFP' or 'IMG'
options.onset - stimulus onset (ms)
options.dur - and dispersion (sd)
options.CVA - use CVA for spatial modes [default = 0]
options.Nmax - maxiumum number of iterations [default = 64]
dipfit - Dipole structure (for electromagnetic forward model)
See spm_dcm_erp_dipfit: this field is removed from DCM.M to save
memory - and is offered as an output argument if needed
The scheme can be initialised with parameters for the neuronal model
and spatial (observer) model by specifying the fields DCM.P and DCM.Q,
respectively. If previous priors (DCM.M.pE and pC or DCM.M.gE and gC or
DCM.M.hE and hC) are specified, they will be used. Explicit priors can be
useful for Bayesian parameter averaging - but would not normally be
called upon - because prior constraints are specified by DCM.A, DCM.B,...
__________________________________________________________________________
[Matlab code]( https://github.com/spm/spm/blob/main/toolbox/dcm_meeg/spm_dcm_erp.m )
Copyright (C) 1995-2025 Functional Imaging Laboratory, Department of Imaging Neuroscience, UCL

It also works for classes, either to get more info on the class itself...

help(spm.meeg)
Help on class meeg in module spm.meeg:
class meeg(mpython.matlab_class.MatlabClass)
| meeg(*args, **kwargs)
|
| Method resolution order:
| meeg
| mpython.matlab_class.MatlabClass
| mpython.core.base_types.MatlabType
| builtins.object

... or learn how to construct an object from it:

help(spm.meeg.__init__)
Help on function __init__ in module spm.meeg:
__init__(self, *args, **kwargs)
Function for creating meeg objects.
FORMAT
D = meeg;
returns an empty object
D = meeg(D);
converts a D struct to object or does nothing if already
object

2. Using Matlab-like types

SPM Python provides you with a type system that allows to reuse Matlab syntax without too much worries. The following classes are available:

  1. spm.Cell, for cell arrays
  2. spm.Struct, for struct arrays
  3. spm.Array, for general arrays

2.1. Base types

We've got cell arrays:

# Create an empty 1D Cell array with a shape of (3,)c=spm.Cell(3)
# Populate the Cell array with datac[0] ="Hello"c[1] ="World"c[2] =42# Print the Cell arrayprint("Initial Cell array:", c.tolist())
# Add a new element in (undefined) index 4c[4] ="New Element"# Print the updated Cell arrayprint("Updated Cell array:", c.tolist())
Initial Cell array: ['Hello', 'World', 42]
Updated Cell array: ['Hello', 'World', 42, Array([]), 'New Element']

Some struct arrays as well:

# Create an empty Structexample_struct=spm.Struct()
example_struct.name="Example"example_struct.value=42print("Single Struct example:", example_struct)
# Create a 1D Struct arraystruct_array_1d=spm.Struct(3)
struct_array_1d[0].name="First"struct_array_1d[1].name="Second"struct_array_1d[2].name="Third"print("1D Struct array example:", struct_array_1d)
# Create a 2D Struct arraystruct_array_2d=spm.Struct(2, 2)
struct_array_2d[0, 0].name="Top Left"struct_array_2d[0, 1].name="Top Right"struct_array_2d[1, 0].name="Bottom Left"struct_array_2d[1, 1].name="Bottom Right"print("2D Struct array example:")
print(struct_array_2d)
# Add a new field to the Structexample_struct.new_field="New Field Value"print("Updated Struct with new field:", example_struct)
Single Struct example: {'name': 'Example', 'value': 42}
1D Struct array example: [{'name': 'First'}, {'name': 'Second'}, {'name': 'Third'}]
2D Struct array example:
[[{'name': 'Top Left'}, {'name': 'Top Right'}],
[{'name': 'Bottom Left'}, {'name': 'Bottom Right'}]]
Updated Struct with new field: {'name': 'Example', 'value': 42, 'new_field': 'New Field Value'}

And some generic arrays too:

# Create an empty array (scalar)a=spm.Array()
print("Empty array:", a, "Shape:", a.shape)
# Create a 1D array of length 3a1d=spm.Array(3)
print("1D array:", a1d)
# Create a 2D array (3 rows, 2 columns)a2d=spm.Array(3, 2)
print("2D array:\n", a2d)
Empty array: 0.0 Shape: ()
1D array: [0.0, 0.0, 0.0]
2D array:
[[0.0, 0.0],
[0.0, 0.0],
[0.0, 0.0]]

2.2. Array operations

All of these types are derived from np.ndarray (thank you, Yael), which makes them really nice to work with if you're confortable with Numpy.

# Create a 1D struct arrays=spm.Struct(4)
# Populate the struct array with dataforiinrange(4):
s[i].value=is[i].label=f"item{i}"print("Original Struct:", s)
# Reshape the struct array to 2x2s_reshaped=s.reshape((2, 2))
print("Reshaped Struct (2x2):\n", s_reshaped)
# Transpose the struct arrays_transposed=np.transpose(s_reshaped)
Original Struct: [{'value': 0, 'label': 'item0'}, {'value': 1, 'label': 'item1'},
{'value': 2, 'label': 'item2'}, {'value': 3, 'label': 'item3'}]
Reshaped Struct (2x2):
[[{'value': 0, 'label': 'item0'}, {'value': 1, 'label': 'item1'}],
[{'value': 2, 'label': 'item2'}, {'value': 3, 'label': 'item3'}]]
Concatenated Struct (1D): [{'value': 0, 'label': 'item0'} {'value': 1, 'label': 'item1'}
{'value': 2, 'label': 'item2'} {'value': 3, 'label': 'item3'}
{'value': 10, 'label': 'item10'} {'value': 11, 'label': 'item11'}
{'value': 12, 'label': 'item12'} {'value': 13, 'label': 'item13'}]
# Concatenate along the first axiss2=spm.Struct(4)
foriinrange(4):
s2[i].value=i+10s2[i].label=f"item{i+10}"s_concat=np.concatenate([s, s2])
print("Concatenated Struct (1D):", s_concat)
Concatenated Struct (1D): [{'value': 0, 'label': 'item0'} {'value': 1, 'label': 'item1'}
{'value': 2, 'label': 'item2'} {'value': 3, 'label': 'item3'}
{'value': 10, 'label': 'item10'} {'value': 11, 'label': 'item11'}
{'value': 12, 'label': 'item12'} {'value': 13, 'label': 'item13'}]
# Concatenate along the first axiss2=spm.Struct(4)
foriinrange(4):
s2[i].value=i+10s2[i].label=f"item{i+10}"s_concat=np.concatenate([s, s2])
print("Concatenated Struct (1D):", s_concat)
# Create a 1D Cell arrayc=spm.Cell(4)
c[:] = ["hello", 123, {"a": 1}, [1, 2, 3]]
print("Original Cell:", c)
# Create a 1D Cell arrayc_extra=spm.Cell(2)
c_extra[:] = ["more", "cells"]
# Concatenate the Cell arraysc_concat=np.concatenate([c, c_extra])
print("Concatenated Cell (1D):", c_concat.tolist())
Original Cell: [hello, 123, [a], [1, 2, 3]]
Concatenated Cell (1D): ['hello', 123, Cell(['a']), Cell([1, 2, 3]), 'more', 'cells']

2.3. Type inference

One of the nice feature these types have is type inference at construction time. This enables accessing undefined field of an array, as long as the indexing sequence ends up with an assignment. There are a few extra rules:

  1. .: Use dot indexing to create a new field, as you'd use . in Matlab,
  2. []: Use square brackets for array indexing, as you'd use () in Matlab,
  3. (): Use round brackets for cell indexing, as you'd use {} in Matlab,

For example, to create a struct array with a field containing a cell array with, in third position, a struct array with a 2-by-2 random matrix in fifth position (showcasing all three rules):

S=spm.Struct()
S.field(3).struct[5].elem=np.random.rand(2, 2)
S
{'field': Cell([Array([]), Array([]), Array([]),
{'struct': Struct([{'elem': Array([])}, {'elem': Array([])}, {'elem': Array([])},
{'elem': Array([])}, {'elem': Array([])},
{'elem': Array([[0.38339607, 0.55686198],
[0.53678757, 0.5828017 ]])} ])} ])}

There is one caveat though: elements of unfinalised cell arrays cannot be specified:

>>>S=spm.Struct()
>>>S.field(3) ='test'S.field(3) ='test'^SyntaxError: cannotassigntofunctioncallhere. Maybeyoumeant'=='insteadof'='?

This is why we need one additional rule:

  1. as_cell[]: Initialising an element of an unfinalised cell array needs to use as_cell.

Using as_cell solves exactly this problem:

S=spm.Struct()
S.field.as_cell[3] ="test"S
{'field': Cell([Array([]), Array([]), Array([]), 'test'])}
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Getting started

Johan Medrano edited this page May 22, 2025 · 1 revision

0. Importing SPM Python

importspmimportnumpyasnp

1. Getting help on a function

To get help on a function or class, you can use the help function as you would in Matlab. For instance,

help(spm.spm_dcm_erp)
Help on function spm_dcm_erp in module spm.__toolbox.__dcm_meeg.spm_dcm_erp:
spm_dcm_erp(*args, **kwargs)
Estimate parameters of a DCM model (Variational Lapalce)
FORMAT [DCM,dipfit] = spm_dcm_erp(DCM)
DCM
name: name string
Lpos: Source locations
xY: data [1x1 struct]
xU: design [1x1 struct]
Sname: cell of source name strings
A: {[nr x nr double] [nr x nr double] [nr x nr double]}
B: {[nr x nr double], ...} Connection constraints
C: [nr x 1 double]
options.trials - indices of trials
options.Tdcm - [start end] time window in ms
options.D - time bin decimation (usually 1 or 2)
options.h - number of DCT drift terms (usually 1 or 2)
options.Nmodes - number of spatial models to invert
options.analysis - 'ERP', 'SSR' or 'IND'
options.model - 'ERP', 'SEP', 'CMC', 'CMM', 'NMM' or 'MFM'
options.spatial - 'ECD', 'LFP' or 'IMG'
options.onset - stimulus onset (ms)
options.dur - and dispersion (sd)
options.CVA - use CVA for spatial modes [default = 0]
options.Nmax - maxiumum number of iterations [default = 64]
dipfit - Dipole structure (for electromagnetic forward model)
See spm_dcm_erp_dipfit: this field is removed from DCM.M to save
memory - and is offered as an output argument if needed
The scheme can be initialised with parameters for the neuronal model
and spatial (observer) model by specifying the fields DCM.P and DCM.Q,
respectively. If previous priors (DCM.M.pE and pC or DCM.M.gE and gC or
DCM.M.hE and hC) are specified, they will be used. Explicit priors can be
useful for Bayesian parameter averaging - but would not normally be
called upon - because prior constraints are specified by DCM.A, DCM.B,...
__________________________________________________________________________
[Matlab code]( https://github.com/spm/spm/blob/main/toolbox/dcm_meeg/spm_dcm_erp.m )
Copyright (C) 1995-2025 Functional Imaging Laboratory, Department of Imaging Neuroscience, UCL

It also works for classes, either to get more info on the class itself...

help(spm.meeg)
Help on class meeg in module spm.meeg:
class meeg(mpython.matlab_class.MatlabClass)
| meeg(*args, **kwargs)
|
| Method resolution order:
| meeg
| mpython.matlab_class.MatlabClass
| mpython.core.base_types.MatlabType
| builtins.object

... or learn how to construct an object from it:

help(spm.meeg.__init__)
Help on function __init__ in module spm.meeg:
__init__(self, *args, **kwargs)
Function for creating meeg objects.
FORMAT
D = meeg;
returns an empty object
D = meeg(D);
converts a D struct to object or does nothing if already
object

2. Using Matlab-like types

SPM Python provides you with a type system that allows to reuse Matlab syntax without too much worries. The following classes are available:

  1. spm.Cell, for cell arrays
  2. spm.Struct, for struct arrays
  3. spm.Array, for general arrays

2.1. Base types

We've got cell arrays:

# Create an empty 1D Cell array with a shape of (3,)c=spm.Cell(3)
# Populate the Cell array with datac[0] ="Hello"c[1] ="World"c[2] =42# Print the Cell arrayprint("Initial Cell array:", c.tolist())
# Add a new element in (undefined) index 4c[4] ="New Element"# Print the updated Cell arrayprint("Updated Cell array:", c.tolist())
Initial Cell array: ['Hello', 'World', 42]
Updated Cell array: ['Hello', 'World', 42, Array([]), 'New Element']

Some struct arrays as well:

# Create an empty Structexample_struct=spm.Struct()
example_struct.name="Example"example_struct.value=42print("Single Struct example:", example_struct)
# Create a 1D Struct arraystruct_array_1d=spm.Struct(3)
struct_array_1d[0].name="First"struct_array_1d[1].name="Second"struct_array_1d[2].name="Third"print("1D Struct array example:", struct_array_1d)
# Create a 2D Struct arraystruct_array_2d=spm.Struct(2, 2)
struct_array_2d[0, 0].name="Top Left"struct_array_2d[0, 1].name="Top Right"struct_array_2d[1, 0].name="Bottom Left"struct_array_2d[1, 1].name="Bottom Right"print("2D Struct array example:")
print(struct_array_2d)
# Add a new field to the Structexample_struct.new_field="New Field Value"print("Updated Struct with new field:", example_struct)
Single Struct example: {'name': 'Example', 'value': 42}
1D Struct array example: [{'name': 'First'}, {'name': 'Second'}, {'name': 'Third'}]
2D Struct array example:
[[{'name': 'Top Left'}, {'name': 'Top Right'}],
[{'name': 'Bottom Left'}, {'name': 'Bottom Right'}]]
Updated Struct with new field: {'name': 'Example', 'value': 42, 'new_field': 'New Field Value'}

And some generic arrays too:

# Create an empty array (scalar)a=spm.Array()
print("Empty array:", a, "Shape:", a.shape)
# Create a 1D array of length 3a1d=spm.Array(3)
print("1D array:", a1d)
# Create a 2D array (3 rows, 2 columns)a2d=spm.Array(3, 2)
print("2D array:\n", a2d)
Empty array: 0.0 Shape: ()
1D array: [0.0, 0.0, 0.0]
2D array:
[[0.0, 0.0],
[0.0, 0.0],
[0.0, 0.0]]

2.2. Array operations

All of these types are derived from np.ndarray (thank you, Yael), which makes them really nice to work with if you're confortable with Numpy.

# Create a 1D struct arrays=spm.Struct(4)
# Populate the struct array with dataforiinrange(4):
s[i].value=is[i].label=f"item{i}"print("Original Struct:", s)
# Reshape the struct array to 2x2s_reshaped=s.reshape((2, 2))
print("Reshaped Struct (2x2):\n", s_reshaped)
# Transpose the struct arrays_transposed=np.transpose(s_reshaped)
Original Struct: [{'value': 0, 'label': 'item0'}, {'value': 1, 'label': 'item1'},
{'value': 2, 'label': 'item2'}, {'value': 3, 'label': 'item3'}]
Reshaped Struct (2x2):
[[{'value': 0, 'label': 'item0'}, {'value': 1, 'label': 'item1'}],
[{'value': 2, 'label': 'item2'}, {'value': 3, 'label': 'item3'}]]
Concatenated Struct (1D): [{'value': 0, 'label': 'item0'} {'value': 1, 'label': 'item1'}
{'value': 2, 'label': 'item2'} {'value': 3, 'label': 'item3'}
{'value': 10, 'label': 'item10'} {'value': 11, 'label': 'item11'}
{'value': 12, 'label': 'item12'} {'value': 13, 'label': 'item13'}]
# Concatenate along the first axiss2=spm.Struct(4)
foriinrange(4):
s2[i].value=i+10s2[i].label=f"item{i+10}"s_concat=np.concatenate([s, s2])
print("Concatenated Struct (1D):", s_concat)
Concatenated Struct (1D): [{'value': 0, 'label': 'item0'} {'value': 1, 'label': 'item1'}
{'value': 2, 'label': 'item2'} {'value': 3, 'label': 'item3'}
{'value': 10, 'label': 'item10'} {'value': 11, 'label': 'item11'}
{'value': 12, 'label': 'item12'} {'value': 13, 'label': 'item13'}]
# Concatenate along the first axiss2=spm.Struct(4)
foriinrange(4):
s2[i].value=i+10s2[i].label=f"item{i+10}"s_concat=np.concatenate([s, s2])
print("Concatenated Struct (1D):", s_concat)
# Create a 1D Cell arrayc=spm.Cell(4)
c[:] = ["hello", 123, {"a": 1}, [1, 2, 3]]
print("Original Cell:", c)
# Create a 1D Cell arrayc_extra=spm.Cell(2)
c_extra[:] = ["more", "cells"]
# Concatenate the Cell arraysc_concat=np.concatenate([c, c_extra])
print("Concatenated Cell (1D):", c_concat.tolist())
Original Cell: [hello, 123, [a], [1, 2, 3]]
Concatenated Cell (1D): ['hello', 123, Cell(['a']), Cell([1, 2, 3]), 'more', 'cells']

2.3. Type inference

One of the nice feature these types have is type inference at construction time. This enables accessing undefined field of an array, as long as the indexing sequence ends up with an assignment. There are a few extra rules:

  1. .: Use dot indexing to create a new field, as you'd use . in Matlab,
  2. []: Use square brackets for array indexing, as you'd use () in Matlab,
  3. (): Use round brackets for cell indexing, as you'd use {} in Matlab,

For example, to create a struct array with a field containing a cell array with, in third position, a struct array with a 2-by-2 random matrix in fifth position (showcasing all three rules):

S=spm.Struct()
S.field(3).struct[5].elem=np.random.rand(2, 2)
S
{'field': Cell([Array([]), Array([]), Array([]),
{'struct': Struct([{'elem': Array([])}, {'elem': Array([])}, {'elem': Array([])},
{'elem': Array([])}, {'elem': Array([])},
{'elem': Array([[0.38339607, 0.55686198],
[0.53678757, 0.5828017 ]])} ])} ])}

There is one caveat though: elements of unfinalised cell arrays cannot be specified:

>>>S=spm.Struct()
>>>S.field(3) ='test'S.field(3) ='test'^SyntaxError: cannotassigntofunctioncallhere. Maybeyoumeant'=='insteadof'='?

This is why we need one additional rule:

  1. as_cell[]: Initialising an element of an unfinalised cell array needs to use as_cell.

Using as_cell solves exactly this problem:

S=spm.Struct()
S.field.as_cell[3] ="test"S
{'field': Cell([Array([]), Array([]), Array([]), 'test'])}
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Getting started

Johan Medrano edited this page May 22, 2025 · 1 revision

0. Importing SPM Python

importspmimportnumpyasnp

1. Getting help on a function

To get help on a function or class, you can use the help function as you would in Matlab. For instance,

help(spm.spm_dcm_erp)
Help on function spm_dcm_erp in module spm.__toolbox.__dcm_meeg.spm_dcm_erp:
spm_dcm_erp(*args, **kwargs)
Estimate parameters of a DCM model (Variational Lapalce)
FORMAT [DCM,dipfit] = spm_dcm_erp(DCM)
DCM
name: name string
Lpos: Source locations
xY: data [1x1 struct]
xU: design [1x1 struct]
Sname: cell of source name strings
A: {[nr x nr double] [nr x nr double] [nr x nr double]}
B: {[nr x nr double], ...} Connection constraints
C: [nr x 1 double]
options.trials - indices of trials
options.Tdcm - [start end] time window in ms
options.D - time bin decimation (usually 1 or 2)
options.h - number of DCT drift terms (usually 1 or 2)
options.Nmodes - number of spatial models to invert
options.analysis - 'ERP', 'SSR' or 'IND'
options.model - 'ERP', 'SEP', 'CMC', 'CMM', 'NMM' or 'MFM'
options.spatial - 'ECD', 'LFP' or 'IMG'
options.onset - stimulus onset (ms)
options.dur - and dispersion (sd)
options.CVA - use CVA for spatial modes [default = 0]
options.Nmax - maxiumum number of iterations [default = 64]
dipfit - Dipole structure (for electromagnetic forward model)
See spm_dcm_erp_dipfit: this field is removed from DCM.M to save
memory - and is offered as an output argument if needed
The scheme can be initialised with parameters for the neuronal model
and spatial (observer) model by specifying the fields DCM.P and DCM.Q,
respectively. If previous priors (DCM.M.pE and pC or DCM.M.gE and gC or
DCM.M.hE and hC) are specified, they will be used. Explicit priors can be
useful for Bayesian parameter averaging - but would not normally be
called upon - because prior constraints are specified by DCM.A, DCM.B,...
__________________________________________________________________________
[Matlab code]( https://github.com/spm/spm/blob/main/toolbox/dcm_meeg/spm_dcm_erp.m )
Copyright (C) 1995-2025 Functional Imaging Laboratory, Department of Imaging Neuroscience, UCL

It also works for classes, either to get more info on the class itself...

help(spm.meeg)
Help on class meeg in module spm.meeg:
class meeg(mpython.matlab_class.MatlabClass)
| meeg(*args, **kwargs)
|
| Method resolution order:
| meeg
| mpython.matlab_class.MatlabClass
| mpython.core.base_types.MatlabType
| builtins.object

... or learn how to construct an object from it:

help(spm.meeg.__init__)
Help on function __init__ in module spm.meeg:
__init__(self, *args, **kwargs)
Function for creating meeg objects.
FORMAT
D = meeg;
returns an empty object
D = meeg(D);
converts a D struct to object or does nothing if already
object

2. Using Matlab-like types

SPM Python provides you with a type system that allows to reuse Matlab syntax without too much worries. The following classes are available:

  1. spm.Cell, for cell arrays
  2. spm.Struct, for struct arrays
  3. spm.Array, for general arrays

2.1. Base types

We've got cell arrays:

# Create an empty 1D Cell array with a shape of (3,)c=spm.Cell(3)
# Populate the Cell array with datac[0] ="Hello"c[1] ="World"c[2] =42# Print the Cell arrayprint("Initial Cell array:", c.tolist())
# Add a new element in (undefined) index 4c[4] ="New Element"# Print the updated Cell arrayprint("Updated Cell array:", c.tolist())
Initial Cell array: ['Hello', 'World', 42]
Updated Cell array: ['Hello', 'World', 42, Array([]), 'New Element']

Some struct arrays as well:

# Create an empty Structexample_struct=spm.Struct()
example_struct.name="Example"example_struct.value=42print("Single Struct example:", example_struct)
# Create a 1D Struct arraystruct_array_1d=spm.Struct(3)
struct_array_1d[0].name="First"struct_array_1d[1].name="Second"struct_array_1d[2].name="Third"print("1D Struct array example:", struct_array_1d)
# Create a 2D Struct arraystruct_array_2d=spm.Struct(2, 2)
struct_array_2d[0, 0].name="Top Left"struct_array_2d[0, 1].name="Top Right"struct_array_2d[1, 0].name="Bottom Left"struct_array_2d[1, 1].name="Bottom Right"print("2D Struct array example:")
print(struct_array_2d)
# Add a new field to the Structexample_struct.new_field="New Field Value"print("Updated Struct with new field:", example_struct)
Single Struct example: {'name': 'Example', 'value': 42}
1D Struct array example: [{'name': 'First'}, {'name': 'Second'}, {'name': 'Third'}]
2D Struct array example:
[[{'name': 'Top Left'}, {'name': 'Top Right'}],
[{'name': 'Bottom Left'}, {'name': 'Bottom Right'}]]
Updated Struct with new field: {'name': 'Example', 'value': 42, 'new_field': 'New Field Value'}

And some generic arrays too:

# Create an empty array (scalar)a=spm.Array()
print("Empty array:", a, "Shape:", a.shape)
# Create a 1D array of length 3a1d=spm.Array(3)
print("1D array:", a1d)
# Create a 2D array (3 rows, 2 columns)a2d=spm.Array(3, 2)
print("2D array:\n", a2d)
Empty array: 0.0 Shape: ()
1D array: [0.0, 0.0, 0.0]
2D array:
[[0.0, 0.0],
[0.0, 0.0],
[0.0, 0.0]]

2.2. Array operations

All of these types are derived from np.ndarray (thank you, Yael), which makes them really nice to work with if you're confortable with Numpy.

# Create a 1D struct arrays=spm.Struct(4)
# Populate the struct array with dataforiinrange(4):
s[i].value=is[i].label=f"item{i}"print("Original Struct:", s)
# Reshape the struct array to 2x2s_reshaped=s.reshape((2, 2))
print("Reshaped Struct (2x2):\n", s_reshaped)
# Transpose the struct arrays_transposed=np.transpose(s_reshaped)
Original Struct: [{'value': 0, 'label': 'item0'}, {'value': 1, 'label': 'item1'},
{'value': 2, 'label': 'item2'}, {'value': 3, 'label': 'item3'}]
Reshaped Struct (2x2):
[[{'value': 0, 'label': 'item0'}, {'value': 1, 'label': 'item1'}],
[{'value': 2, 'label': 'item2'}, {'value': 3, 'label': 'item3'}]]
Concatenated Struct (1D): [{'value': 0, 'label': 'item0'} {'value': 1, 'label': 'item1'}
{'value': 2, 'label': 'item2'} {'value': 3, 'label': 'item3'}
{'value': 10, 'label': 'item10'} {'value': 11, 'label': 'item11'}
{'value': 12, 'label': 'item12'} {'value': 13, 'label': 'item13'}]
# Concatenate along the first axiss2=spm.Struct(4)
foriinrange(4):
s2[i].value=i+10s2[i].label=f"item{i+10}"s_concat=np.concatenate([s, s2])
print("Concatenated Struct (1D):", s_concat)
Concatenated Struct (1D): [{'value': 0, 'label': 'item0'} {'value': 1, 'label': 'item1'}
{'value': 2, 'label': 'item2'} {'value': 3, 'label': 'item3'}
{'value': 10, 'label': 'item10'} {'value': 11, 'label': 'item11'}
{'value': 12, 'label': 'item12'} {'value': 13, 'label': 'item13'}]
# Concatenate along the first axiss2=spm.Struct(4)
foriinrange(4):
s2[i].value=i+10s2[i].label=f"item{i+10}"s_concat=np.concatenate([s, s2])
print("Concatenated Struct (1D):", s_concat)
# Create a 1D Cell arrayc=spm.Cell(4)
c[:] = ["hello", 123, {"a": 1}, [1, 2, 3]]
print("Original Cell:", c)
# Create a 1D Cell arrayc_extra=spm.Cell(2)
c_extra[:] = ["more", "cells"]
# Concatenate the Cell arraysc_concat=np.concatenate([c, c_extra])
print("Concatenated Cell (1D):", c_concat.tolist())
Original Cell: [hello, 123, [a], [1, 2, 3]]
Concatenated Cell (1D): ['hello', 123, Cell(['a']), Cell([1, 2, 3]), 'more', 'cells']

2.3. Type inference

One of the nice feature these types have is type inference at construction time. This enables accessing undefined field of an array, as long as the indexing sequence ends up with an assignment. There are a few extra rules:

  1. .: Use dot indexing to create a new field, as you'd use . in Matlab,
  2. []: Use square brackets for array indexing, as you'd use () in Matlab,
  3. (): Use round brackets for cell indexing, as you'd use {} in Matlab,

For example, to create a struct array with a field containing a cell array with, in third position, a struct array with a 2-by-2 random matrix in fifth position (showcasing all three rules):

S=spm.Struct()
S.field(3).struct[5].elem=np.random.rand(2, 2)
S
{'field': Cell([Array([]), Array([]), Array([]),
{'struct': Struct([{'elem': Array([])}, {'elem': Array([])}, {'elem': Array([])},
{'elem': Array([])}, {'elem': Array([])},
{'elem': Array([[0.38339607, 0.55686198],
[0.53678757, 0.5828017 ]])} ])} ])}

There is one caveat though: elements of unfinalised cell arrays cannot be specified:

>>>S=spm.Struct()
>>>S.field(3) ='test'S.field(3) ='test'^SyntaxError: cannotassigntofunctioncallhere. Maybeyoumeant'=='insteadof'='?

This is why we need one additional rule:

  1. as_cell[]: Initialising an element of an unfinalised cell array needs to use as_cell.

Using as_cell solves exactly this problem:

S=spm.Struct()
S.field.as_cell[3] ="test"S
{'field': Cell([Array([]), Array([]), Array([]), 'test'])}
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Getting started

Johan Medrano edited this page May 22, 2025 · 1 revision

0. Importing SPM Python

importspmimportnumpyasnp

1. Getting help on a function

To get help on a function or class, you can use the help function as you would in Matlab. For instance,

help(spm.spm_dcm_erp)
Help on function spm_dcm_erp in module spm.__toolbox.__dcm_meeg.spm_dcm_erp:
spm_dcm_erp(*args, **kwargs)
Estimate parameters of a DCM model (Variational Lapalce)
FORMAT [DCM,dipfit] = spm_dcm_erp(DCM)
DCM
name: name string
Lpos: Source locations
xY: data [1x1 struct]
xU: design [1x1 struct]
Sname: cell of source name strings
A: {[nr x nr double] [nr x nr double] [nr x nr double]}
B: {[nr x nr double], ...} Connection constraints
C: [nr x 1 double]
options.trials - indices of trials
options.Tdcm - [start end] time window in ms
options.D - time bin decimation (usually 1 or 2)
options.h - number of DCT drift terms (usually 1 or 2)
options.Nmodes - number of spatial models to invert
options.analysis - 'ERP', 'SSR' or 'IND'
options.model - 'ERP', 'SEP', 'CMC', 'CMM', 'NMM' or 'MFM'
options.spatial - 'ECD', 'LFP' or 'IMG'
options.onset - stimulus onset (ms)
options.dur - and dispersion (sd)
options.CVA - use CVA for spatial modes [default = 0]
options.Nmax - maxiumum number of iterations [default = 64]
dipfit - Dipole structure (for electromagnetic forward model)
See spm_dcm_erp_dipfit: this field is removed from DCM.M to save
memory - and is offered as an output argument if needed
The scheme can be initialised with parameters for the neuronal model
and spatial (observer) model by specifying the fields DCM.P and DCM.Q,
respectively. If previous priors (DCM.M.pE and pC or DCM.M.gE and gC or
DCM.M.hE and hC) are specified, they will be used. Explicit priors can be
useful for Bayesian parameter averaging - but would not normally be
called upon - because prior constraints are specified by DCM.A, DCM.B,...
__________________________________________________________________________
[Matlab code]( https://github.com/spm/spm/blob/main/toolbox/dcm_meeg/spm_dcm_erp.m )
Copyright (C) 1995-2025 Functional Imaging Laboratory, Department of Imaging Neuroscience, UCL

It also works for classes, either to get more info on the class itself...

help(spm.meeg)
Help on class meeg in module spm.meeg:
class meeg(mpython.matlab_class.MatlabClass)
| meeg(*args, **kwargs)
|
| Method resolution order:
| meeg
| mpython.matlab_class.MatlabClass
| mpython.core.base_types.MatlabType
| builtins.object

... or learn how to construct an object from it:

help(spm.meeg.__init__)
Help on function __init__ in module spm.meeg:
__init__(self, *args, **kwargs)
Function for creating meeg objects.
FORMAT
D = meeg;
returns an empty object
D = meeg(D);
converts a D struct to object or does nothing if already
object

2. Using Matlab-like types

SPM Python provides you with a type system that allows to reuse Matlab syntax without too much worries. The following classes are available:

  1. spm.Cell, for cell arrays
  2. spm.Struct, for struct arrays
  3. spm.Array, for general arrays

2.1. Base types

We've got cell arrays:

# Create an empty 1D Cell array with a shape of (3,)c=spm.Cell(3)
# Populate the Cell array with datac[0] ="Hello"c[1] ="World"c[2] =42# Print the Cell arrayprint("Initial Cell array:", c.tolist())
# Add a new element in (undefined) index 4c[4] ="New Element"# Print the updated Cell arrayprint("Updated Cell array:", c.tolist())
Initial Cell array: ['Hello', 'World', 42]
Updated Cell array: ['Hello', 'World', 42, Array([]), 'New Element']

Some struct arrays as well:

# Create an empty Structexample_struct=spm.Struct()
example_struct.name="Example"example_struct.value=42print("Single Struct example:", example_struct)
# Create a 1D Struct arraystruct_array_1d=spm.Struct(3)
struct_array_1d[0].name="First"struct_array_1d[1].name="Second"struct_array_1d[2].name="Third"print("1D Struct array example:", struct_array_1d)
# Create a 2D Struct arraystruct_array_2d=spm.Struct(2, 2)
struct_array_2d[0, 0].name="Top Left"struct_array_2d[0, 1].name="Top Right"struct_array_2d[1, 0].name="Bottom Left"struct_array_2d[1, 1].name="Bottom Right"print("2D Struct array example:")
print(struct_array_2d)
# Add a new field to the Structexample_struct.new_field="New Field Value"print("Updated Struct with new field:", example_struct)
Single Struct example: {'name': 'Example', 'value': 42}
1D Struct array example: [{'name': 'First'}, {'name': 'Second'}, {'name': 'Third'}]
2D Struct array example:
[[{'name': 'Top Left'}, {'name': 'Top Right'}],
[{'name': 'Bottom Left'}, {'name': 'Bottom Right'}]]
Updated Struct with new field: {'name': 'Example', 'value': 42, 'new_field': 'New Field Value'}

And some generic arrays too:

# Create an empty array (scalar)a=spm.Array()
print("Empty array:", a, "Shape:", a.shape)
# Create a 1D array of length 3a1d=spm.Array(3)
print("1D array:", a1d)
# Create a 2D array (3 rows, 2 columns)a2d=spm.Array(3, 2)
print("2D array:\n", a2d)
Empty array: 0.0 Shape: ()
1D array: [0.0, 0.0, 0.0]
2D array:
[[0.0, 0.0],
[0.0, 0.0],
[0.0, 0.0]]

2.2. Array operations

All of these types are derived from np.ndarray (thank you, Yael), which makes them really nice to work with if you're confortable with Numpy.

# Create a 1D struct arrays=spm.Struct(4)
# Populate the struct array with dataforiinrange(4):
s[i].value=is[i].label=f"item{i}"print("Original Struct:", s)
# Reshape the struct array to 2x2s_reshaped=s.reshape((2, 2))
print("Reshaped Struct (2x2):\n", s_reshaped)
# Transpose the struct arrays_transposed=np.transpose(s_reshaped)
Original Struct: [{'value': 0, 'label': 'item0'}, {'value': 1, 'label': 'item1'},
{'value': 2, 'label': 'item2'}, {'value': 3, 'label': 'item3'}]
Reshaped Struct (2x2):
[[{'value': 0, 'label': 'item0'}, {'value': 1, 'label': 'item1'}],
[{'value': 2, 'label': 'item2'}, {'value': 3, 'label': 'item3'}]]
Concatenated Struct (1D): [{'value': 0, 'label': 'item0'} {'value': 1, 'label': 'item1'}
{'value': 2, 'label': 'item2'} {'value': 3, 'label': 'item3'}
{'value': 10, 'label': 'item10'} {'value': 11, 'label': 'item11'}
{'value': 12, 'label': 'item12'} {'value': 13, 'label': 'item13'}]
# Concatenate along the first axiss2=spm.Struct(4)
foriinrange(4):
s2[i].value=i+10s2[i].label=f"item{i+10}"s_concat=np.concatenate([s, s2])
print("Concatenated Struct (1D):", s_concat)
Concatenated Struct (1D): [{'value': 0, 'label': 'item0'} {'value': 1, 'label': 'item1'}
{'value': 2, 'label': 'item2'} {'value': 3, 'label': 'item3'}
{'value': 10, 'label': 'item10'} {'value': 11, 'label': 'item11'}
{'value': 12, 'label': 'item12'} {'value': 13, 'label': 'item13'}]
# Concatenate along the first axiss2=spm.Struct(4)
foriinrange(4):
s2[i].value=i+10s2[i].label=f"item{i+10}"s_concat=np.concatenate([s, s2])
print("Concatenated Struct (1D):", s_concat)
# Create a 1D Cell arrayc=spm.Cell(4)
c[:] = ["hello", 123, {"a": 1}, [1, 2, 3]]
print("Original Cell:", c)
# Create a 1D Cell arrayc_extra=spm.Cell(2)
c_extra[:] = ["more", "cells"]
# Concatenate the Cell arraysc_concat=np.concatenate([c, c_extra])
print("Concatenated Cell (1D):", c_concat.tolist())
Original Cell: [hello, 123, [a], [1, 2, 3]]
Concatenated Cell (1D): ['hello', 123, Cell(['a']), Cell([1, 2, 3]), 'more', 'cells']

2.3. Type inference

One of the nice feature these types have is type inference at construction time. This enables accessing undefined field of an array, as long as the indexing sequence ends up with an assignment. There are a few extra rules:

  1. .: Use dot indexing to create a new field, as you'd use . in Matlab,
  2. []: Use square brackets for array indexing, as you'd use () in Matlab,
  3. (): Use round brackets for cell indexing, as you'd use {} in Matlab,

For example, to create a struct array with a field containing a cell array with, in third position, a struct array with a 2-by-2 random matrix in fifth position (showcasing all three rules):

S=spm.Struct()
S.field(3).struct[5].elem=np.random.rand(2, 2)
S
{'field': Cell([Array([]), Array([]), Array([]),
{'struct': Struct([{'elem': Array([])}, {'elem': Array([])}, {'elem': Array([])},
{'elem': Array([])}, {'elem': Array([])},
{'elem': Array([[0.38339607, 0.55686198],
[0.53678757, 0.5828017 ]])} ])} ])}

There is one caveat though: elements of unfinalised cell arrays cannot be specified:

>>>S=spm.Struct()
>>>S.field(3) ='test'S.field(3) ='test'^SyntaxError: cannotassigntofunctioncallhere. Maybeyoumeant'=='insteadof'='?

This is why we need one additional rule:

  1. as_cell[]: Initialising an element of an unfinalised cell array needs to use as_cell.

Using as_cell solves exactly this problem:

S=spm.Struct()
S.field.as_cell[3] ="test"S
{'field': Cell([Array([]), Array([]), Array([]), 'test'])}