Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use constrained layout in matplotlib visualization #12050

Merged
merged 35 commits into from
Oct 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
af6e250
rm utility function tight_layout
mscheltienne Oct 2, 2023
896c67e
rm imports
mscheltienne Oct 2, 2023
721bcc4
first pass
mscheltienne Oct 2, 2023
bfd8bf2
second pass
mscheltienne Oct 2, 2023
8f08874
bump minimum matplotlib requirement
mscheltienne Oct 2, 2023
00ebf20
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 2, 2023
affa975
Merge remote-tracking branch 'upstream/main' into layout
mscheltienne Oct 2, 2023
dbbd6bd
rm unused imports
mscheltienne Oct 2, 2023
7f3a45d
fix typo
mscheltienne Oct 2, 2023
f536206
fix more layout="constrained"
mscheltienne Oct 2, 2023
b8cd21e
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 2, 2023
f22c531
add back on_resize to abstraction layer
mscheltienne Oct 2, 2023
e0ecbbc
Merge remote-tracking branch 'upstream/main' into layout
mscheltienne Oct 2, 2023
2f368ab
Merge remote-tracking branch 'upstream/main' into layout
mscheltienne Oct 2, 2023
6e1c104
fix more
mscheltienne Oct 3, 2023
4a8d99e
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 3, 2023
ad28e21
rm unused variables
mscheltienne Oct 3, 2023
40f1689
Merge remote-tracking branch 'upstream/main' into layout
mscheltienne Oct 3, 2023
b8ca1f9
fix one more
mscheltienne Oct 3, 2023
a80517c
fix one more
mscheltienne Oct 3, 2023
8a85050
Merge remote-tracking branch 'upstream/main' into layout
mscheltienne Oct 3, 2023
d85f485
Merge remote-tracking branch 'upstream/main' into layout
larsoner Oct 5, 2023
4ec17bd
WIP: Pass
larsoner Oct 5, 2023
4807652
FIX: More
larsoner Oct 5, 2023
9147e07
FIX: Add
larsoner Oct 5, 2023
62a10a9
FIX: Close [circle full]
larsoner Oct 5, 2023
d4b9bc1
FIX: Topo cant [circle full]
larsoner Oct 5, 2023
d6bf891
FIX: Fixes
larsoner Oct 5, 2023
db67b05
WIP: Stop [circle full]
larsoner Oct 5, 2023
7a4acdb
FIX: More [circle full]
larsoner Oct 5, 2023
3e94e88
FIX: Not for topo
larsoner Oct 5, 2023
0c2f63a
FIX: More [circle full]
larsoner Oct 5, 2023
4d11828
FIX: More more [circle full]
larsoner Oct 6, 2023
ebe299c
FIX: Old [circle full]
larsoner Oct 6, 2023
b6e4bee
FIX: Example [circle full]
larsoner Oct 6, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ The minimum required dependencies to run MNE-Python are:
- Python >= 3.8
- NumPy >= 1.21.2
- SciPy >= 1.7.1
- Matplotlib >= 3.4.3
- Matplotlib >= 3.5.0
- pooch >= 1.5
- tqdm
- Jinja2
Expand Down
1 change: 1 addition & 0 deletions doc/changes/devel.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ Enhancements
- 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`_)
- Add support for writing forward solutions to HDF5 and convenience function :meth:`mne.Forward.save` (:gh:`12036` by `Eric Larson`_)
- Refactored internals of :func:`mne.read_annotations` (:gh:`11964` by `Paul Roujansky`_)
- By default MNE-Python creates matplotlib figures with ``layout='constrained'`` rather than the default ``layout='tight'`` (:gh:`12050` by `Mathieu Scheltienne`_ and `Eric Larson`_)
- 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`_)
- 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`_)

Expand Down
1 change: 0 additions & 1 deletion doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -1290,7 +1290,6 @@ def reset_warnings(gallery_conf, fname):
warnings.filterwarnings("default", module="sphinx")
# allow these warnings, but don't show them
for key in (
"The module matplotlib.tight_layout is deprecated", # nilearn
"invalid version and will not be supported", # pyxdf
"distutils Version classes are deprecated", # seaborn and neo
"`np.object` is a deprecated alias for the builtin `object`", # pyxdf
Expand Down
6 changes: 2 additions & 4 deletions examples/decoding/decoding_rsa_sgskip.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@
##############################################################################
# Plot
labels = [""] * 5 + ["face"] + [""] * 11 + ["bodypart"] + [""] * 6
fig, ax = plt.subplots(1)
fig, ax = plt.subplots(1, layout="constrained")
im = ax.matshow(confusion, cmap="RdBu_r", clim=[0.3, 0.7])
ax.set_yticks(range(len(classes)))
ax.set_yticklabels(labels)
Expand All @@ -159,14 +159,13 @@
ax.axhline(11.5, color="k")
ax.axvline(11.5, color="k")
plt.colorbar(im)
plt.tight_layout()
plt.show()

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

##############################################################################
Expand Down
3 changes: 1 addition & 2 deletions examples/decoding/decoding_spoc_CMC.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,15 +68,14 @@
y_preds = cross_val_predict(clf, X, y, cv=cv)

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

##############################################################################
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@

# %%
# Plot
fig, ax = plt.subplots(constrained_layout=True)
fig, ax = plt.subplots(layout="constrained")
im = ax.matshow(
scores,
vmin=0,
Expand Down
9 changes: 5 additions & 4 deletions examples/decoding/decoding_xdawn_eeg.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,22 +99,24 @@
cm_normalized = cm.astype(float) / cm.sum(axis=1)[:, np.newaxis]

# Plot confusion matrix
fig, ax = plt.subplots(1)
fig, ax = plt.subplots(1, layout="constrained")
im = ax.imshow(cm_normalized, interpolation="nearest", cmap=plt.cm.Blues)
ax.set(title="Normalized Confusion matrix")
fig.colorbar(im)
tick_marks = np.arange(len(target_names))
plt.xticks(tick_marks, target_names, rotation=45)
plt.yticks(tick_marks, target_names)
fig.tight_layout()
ax.set(ylabel="True label", xlabel="Predicted label")

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

fig, axes = plt.subplots(
nrows=len(event_id), ncols=n_filter, figsize=(n_filter, len(event_id) * 2)
nrows=len(event_id),
ncols=n_filter,
figsize=(n_filter, len(event_id) * 2),
layout="constrained",
)
fitted_xdawn = clf.steps[0][1]
info = create_info(epochs.ch_names, 1, epochs.get_channel_types())
Expand All @@ -131,7 +133,6 @@
show=False,
)
axes[ii, 0].set(ylabel=cur_class)
fig.tight_layout(h_pad=1.0, w_pad=1.0, pad=0.1)

# %%
# References
Expand Down
16 changes: 5 additions & 11 deletions examples/decoding/receptive_field_mtrf.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,11 @@
n_channels = len(raw.ch_names)

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

# %%
# Create and fit a receptive field model
Expand Down Expand Up @@ -117,12 +116,11 @@
mean_scores = scores.mean(axis=0)

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

# %%
# Investigate model coefficients
Expand All @@ -134,7 +132,7 @@

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

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

# %%
# Create and fit a stimulus reconstruction model
Expand Down Expand Up @@ -240,15 +236,14 @@

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

# %%
# Investigate model coefficients
Expand Down Expand Up @@ -292,7 +287,6 @@
title="Inverse-transformed coefficients\nbetween delays %s and %s"
% (time_plot[0], time_plot[1])
)
mne.viz.tight_layout()

# %%
# References
Expand Down
6 changes: 2 additions & 4 deletions examples/inverse/label_source_activations.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@
# View source activations
# -----------------------

fig, ax = plt.subplots(1)
fig, ax = plt.subplots(1, layout="constrained")
t = 1e3 * stc_label.times
ax.plot(t, stc_label.data.T, "k", linewidth=0.5, alpha=0.5)
pe = [
Expand All @@ -81,7 +81,6 @@
xlim=xlim,
ylim=ylim,
)
mne.viz.tight_layout()

# %%
# Using vector solutions
Expand All @@ -92,7 +91,7 @@
pick_ori = "vector"
stc_vec = apply_inverse(evoked, inverse_operator, lambda2, method, pick_ori=pick_ori)
data = stc_vec.extract_label_time_course(label, src)
fig, ax = plt.subplots(1)
fig, ax = plt.subplots(1, layout="constrained")
stc_vec_label = stc_vec.in_label(label)
colors = ["#EE6677", "#228833", "#4477AA"]
for ii, name in enumerate("XYZ"):
Expand All @@ -117,4 +116,3 @@
xlim=xlim,
ylim=ylim,
)
mne.viz.tight_layout()
3 changes: 1 addition & 2 deletions examples/inverse/mixed_source_space_inverse.py
Original file line number Diff line number Diff line change
Expand Up @@ -194,9 +194,8 @@
)

# plot the times series of 2 labels
fig, axes = plt.subplots(1)
fig, axes = plt.subplots(1, layout="constrained")
axes.plot(1e3 * stc.times, label_ts[0][0, :], "k", label="bankssts-lh")
axes.plot(1e3 * stc.times, label_ts[0][-1, :].T, "r", label="Brain-stem")
axes.set(xlabel="Time (ms)", ylabel="MNE current (nAm)")
axes.legend()
mne.viz.tight_layout()
3 changes: 1 addition & 2 deletions examples/inverse/source_space_snr.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,9 @@
# Plot an average SNR across source points over time:
ave = np.mean(snr_stc.data, axis=0)

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

# Find time point of maximum SNR
maxidx = np.argmax(ave)
Expand Down
7 changes: 2 additions & 5 deletions examples/preprocessing/eeg_bridging.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@

bridged_idx, ed_matrix = ed_data[6]

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

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

fig.tight_layout()

# %%
# Examine the Distribution of Electrical Distances
# ------------------------------------------------
Expand Down Expand Up @@ -208,7 +206,7 @@
# reflect neural or at least anatomical differences as well (i.e. the
# distance from the sensors to the brain).

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

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

# %%
# For the group of subjects, let's look at their electrical distances
Expand Down
3 changes: 1 addition & 2 deletions examples/preprocessing/eeg_csd.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,7 @@
# CSD has parameters ``stiffness`` and ``lambda2`` affecting smoothing and
# spline flexibility, respectively. Let's see how they affect the solution:

fig, ax = plt.subplots(4, 4)
fig.subplots_adjust(hspace=0.5)
fig, ax = plt.subplots(4, 4, layout="constrained")
fig.set_size_inches(10, 10)
for i, lambda2 in enumerate([0, 1e-7, 1e-5, 1e-3]):
for j, m in enumerate([5, 4, 3, 2]):
Expand Down
3 changes: 1 addition & 2 deletions examples/preprocessing/eog_artifact_histogram.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@

# %%
# Plot EOG artifact distribution
fig, ax = plt.subplots()
fig, ax = plt.subplots(layout="constrained")
ax.stem(1e3 * epochs.times, data)
ax.set(xlabel="Times (ms)", ylabel="Blink counts (from %s trials)" % len(epochs))
fig.tight_layout()
7 changes: 3 additions & 4 deletions examples/preprocessing/eog_regression.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,10 +69,9 @@
epochs_after = mne.Epochs(raw_clean, events, event_id, tmin, tmax, baseline=(tmin, 0))
evoked_after = epochs_after.average()

fig, ax = plt.subplots(nrows=3, ncols=2, figsize=(10, 7), sharex=True, sharey="row")
fig, ax = plt.subplots(
nrows=3, ncols=2, figsize=(10, 7), sharex=True, sharey="row", layout="constrained"
)
evoked_before.plot(axes=ax[:, 0], spatial_colors=True)
evoked_after.plot(axes=ax[:, 1], spatial_colors=True)
fig.subplots_adjust(
top=0.905, bottom=0.09, left=0.08, right=0.975, hspace=0.325, wspace=0.145
)
fig.suptitle("Before --> After")
3 changes: 0 additions & 3 deletions examples/preprocessing/shift_evoked.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@

import matplotlib.pyplot as plt
import mne
from mne.viz import tight_layout
from mne.datasets import sample

print(__doc__)
Expand Down Expand Up @@ -60,5 +59,3 @@
titles=dict(grad="Absolute shift: 500 ms"),
time_unit="s",
)

tight_layout()
6 changes: 3 additions & 3 deletions examples/simulation/plot_stc_metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@
]

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

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

Expand Down Expand Up @@ -277,6 +277,6 @@

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