Matplotlib 3.5 (or presumably better)
If you are using Matplotlib 3.5 (or presumably better), this works for what you want, I think (or close):
from mpl_toolkits.axes_grid1 import host_subplot
import mpl_toolkits.axisartist as axisartist
hostImage = host_subplot(221, axes_class=axisartist.Axes)
from matplotlib.offsetbox import TextArea, DrawingArea, OffsetImage, AnnotationBbox
import matplotlib.image as mpimg
test_image = mpimg.imread('testImage.png')
imagebox = OffsetImage(test_image, zoom=1)
ab = AnnotationBbox(imagebox, (-0.0025, 0), box_alignment=(1, 0))
hostImage.add_artist(ab)
hostImage.figure.subplots_adjust(left=0.69) # based on https://matplotlib.org/stable/tutorials/intermediate/tight_layout_guide.html saying how to manually adjust
hostImage.figure.set_size_inches((18, 10)) # from https://github.com/matplotlib/matplotlib/blob/master/lib/matplotlib/figure.py; also see drevicko's comment https://stackoverflow.com/a/638443/8508004
hostImage.figure.savefig("my_image_test.png") # fix for `hostImage.savefig("my_image_test.png")`, based on https://forum.freecodecamp.org/t/attribute-error-axessubplot-object-has-no-attribute-savefig/46025
This will show the same view of the produced plot in both the direct in JupyterLab output and in the image file produced. (The actual size will probably be slightly different, with the image file displaying better resolution.) **If you don't want to produce an image file, then you can remove the last two lines and just include the adjustment **,figure.subplots_adjust(left=0.69)
, to account for the Annotation box being added.
I put pertinent sources in the comments for each line.
My test image was wide and short so you may need to adjust figure.subplots_adjust(left=0.69)
to what works for you. (Now I don't like that I had to stumble around trying very high and low versions of the left
value for figure.subplots_adjust()
, and then hone in on a just-right setting but it worked. I will say that usually I set the figure size before making the subplots, such as here, and maybe doing it that way makes it seem less experimenting is necessary to get it working. But the fact the manual adjustment is mentioned in discussion of tight_layout in Matplotlib's documentation, in regards to elements going outside the figure area, makes me think it happens that you need to do some adjusting now and then.)
Here I use hostImage.figure.set_size_inches((18, 10))
. Maybe you don't need yours as wide?
Code for checking Matplotlib version:
import matplotlib
print (matplotlib.__version__ )
Matplotlib versions prior to 3.5 (or maybe specifically 3.2.1?)
The code above wasn't working with Matplotlib 3.2.1 with all else the same. (In launches of Jupyter sessions served via MyBinder from here before running %pip install matplotlib --upgrade
in a cell and restarting the kernel.) The image produced was good but the output directly in the Jupyter notebook was cutoff and only showing a fragment.
This code block below works for what you want, I think (or close), if using Matplotlib 3.2.1. Since I couldn't get the direct output in the Jupyter cell where I was using Matplotplib 3.2.1 to display correctly, this just displays the plot from the associated image file produced.
from mpl_toolkits.axes_grid1 import host_subplot
import mpl_toolkits.axisartist as axisartist
hostImage = host_subplot(221, axes_class=axisartist.Axes)
from matplotlib.offsetbox import TextArea, DrawingArea, OffsetImage, AnnotationBbox
import matplotlib.image as mpimg
test_image = mpimg.imread('testImage.png')
imagebox = OffsetImage(test_image, zoom=1)
ab = AnnotationBbox(imagebox, (-0.0025, 0), box_alignment=(1, 0))
hostImage.add_artist(ab)
hostImage.figure.subplots_adjust(left=0.69) # based on https://matplotlib.org/stable/tutorials/intermediate/tight_layout_guide.html saying how to manually adjust
hostImage.figure.set_size_inches((18, 10)) # from https://github.com/matplotlib/matplotlib/blob/master/lib/matplotlib/figure.py; also see drevicko's comment https://stackoverflow.com/a/638443/8508004
hostImage.figure.savefig("my_image_test.png") # fix for `hostImage.savefig("my_image_test.png")`, based on https://forum.freecodecamp.org/t/attribute-error-axessubplot-object-has-no-attribute-savefig/460255
hostImage.figure.clf() # using this so, Jupyter won't display the Matplotlib plot object; instead we'll show the image file
from IPython.display import Image
Image(filename="my_image_test.png")
How things are working for the shared lines I added is covered above.
Optionally when using Matplotlib 3.2.1 with code like here, to not also show the matplotlib cruft, such as something like <Figure size 1296x720 with 0 Axes>
, you can split running this between two cells.
First cell's code:
%%capture
from mpl_toolkits.axes_grid1 import host_subplot
import mpl_toolkits.axisartist as axisartist
hostImage = host_subplot(221, axes_class=axisartist.Axes)
from matplotlib.offsetbox import TextArea, DrawingArea, OffsetImage, AnnotationBbox
import matplotlib.image as mpimg
test_image = mpimg.imread('testImage.png')
imagebox = OffsetImage(test_image, zoom=1)
ab = AnnotationBbox(imagebox, (-0.0025, 0), box_alignment=(1, 0))
hostImage.add_artist(ab)
hostImage.figure.subplots_adjust(left=0.69) # based on https://matplotlib.org/stable/tutorials/intermediate/tight_layout_guide.html saying how to manually adjust
hostImage.figure.set_size_inches((18, 10)) # from https://github.com/matplotlib/matplotlib/blob/master/lib/matplotlib/figure.py; also see drevicko's comment https://stackoverflow.com/a/638443/8508004
hostImage.figure.savefig("my_image_test.png") # fix for `hostImage.savefig("my_image_test.png")`, based on https://forum.freecodecamp.org/t/attribute-error-axessubplot-object-has-no-attribute-savefig/460255
hostImage.figure.clf() # using this so, Jupyter won't display the Matplotlib plot object; instead we'll show the image file
Second cell's code:
from IPython.display import Image
Image(filename="my_image_test.png")
The first cell will show no output of any kind now due to the %%capture
cell magic.
UPDATE:
(code below only tested with Matplotlib 3.5.)
Some options based on addition of sample figure OP is using and additional information in comment here, I suggest starting over with simpler subplot use for arranging the two elements. (If it was much more complex, I'd suggest other methods for compositing the two elements. Options would include: If just for presenting in Jupyter, ipywidgets can be used for layout. Pillow and ReportLab can be useful if making a publication-quality figure is the goal.)
!curl -o testImage.png https://owncloud.tuwien.ac.at/index.php/s/3caJsb2PcwN7HdU/download
#based on https://matplotlib.org/stable/gallery/subplots_axes_and_figures/subplots_demo.html
# and https://www.moonbooks.org/Articles/How-to-insert-an-image-a-picture-or-a-photo-in-a-matplotlib-figure/
# and https://nbviewer.org/gist/fomightez/4c2116e50f080b1305c41b9ac70df124#Solution
# axis off for lower plot based on https://stackoverflow.com/a/10035974/8508004
import matplotlib.pyplot as plt
from matplotlib.offsetbox import TextArea, DrawingArea, OffsetImage, AnnotationBbox
import matplotlib.image as mpimg
fig, axs = plt.subplots(2,1,figsize=(4, 8))
#fig.suptitle('Vertically stacked subplots')
axs[0].grid()
axs[1].grid()
test_image = mpimg.imread('testImage.png')
imagebox = OffsetImage(test_image, zoom=1)
ab = AnnotationBbox(imagebox, (0.5,0.5))
axs[1].add_artist(ab)
axs[1].axis('off');
Or:
!curl -o testImage.png https://owncloud.tuwien.ac.at/index.php/s/3caJsb2PcwN7HdU/download
#based on https://matplotlib.org/stable/gallery/subplots_axes_and_figures/subplots_demo.html
# and https://www.moonbooks.org/Articles/How-to-insert-an-image-a-picture-or-a-photo-in-a-matplotlib-figure/
# and https://nbviewer.org/gist/fomightez/4c2116e50f080b1305c41b9ac70df124#Solution
# axis turned off for lower plot based on https://stackoverflow.com/a/10035974/8508004
import matplotlib.pyplot as plt
from matplotlib.offsetbox import TextArea, DrawingArea, OffsetImage, AnnotationBbox
import matplotlib.image as mpimg
# data to plot based on https://stackoverflow.com/a/17996099/8508004 and converting it
# to work with subplot method
fig, axs = plt.subplots(2,1)
plt.subplots_adjust(hspace=1.8) # to move the bottom plot down some so not covering the top small one
#fig.suptitle('Vertically stacked subplots')
axs[0].plot(range(15))
axs[0].set_xlim(-7, 7)
axs[0].set_ylim(-7, 7)
axs[0].set_aspect('equal')
axs[1].grid()
test_image = mpimg.imread('testImage.png')
imagebox = OffsetImage(test_image, zoom=1)
ab = AnnotationBbox(imagebox, (0.5,0.5))
axs[1].add_artist(ab)
axs[1].axis('off');
Or if want to save the figure something like:
!curl -o testImage.png https://owncloud.tuwien.ac.at/index.php/s/3caJsb2PcwN7HdU/download
#based on https://matplotlib.org/stable/gallery/subplots_axes_and_figures/subplots_demo.html
# and https://www.moonbooks.org/Articles/How-to-insert-an-image-a-picture-or-a-photo-in-a-matplotlib-figure/
# and https://nbviewer.org/gist/fomightez/4c2116e50f080b1305c41b9ac70df124#Solution
# axis turned off for lower plot based on https://stackoverflow.com/a/10035974/8508004
import matplotlib.pyplot as plt
from matplotlib.offsetbox import TextArea, DrawingArea, OffsetImage, AnnotationBbox
import matplotlib.image as mpimg
# data to plot based on https://stackoverflow.com/a/17996099/8508004 and converting it
# to work with subplot method
fig, axs = plt.subplots(2,1)
plt.subplots_adjust(hspace=0.3) # to move the bottom plot down some so not covering the top small one
#fig.suptitle('Vertically stacked subplots')
axs[0].plot(range(15))
axs[0].set_xlim(-7, 7)
axs[0].set_ylim(-7, 7)
axs[0].set_aspect('equal')
axs[1].grid()
test_image = mpimg.imread('testImage.png')
imagebox = OffsetImage(test_image, zoom=1)
ab = AnnotationBbox(imagebox, (0.5,0.5))
axs[1].add_artist(ab)
axs[1].axis('off')
# to accomodate this adjustment in the figure that gets saved via `plt.savefig()`, increase figure size
fig.set_size_inches((4, 7)) # from https://github.com/matplotlib/matplotlib/blob/master/lib/matplotlib/figure.py; also see drevicko's comment
plt.savefig("stacked.png");
I'm not sure while the size changes on the top plot if you set the size so you can accomodate them but there's some honing on the right numbers needed there.