Skip to content

Commit fa6976a

Browse files
committed
single evokeds instead of evoked with dipoles as timepoints
1 parent bd754f8 commit fa6976a

1 file changed

Lines changed: 16 additions & 11 deletions

File tree

tutorials/inverse/80_brainstorm_phantom_elekta.py

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -98,14 +98,11 @@
9898
print(f"Strongest peak at {t_peak * 1000:.1f} ms")
9999

100100
# Here we store the evoked data for each dipole at the peak amplitude.
101-
data = []
101+
evokeds = []
102102
for ii in event_id:
103103
evoked = epochs[str(ii)][1:-1].average().crop(t_peak, t_peak)
104-
data.append(evoked.data[:, 0])
105-
106-
# Finally, we store the evokeds from all dipoles as an evoked,
107-
# treating every dipole as a timepoint.
108-
evoked = mne.EvokedArray(np.array(data).T, evoked.info, tmin=0.0)
104+
evoked = mne.EvokedArray(np.array(evoked.data), evoked.info, tmin=0.0)
105+
evokeds.append(evoked)
109106

110107
# Next, we need to compute the noise covariance to capture the sensor noise structure.
111108
# We use the baseline window to estimate covariance.
@@ -120,12 +117,16 @@
120117
fetch_phantom("otaniemi", subjects_dir=subjects_dir)
121118
sphere = mne.make_sphere_model(r0=(0.0, 0.0, 0.0), head_radius=0.08)
122119

120+
dip_all, residuals_all = [], []
123121
# Let's finally fit all 32 phantom dipoles.
124-
dip, residual = fit_dipole(evoked, cov, sphere, n_jobs=1)
122+
for evoked in evokeds:
123+
dip, residual = fit_dipole(evoked, cov, sphere, n_jobs=1)
124+
dip_all.append(dip)
125+
residuals_all.append(residual)
125126
# %%
126127
# Let's visualize the explained variance.
127128
# The dipole object stores the goodness of fit (GOF) for each dipole.
128-
gof = dip.gof # array of GOF values
129+
gof = [dip.gof[0] for dip in dip_all] # array of GOF values
129130

130131
# Define colorblind-friendly colors
131132
colors = ["#E69F00" if val < 60 else "#0072B2" for val in gof]
@@ -142,29 +143,33 @@
142143
# Finally, we compare the estimated to the true dipole locations.
143144
actual_pos, actual_ori = mne.dipole.get_phantom_dipoles()
144145
actual_amp = 200.0 # nAm
146+
# dipole estimations
147+
dip_pos = [dip.pos[0] for dip in dip_all]
148+
dip_ori = [dip.ori[0] for dip in dip_all]
149+
dip_amplitude = [dip.amplitude[0] for dip in dip_all]
145150

146151
fig, (ax1, ax2, ax3) = plt.subplots(
147152
nrows=3, ncols=1, figsize=(6, 7), layout="constrained"
148153
)
149154

150155
# Here we calculate the euclidean distance between estimated and true positions.
151156
# We multiply by 1000 to convert from meter to millimeter.
152-
diffs = 1000 * np.sqrt(np.sum((dip.pos - actual_pos) ** 2, axis=-1))
157+
diffs = 1000 * np.sqrt(np.sum((dip_pos - actual_pos) ** 2, axis=-1))
153158
print(f"mean(position error) = {np.mean(diffs):0.1f} mm")
154159
ax1.bar(event_id, diffs)
155160
ax1.set_xlabel("Dipole index")
156161
ax1.set_ylabel("Loc. error (mm)")
157162

158163
# Next we calculate the angle between estimated and true orientation.
159164
# We convert radians to degrees.
160-
angles = np.rad2deg(np.arccos(np.abs(np.sum(dip.ori * actual_ori, axis=1))))
165+
angles = np.rad2deg(np.arccos(np.abs(np.sum(dip_ori * actual_ori, axis=1))))
161166
print(f"mean(angle error) = {np.mean(angles):0.1f}°")
162167
ax2.bar(event_id, angles)
163168
ax2.set_xlabel("Dipole index")
164169
ax2.set_ylabel("Angle error (°)")
165170

166171
# Finally we compare amplitudes by subtracting estimated from true amplitude.
167-
amps = actual_amp - dip.amplitude / 1e-9
172+
amps = actual_amp - np.array(dip_amplitude) / 1e-9
168173
print(f"mean(abs amplitude error) = {np.mean(np.abs(amps)):0.1f} nAm")
169174
ax3.bar(event_id, amps)
170175
ax3.set_xlabel("Dipole index")

0 commit comments

Comments
 (0)