Skip to content

Commit fdaeb86

Browse files
Mathieu Scheltiennepre-commit-ci[bot]larsoner
authored
Use constrained layout in matplotlib visualization (mne-tools#12050)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Eric Larson <[email protected]>
1 parent 37ae7e3 commit fdaeb86

File tree

71 files changed

+351
-620
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

71 files changed

+351
-620
lines changed

README.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ The minimum required dependencies to run MNE-Python are:
9696
- Python >= 3.8
9797
- NumPy >= 1.21.2
9898
- SciPy >= 1.7.1
99-
- Matplotlib >= 3.4.3
99+
- Matplotlib >= 3.5.0
100100
- pooch >= 1.5
101101
- tqdm
102102
- Jinja2

doc/changes/devel.rst

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ Enhancements
3737
- Add :class:`~mne.time_frequency.EpochsSpectrumArray` and :class:`~mne.time_frequency.SpectrumArray` to support creating power spectra from :class:`NumPy array <numpy.ndarray>` data (:gh:`11803` by `Alex Rockhill`_)
3838
- Add support for writing forward solutions to HDF5 and convenience function :meth:`mne.Forward.save` (:gh:`12036` by `Eric Larson`_)
3939
- Refactored internals of :func:`mne.read_annotations` (:gh:`11964` by `Paul Roujansky`_)
40+
- By default MNE-Python creates matplotlib figures with ``layout='constrained'`` rather than the default ``layout='tight'`` (:gh:`12050` by `Mathieu Scheltienne`_ and `Eric Larson`_)
4041
- Enhance :func:`~mne.viz.plot_evoked_field` with a GUI that has controls for time, colormap, and contour lines (:gh:`11942` by `Marijn van Vliet`_)
4142
- Add :class:`mne.viz.ui_events.UIEvent` linking for interactive colorbars, allowing users to link figures and change the colormap and limits interactively. This supports :func:`~mne.viz.plot_evoked_topomap`, :func:`~mne.viz.plot_ica_components`, :func:`~mne.viz.plot_tfr_topomap`, :func:`~mne.viz.plot_projs_topomap`, :meth:`~mne.Evoked.plot_image`, and :meth:`~mne.Epochs.plot_image` (:gh:`12057` by `Santeri Ruuskanen`_)
4243

doc/conf.py

-1
Original file line numberDiff line numberDiff line change
@@ -1291,7 +1291,6 @@ def reset_warnings(gallery_conf, fname):
12911291
warnings.filterwarnings("default", module="sphinx")
12921292
# allow these warnings, but don't show them
12931293
for key in (
1294-
"The module matplotlib.tight_layout is deprecated", # nilearn
12951294
"invalid version and will not be supported", # pyxdf
12961295
"distutils Version classes are deprecated", # seaborn and neo
12971296
"`np.object` is a deprecated alias for the builtin `object`", # pyxdf

examples/decoding/decoding_rsa_sgskip.py

+2-4
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@
150150
##############################################################################
151151
# Plot
152152
labels = [""] * 5 + ["face"] + [""] * 11 + ["bodypart"] + [""] * 6
153-
fig, ax = plt.subplots(1)
153+
fig, ax = plt.subplots(1, layout="constrained")
154154
im = ax.matshow(confusion, cmap="RdBu_r", clim=[0.3, 0.7])
155155
ax.set_yticks(range(len(classes)))
156156
ax.set_yticklabels(labels)
@@ -159,14 +159,13 @@
159159
ax.axhline(11.5, color="k")
160160
ax.axvline(11.5, color="k")
161161
plt.colorbar(im)
162-
plt.tight_layout()
163162
plt.show()
164163

165164
##############################################################################
166165
# Confusion matrix related to mental representations have been historically
167166
# summarized with dimensionality reduction using multi-dimensional scaling [1].
168167
# See how the face samples cluster together.
169-
fig, ax = plt.subplots(1)
168+
fig, ax = plt.subplots(1, layout="constrained")
170169
mds = MDS(2, random_state=0, dissimilarity="precomputed")
171170
chance = 0.5
172171
summary = mds.fit_transform(chance - confusion)
@@ -186,7 +185,6 @@
186185
)
187186
ax.axis("off")
188187
ax.legend(loc="lower right", scatterpoints=1, ncol=2)
189-
plt.tight_layout()
190188
plt.show()
191189

192190
##############################################################################

examples/decoding/decoding_spoc_CMC.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -68,15 +68,14 @@
6868
y_preds = cross_val_predict(clf, X, y, cv=cv)
6969

7070
# Plot the True EMG power and the EMG power predicted from MEG data
71-
fig, ax = plt.subplots(1, 1, figsize=[10, 4])
71+
fig, ax = plt.subplots(1, 1, figsize=[10, 4], layout="constrained")
7272
times = raw.times[meg_epochs.events[:, 0] - raw.first_samp]
7373
ax.plot(times, y_preds, color="b", label="Predicted EMG")
7474
ax.plot(times, y, color="r", label="True EMG")
7575
ax.set_xlabel("Time (s)")
7676
ax.set_ylabel("EMG Power")
7777
ax.set_title("SPoC MEG Predictions")
7878
plt.legend()
79-
mne.viz.tight_layout()
8079
plt.show()
8180

8281
##############################################################################

examples/decoding/decoding_time_generalization_conditions.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@
8888

8989
# %%
9090
# Plot
91-
fig, ax = plt.subplots(constrained_layout=True)
91+
fig, ax = plt.subplots(layout="constrained")
9292
im = ax.matshow(
9393
scores,
9494
vmin=0,

examples/decoding/decoding_xdawn_eeg.py

+5-4
Original file line numberDiff line numberDiff line change
@@ -99,22 +99,24 @@
9999
cm_normalized = cm.astype(float) / cm.sum(axis=1)[:, np.newaxis]
100100

101101
# Plot confusion matrix
102-
fig, ax = plt.subplots(1)
102+
fig, ax = plt.subplots(1, layout="constrained")
103103
im = ax.imshow(cm_normalized, interpolation="nearest", cmap=plt.cm.Blues)
104104
ax.set(title="Normalized Confusion matrix")
105105
fig.colorbar(im)
106106
tick_marks = np.arange(len(target_names))
107107
plt.xticks(tick_marks, target_names, rotation=45)
108108
plt.yticks(tick_marks, target_names)
109-
fig.tight_layout()
110109
ax.set(ylabel="True label", xlabel="Predicted label")
111110

112111
# %%
113112
# The ``patterns_`` attribute of a fitted Xdawn instance (here from the last
114113
# cross-validation fold) can be used for visualization.
115114

116115
fig, axes = plt.subplots(
117-
nrows=len(event_id), ncols=n_filter, figsize=(n_filter, len(event_id) * 2)
116+
nrows=len(event_id),
117+
ncols=n_filter,
118+
figsize=(n_filter, len(event_id) * 2),
119+
layout="constrained",
118120
)
119121
fitted_xdawn = clf.steps[0][1]
120122
info = create_info(epochs.ch_names, 1, epochs.get_channel_types())
@@ -131,7 +133,6 @@
131133
show=False,
132134
)
133135
axes[ii, 0].set(ylabel=cur_class)
134-
fig.tight_layout(h_pad=1.0, w_pad=1.0, pad=0.1)
135136

136137
# %%
137138
# References

examples/decoding/receptive_field_mtrf.py

+5-11
Original file line numberDiff line numberDiff line change
@@ -67,12 +67,11 @@
6767
n_channels = len(raw.ch_names)
6868

6969
# Plot a sample of brain and stimulus activity
70-
fig, ax = plt.subplots()
70+
fig, ax = plt.subplots(layout="constrained")
7171
lns = ax.plot(scale(raw[:, :800][0].T), color="k", alpha=0.1)
7272
ln1 = ax.plot(scale(speech[0, :800]), color="r", lw=2)
7373
ax.legend([lns[0], ln1[0]], ["EEG", "Speech Envelope"], frameon=False)
7474
ax.set(title="Sample activity", xlabel="Time (s)")
75-
mne.viz.tight_layout()
7675

7776
# %%
7877
# Create and fit a receptive field model
@@ -117,12 +116,11 @@
117116
mean_scores = scores.mean(axis=0)
118117

119118
# Plot mean prediction scores across all channels
120-
fig, ax = plt.subplots()
119+
fig, ax = plt.subplots(layout="constrained")
121120
ix_chs = np.arange(n_channels)
122121
ax.plot(ix_chs, mean_scores)
123122
ax.axhline(0, ls="--", color="r")
124123
ax.set(title="Mean prediction score", xlabel="Channel", ylabel="Score ($r$)")
125-
mne.viz.tight_layout()
126124

127125
# %%
128126
# Investigate model coefficients
@@ -134,7 +132,7 @@
134132

135133
# Print mean coefficients across all time delays / channels (see Fig 1)
136134
time_plot = 0.180 # For highlighting a specific time.
137-
fig, ax = plt.subplots(figsize=(4, 8))
135+
fig, ax = plt.subplots(figsize=(4, 8), layout="constrained")
138136
max_coef = mean_coefs.max()
139137
ax.pcolormesh(
140138
times,
@@ -155,16 +153,14 @@
155153
xticks=np.arange(tmin, tmax + 0.2, 0.2),
156154
)
157155
plt.setp(ax.get_xticklabels(), rotation=45)
158-
mne.viz.tight_layout()
159156

160157
# Make a topographic map of coefficients for a given delay (see Fig 2C)
161158
ix_plot = np.argmin(np.abs(time_plot - times))
162-
fig, ax = plt.subplots()
159+
fig, ax = plt.subplots(layout="constrained")
163160
mne.viz.plot_topomap(
164161
mean_coefs[:, ix_plot], pos=info, axes=ax, show=False, vlim=(-max_coef, max_coef)
165162
)
166163
ax.set(title="Topomap of model coefficients\nfor delay %s" % time_plot)
167-
mne.viz.tight_layout()
168164

169165
# %%
170166
# Create and fit a stimulus reconstruction model
@@ -240,15 +236,14 @@
240236

241237
y_pred = sr.predict(Y[test])
242238
time = np.linspace(0, 2.0, 5 * int(sfreq))
243-
fig, ax = plt.subplots(figsize=(8, 4))
239+
fig, ax = plt.subplots(figsize=(8, 4), layout="constrained")
244240
ax.plot(
245241
time, speech[test][sr.valid_samples_][: int(5 * sfreq)], color="grey", lw=2, ls="--"
246242
)
247243
ax.plot(time, y_pred[sr.valid_samples_][: int(5 * sfreq)], color="r", lw=2)
248244
ax.legend([lns[0], ln1[0]], ["Envelope", "Reconstruction"], frameon=False)
249245
ax.set(title="Stimulus reconstruction")
250246
ax.set_xlabel("Time (s)")
251-
mne.viz.tight_layout()
252247

253248
# %%
254249
# Investigate model coefficients
@@ -292,7 +287,6 @@
292287
title="Inverse-transformed coefficients\nbetween delays %s and %s"
293288
% (time_plot[0], time_plot[1])
294289
)
295-
mne.viz.tight_layout()
296290

