How to add mne plot to another subplot

The objective is to create subplot that consist

  1. Line plot
  2. raw data plot with annotation

The expected output is as shown the figure below

The line plot can be easily created by simply passing the axes parameter

import matplotlib.pyplot as plt
fig = plt.figure(figsize=(9, 10))

ax1 = fig.add_subplot(2, 1, 1)
ax1.plot(range(10))

However, I am having difficulties in subploting the row plot since mne.io.Raw.plot does not accept axes

import os

import mne

sample_data_folder = mne.datasets.sample.data_path()
sample_data_raw_file = os.path.join(sample_data_folder, 'MEG', 'sample',
                                    'sample_audvis_raw.fif')
raw = mne.io.read_raw_fif(sample_data_raw_file, verbose=False)
picks = mne.pick_channels_regexp(raw.ch_names, regexp='EEG 05.')
fig=raw.plot(order=picks, n_channels=len(picks))

While it is possible to first save locally fig.savefig() and append the raw.plot into the subplot as below

sub1 = fig.add_subplot(2, 1, 2)

    image2 = cv.imread()
    sub1.imshow(image2, 'gray')

But, this is inefficient for multiple plotting.

So, my question is, is there better workaround to append the raw.plot onto a subplot without first need to save locally the fig?

I don’t know the answer, but I’m interested to learn if this is possible. Raw.plot() returns a Figure consisting of multiple Axes, so I guess the first step would be to select the one you want. However, it doesn’t seem to be easily possible to move an Axes from one Figure to another from what I’ve seen in a quick Google search, so your solution is already pretty cool.

1 Like

Another alternative is to utilise the CanvasAgg backend directly to create image and extract this image to numpy array, which can in turn be passed off to matplotlib

First create the raw plot and expect an input in the form of MNEBrowseFigure

fig_raw=raw.plot(order=picks, show=False, n_channels=len(picks))

A canvas must be manually attached to the figure (pyplot would automatically do it). This is done by instantiating the canvas with the figure as argument.

canvas = FigureCanvasAgg(fig)

Retrieve a memory view on the renderer buffer, and convert it to a numpy array. There are two options to do this.

Option 1

canvas.draw ()
img = np.asarray(canvas.buffer_rgba())

Option 2

canvas.draw ()
img = np.asarray(canvas.buffer_rgba())
s, (width, height) = canvas.print_to_buffer ()
img = np.frombuffer ( s, np.uint8 ).reshape ( (height, width, 4) )

and pass it to matplotlib

sub1 = fig.add_subplot(2, 1, 2)
sub1.imshow(img)

This will output

Snap 2022-05-14 at 01.46.44

Reference
https://matplotlib.org/3.5.0/gallery/user_interfaces/canvasagg.html

Full code to reproduce the above figure

import os

import matplotlib.pyplot as plt
import mne
import numpy as np
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas

fig = plt.figure(figsize=(9, 10))

ax1 = fig.add_subplot(2, 1, 1)
ax1.plot(range(10))


sample_data_folder = mne.datasets.sample.data_path()
sample_data_raw_file = os.path.join(sample_data_folder, 'MEG', 'sample',
                                    'sample_audvis_raw.fif')
raw = mne.io.read_raw_fif(sample_data_raw_file, verbose=False)
picks = mne.pick_channels_regexp(raw.ch_names, regexp='EEG 05.')
fig_raw=raw.plot(order=picks, show=False, n_channels=len(picks))

canvas = FigureCanvas (fig_raw)
canvas.draw ()

img = np.asarray(canvas.buffer_rgba())
sub1 = fig.add_subplot(2, 1, 2)
sub1.imshow(img)
plt.show()
1 Like

I think another more or less straightforward approach is simply plotting to the Agg backend, extracting the figures as NumPy arrays, and then concatenating those: