-
Notifications
You must be signed in to change notification settings - Fork 1
Getting started
import spm
import numpy as np
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
SPM Python provides you with a type system that allows to reuse Matlab syntax without too much worries. The following classes are available:
-
spm.Cell
, for cell arrays -
spm.Struct
, for struct arrays -
spm.Array
, for general arrays
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 data
c[0] = "Hello"
c[1] = "World"
c[2] = 42
# Print the Cell array
print("Initial Cell array:", c.tolist())
# Add a new element in (undefined) index 4
c[4] = "New Element"
# Print the updated Cell array
print("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 Struct
example_struct = spm.Struct()
example_struct.name = "Example"
example_struct.value = 42
print("Single Struct example:", example_struct)
# Create a 1D Struct array
struct_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 array
struct_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 Struct
example_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 3
a1d = 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]]
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 array
s = spm.Struct(4)
# Populate the struct array with data
for i in range(4):
s[i].value = i
s[i].label = f"item{i}"
print("Original Struct:", s)
# Reshape the struct array to 2x2
s_reshaped = s.reshape((2, 2))
print("Reshaped Struct (2x2):\n", s_reshaped)
# Transpose the struct array
s_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 axis
s2 = spm.Struct(4)
for i in range(4):
s2[i].value = i + 10
s2[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 axis
s2 = spm.Struct(4)
for i in range(4):
s2[i].value = i + 10
s2[i].label = f"item{i+10}"
s_concat = np.concatenate([s, s2])
print("Concatenated Struct (1D):", s_concat)
# Create a 1D Cell array
c = spm.Cell(4)
c[:] = ["hello", 123, {"a": 1}, [1, 2, 3]]
print("Original Cell:", c)
# Create a 1D Cell array
c_extra = spm.Cell(2)
c_extra[:] = ["more", "cells"]
# Concatenate the Cell arrays
c_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']
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:
-
.
: Use dot indexing to create a new field, as you'd use.
in Matlab, -
[]
: Use square brackets for array indexing, as you'd use()
in Matlab, -
()
: 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: cannot assign to function call here. Maybe you meant '==' instead of '='?
This is why we need one additional rule:
-
as_cell[]
: Initialising an element of an unfinalised cell array needs to useas_cell
.
Using as_cell
solves exactly this problem:
S = spm.Struct()
S.field.as_cell[3] = "test"
S
{'field': Cell([Array([]), Array([]), Array([]), 'test'])}