297291
# %%
298292
# References

examples/inverse/label_source_activations.py

+2-4
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@
6262
# View source activations
6363
# -----------------------
6464

65-
fig, ax = plt.subplots(1)
65+
fig, ax = plt.subplots(1, layout="constrained")
6666
t = 1e3 * stc_label.times
6767
ax.plot(t, stc_label.data.T, "k", linewidth=0.5, alpha=0.5)
6868
pe = [
@@ -81,7 +81,6 @@
8181
xlim=xlim,
8282
ylim=ylim,
8383
)
84-
mne.viz.tight_layout()
8584

8685
# %%
8786
# Using vector solutions
@@ -92,7 +91,7 @@
9291
pick_ori = "vector"
9392
stc_vec = apply_inverse(evoked, inverse_operator, lambda2, method, pick_ori=pick_ori)
9493
data = stc_vec.extract_label_time_course(label, src)
95-
fig, ax = plt.subplots(1)
94+
fig, ax = plt.subplots(1, layout="constrained")
9695
stc_vec_label = stc_vec.in_label(label)
9796
colors = ["#EE6677", "#228833", "#4477AA"]
9897
for ii, name in enumerate("XYZ"):
@@ -117,4 +116,3 @@
117116
xlim=xlim,
118117
ylim=ylim,
119118
)
120-
mne.viz.tight_layout()

examples/inverse/mixed_source_space_inverse.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -194,9 +194,8 @@
194194
)
195195

196196
# plot the times series of 2 labels
197-
fig, axes = plt.subplots(1)
197+
fig, axes = plt.subplots(1, layout="constrained")
198198
axes.plot(1e3 * stc.times, label_ts[0][0, :], "k", label="bankssts-lh")
199199
axes.plot(1e3 * stc.times, label_ts[0][-1, :].T, "r", label="Brain-stem")
200200
axes.set(xlabel="Time (ms)", ylabel="MNE current (nAm)")
201201
axes.legend()
202-
mne.viz.tight_layout()

examples/inverse/source_space_snr.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,9 @@
5151
# Plot an average SNR across source points over time:
5252
ave = np.mean(snr_stc.data, axis=0)
5353

54-
fig, ax = plt.subplots()
54+
fig, ax = plt.subplots(layout="constrained")
5555
ax.plot(evoked.times, ave)
5656
ax.set(xlabel="Time (s)", ylabel="SNR MEG-EEG")
57-
fig.tight_layout()
5857

5958
# Find time point of maximum SNR
6059
maxidx = np.argmax(ave)

examples/preprocessing/eeg_bridging.py

+2-5
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@
8888

8989
bridged_idx, ed_matrix = ed_data[6]
9090

91-
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(8, 4))
91+
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(8, 4), layout="constrained")
9292
fig.suptitle("Subject 6 Electrical Distance Matrix")
9393

9494
# take median across epochs, only use upper triangular, lower is NaNs
@@ -110,8 +110,6 @@
110110
ax.set_xlabel("Channel Index")
111111
ax.set_ylabel("Channel Index")
112112

113-
fig.tight_layout()
114-
115113
# %%
116114
# Examine the Distribution of Electrical Distances
117115
# ------------------------------------------------
@@ -208,7 +206,7 @@
208206
# reflect neural or at least anatomical differences as well (i.e. the
209207
# distance from the sensors to the brain).
210208

211-
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(8, 4))
209+
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(8, 4), layout="constrained")
212210
fig.suptitle("Electrical Distance Distribution for EEGBCI Subjects")
213211
for ax in (ax1, ax2):
214212
ax.set_ylabel("Count")
@@ -229,7 +227,6 @@
229227

230228
ax1.axvspan(0, 30, color="r", alpha=0.5)
231229
ax2.legend(loc=(1.04, 0))
232-
fig.subplots_adjust(right=0.725, bottom=0.15, wspace=0.4)
233230

234231
# %%
235232
# For the group of subjects, let's look at their electrical distances

examples/preprocessing/eeg_csd.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,7 @@
7878
# CSD has parameters ``stiffness`` and ``lambda2`` affecting smoothing and
7979
# spline flexibility, respectively. Let's see how they affect the solution:
8080

81-
fig, ax = plt.subplots(4, 4)
82-
fig.subplots_adjust(hspace=0.5)
81+
fig, ax = plt.subplots(4, 4, layout="constrained")
8382
fig.set_size_inches(10, 10)
8483
for i, lambda2 in enumerate([0, 1e-7, 1e-5, 1e-3]):
8584
for j, m in enumerate([5, 4, 3, 2]):

examples/preprocessing/eog_artifact_histogram.py

+1-2
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@
5050

5151
# %%
5252
# Plot EOG artifact distribution
53-
fig, ax = plt.subplots()
53+
fig, ax = plt.subplots(layout="constrained")
5454
ax.stem(1e3 * epochs.times, data)
5555
ax.set(xlabel="Times (ms)", ylabel="Blink counts (from %s trials)" % len(epochs))
56-
fig.tight_layout()

examples/preprocessing/eog_regression.py

+3-4
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,9 @@
6969
epochs_after = mne.Epochs(raw_clean, events, event_id, tmin, tmax, baseline=(tmin, 0))
7070
evoked_after = epochs_after.average()
7171

72-
fig, ax = plt.subplots(nrows=3, ncols=2, figsize=(10, 7), sharex=True, sharey="row")
72+
fig, ax = plt.subplots(
73+
nrows=3, ncols=2, figsize=(10, 7), sharex=True, sharey="row", layout="constrained"
74+
)
7375
evoked_before.plot(axes=ax[:, 0], spatial_colors=True)
7476
evoked_after.plot(axes=ax[:, 1], spatial_colors=True)
75-
fig.subplots_adjust(
76-
top=0.905, bottom=0.09, left=0.08, right=0.975, hspace=0.325, wspace=0.145
77-
)
7877
fig.suptitle("Before --> After")

examples/preprocessing/shift_evoked.py

-3
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414

1515
import matplotlib.pyplot as plt
1616
import mne
17-
from mne.viz import tight_layout
1817
from mne.datasets import sample
1918

2019
print(__doc__)
@@ -60,5 +59,3 @@
6059
titles=dict(grad="Absolute shift: 500 ms"),
6160
time_unit="s",
6261
)
63-
64-
tight_layout()

examples/simulation/plot_stc_metrics.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@
234234
]
235235

236236
# Plot the results
237-
f, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, sharex="col", constrained_layout=True)
237+
f, ((ax1, ax2), (ax3, ax4)) = plt.subplots(2, 2, sharex="col", layout="constrained")
238238
for ax, (title, results) in zip([ax1, ax2, ax3, ax4], region_results.items()):
239239
ax.plot(thresholds, results, ".-")
240240
ax.set(title=title, ylabel="score", xlabel="Threshold", xticks=thresholds)
@@ -243,7 +243,7 @@
243243
ax1.ticklabel_format(axis="y", style="sci", scilimits=(0, 1)) # tweak RLE
244244

245245
# Cosine score with respect to time
246-
f, ax1 = plt.subplots(constrained_layout=True)
246+
f, ax1 = plt.subplots(layout="constrained")
247247
ax1.plot(stc_true_region.times, cosine_score(stc_true_region, stc_est_region))
248248
ax1.set(title="Cosine score", xlabel="Time", ylabel="Score")
249249

@@ -277,6 +277,6 @@
277277

278278
# Plot the results
279279
for name, results in dipole_results.items():
280-
f, ax1 = plt.subplots(constrained_layout=True)
280+
f, ax1 = plt.subplots(layout="constrained")
281281
ax1.plot(thresholds, 100 * np.array(results), ".-")
282282
ax1.set(title=name, ylabel="Error (cm)", xlabel="Threshold", xticks=thresholds)

0 commit comments

Comments
 (0)