Matplotlib Object Oriented Code to display inline in the notebook - python

Any ideas on how I can get this code
# -*- noplot -*-
"""
=============================
The object-oriented interface
=============================
A pure OO (look Ma, no pylab!) example using the agg backend
"""
from matplotlib.backends.backend_agg import FigureCanvasAgg as FigureCanvas
from matplotlib.figure import Figure
fig = Figure()
canvas = FigureCanvas(fig)
ax = fig.add_subplot(111)
ax.plot([1, 2, 3])
ax.set_title('hi mom')
ax.grid(True)
ax.set_xlabel('time')
ax.set_ylabel('volts')
from the matplotlib example gallery at this link to show me the chart in-line in my notebook?
Please Note:
I want to avoid using pyplot as I am trying to use matplotlib using their "Object Oriented" Library only
I have no issues getting pyplot based plots to render inline in my notebook using the %matplotlib inline or %matplotlib notebook magic
This confusing Object Oriented API of matplotlib isn't necessarily rendering inline.
Should I be using a different canvas?
Using fig.show() gives me the following error
AttributeError: 'FigureCanvasAgg' object has no attribute 'manager'
Figure.show works only for figures managed by pyplot, normally created by pyplot.figure().
Also, this particular canvas doesn't have a show method. So I am totally lost on how to get these darn Obj Oriented plots to render inline.

To display a figure which does not live in pyplot and has no figure manager associated with it, you can use IPython.core.display:
from IPython.core.display import display
display(fig)
Just note that there is actually no reason at all not to use pyplot to create the figure. Using pyplot, the code is much cleaner and will automatically show.
%matplotlib inline
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.plot([1, 2, 3])
ax.set_title('hi mom')
ax.grid(True)
ax.set_xlabel('time')
ax.set_ylabel('volts');

Related

Mix matplotlib interactive and inline plots?

Many plots just doesn't need to be interactive so I tried to change them to inline plots.
I tried the following without success:
plt.close(fig). It clear the figure
plt.ioff(), failed
wrap codes between %matplotlib inline %matplotlib notebook. It close other interactive plots
There can only ever be one single backend be active. It would be possible to change the backend, but that would require to close the interactive figures.
An option is to work with interactive backend throughout (e.g. %matplotlib widget) and call a custom function that shows a png image inline once that is desired.
#Cell1
%matplotlib widget
#Cell2
import matplotlib.pyplot as plt
def fig2inline(fig):
from IPython.display import display, Image
from io import BytesIO
plt.close(fig)
buff = BytesIO()
fig.savefig(buff, format='png')
buff.seek(0)
display(Image(data=buff.getvalue()))
#Cell3: (show the interactive plot)
fig, ax = plt.subplots(figsize=(3, 1.7))
ax.plot([1,3,4]);
#Cell4: (show the inline plot)
fig2, ax2 = plt.subplots(figsize=(3, 1.7))
ax2.plot([3,1,1]);
fig2inline(fig2)

Printing cursor coordinates in a matplotib figure in a Jupyter notebook: the smooth way

I want to display the coordinates of my cursor in an image displayed with matplotlib within a Jupyter notebook.
I am using the %matplotlib notebook magic as per this question.
While this provides a nice answer for a static figure, this results in a huge amount of flickering and bugs (the figure sometimes not showing) when used in an interactive setting where the figure is constantly redrawn during slicing. For example,
%matplotlib notebook
from ipywidgets import interact
import matplotlib.pyplot as plt
import numpy as np
vol = np.random.uniform(size=(16, 16, 16))
#interact(z=(0, 15))
def show(z):
plt.imshow(vol[z])
plt.show()
Without %matplotlib notebook, the figure is updating without any flicker, but does not show the cursor coordinates. With the magic, the coordinates are displayed, but the flickering is unbearable.
Is there a way to have pixel coordinates without flickering in that simple situation?
The problem is the use of plt.show(), which will replace the figure. Instead you probably want to update the existing figure.
%matplotlib notebook
from ipywidgets import interact
import matplotlib.pyplot as plt
import numpy as np
vol = np.random.uniform(size=(16, 16, 16))
fig, ax = plt.subplots()
im = ax.imshow(vol[0])
#interact(z=(0, 15))
def show(z):
im.set_array(vol[z])
im.set_clim(vol[z].min(), vol[z].max())
fig.canvas.draw_idle()
Note the the above provides the same functionality as the code in the question, i.e. each array is normalized individually. However, you might decide to set the color normalization only once such that all arrays share the same color limits.
%matplotlib notebook
from ipywidgets import interact
import matplotlib.pyplot as plt
import numpy as np
vol = np.random.uniform(size=(16, 16, 16))
fig, ax = plt.subplots()
im = ax.imshow(vol[0], vmin=vol.min(), vmax=vol.max())
fig.colorbar(im)
#interact(z=(0, 15))
def show(z):
im.set_array(vol[z])
fig.canvas.draw_idle()

Imagegrid in Jupyter notebook

I'm following an example from the matplotlib documentation on Imagegrid, and I'm trying to replicate it from within Jupyter notebook:
% matplotlib inline
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import ImageGrid
import numpy as np
im = np.arange(100)
im.shape = 10, 10
fig = plt.figure(1, (4., 4.))
grid = ImageGrid(fig, 111, # similar to subplot(111)
nrows_ncols=(2, 2), # creates 2x2 grid of axes
axes_pad=0.1, # pad between axes in inch.
)
for i in range(4):
grid[i].imshow(im) # The AxesGrid object work as a list of axes.
plt.show()
Expected output:
What I'm getting:
I'm not getting the grid of images, as you can see. What am I doing wrong?
EDIT
If I remove the %matplotlib inline option, I just get this (it's cell[1] to prove I restarted my kernel):
No plots shown.
I'm running matplotlib version 3.0.0, checked with conda list matplotlib, jupyter is 4.4.0, checked with jupyter --version. On Windows 10, Anaconda, python 3.6.
This is an issue with matplotlib 3.0.0. This has now been fixed, such that it will not occur in the upcoming 3.0.1 bugfix release.
In the meantime you have two options.
Revert to matplotlib 2.2.3
Decide to not crop the images when using %matplotlib inline. Do so via
%config InlineBackend.print_figure_kwargs = {'bbox_inches':None}
in IPython or Jupyter.
Remove
%matplotlib inline
and restart everything or put it in a separate cell as seen below. It appears that the magic command always needs to be run in a separate cell before the plotting and if it was run before the kernel needs to be restarted. See here
enter link description here
and it will work. %matplotlib inline is not necessary to render plots in jupyter it is just a convenience. plt.show() will render plots whenever it is called.
I have had this issue with some mpl in jupyter. I think the issue is that the magic command causes it to render any plot as soon as it is available as opposed to mpl which waits until it is told to render and how.
Full example code straight from the mpl example you linked in your question:
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import ImageGrid
import numpy as np
im = np.arange(100)
im.shape = 10, 10
fig = plt.figure(1, (4., 4.))
grid = ImageGrid(fig, 111, # similar to subplot(111)
nrows_ncols=(2, 2), # creates 2x2 grid of axes
axes_pad=0.1, # pad between axes in inch.
)
for i in range(4):
grid[i].imshow(im) # The AxesGrid object work as a list of axes.
plt.show() # Renders all available axes when called

How to create a plot in matplotlib without using pyplot

I've been using matplotlib for five months now on a daily basis, and I still find creation of new figures confusing.
Usually I create a figure with 2x2 subplots using, for example, somthing like:
import matplotlib.pyplot as plt
import itertools as it
fig,axes = plt.subplots(2,2)
axit = (ax for ax in it.chain(*axes))
for each of four data series I want to plot:
ax = next(axit)
ax.plot(...)
The question I have now is: how can operate completely independently of pyplot, ie, how can I create a figure, populate it with plots, make style changes, and only tell that figure to appear at the exact moment I want it to appear. Here is what I am having trouble with:
import matplotlib as mpl
gs = gridspec.GridSpec(2,2)
fig = mpl.figure.Figure()
ax1 = fig.add_subplot(gs[0])
ax1.plot([1,2,3])
ax2 = fig.add_subplot(gs[1])
ax2.plot([3,2,1])
After running the above, the only thing that comes to mind would be to use:
plt.draw()
But this does not work. What is missing to make the figure with the plots appear? Also, is
fig = mpl.figure.Figure()
all I have to do to create the figure without pyplot?
This works for me without matplotlib.pyplot
import sys
from PyQt5 import QtWidgets
from matplotlib.backends.backend_qt5agg import (
FigureCanvasQTAgg as FigureCanvas)
from matplotlib.figure import Figure
import numpy as np
fig=Figure()
canvas=FigureCanvas(fig)
ax=canvas.figure.add_subplot(111)
x=np.arange(-5,5,0.1)
y=np.sin(x)
ax.plot(x,y)
canvas.show()
app=QtWidgets.QApplication(sys.argv)
app.exec()
You could attach a suitable backend to your figure manually and then show it:
from matplotlib.backends import backend_qt4agg # e.g.
backend_qt4agg.new_figure_manager_given_figure(1, fig)
fig.show()
... but why not use pyplot?

Prevent matplotlib statefulness

If I create an Axes object in matplotlib and mutate it (i.e. by plotting some data) and then I call a function without passing my Axes object to that function then that function can still mutate my Axes. For example:
import matplotlib.pyplot as plt
import numpy as np
def innocent_looking_function():
#let's draw a red line on some unsuspecting Axes!
plt.plot(100*np.random.rand(20), color='r')
fig, ax = plt.subplots()
ax.plot(100*np.random.rand(20), color='b') #draw blue line on ax
#ax now has a blue line, as expected
innocent_looking_function()
#ax now unexpectedly has a blue line and a red line!
My question is: can I prevent this global-variable behaviour in general? I know I can call plt.close() before calling any innocent_looking_function() but is there some way to make this the default?
Sure! What you need to do is bypass the pyplot state machine entirely when you make your figure.
It's more verbose, as you can't just call fig = plt.figure().
First off, let me explain how plt.gca() or plt.gcf() works. When using the pyplot interface, matplotlib stores all created-but-not-displayed figure managers. Figure managers are basically the gui wrapper for a figure.
plt._pylab_helpers.Gcf is the singleton object that stores the figure managers and keeps track of which one is currently active. plt.gcf() returns the active figure from _pylab_helpers.Gcf. Each Figure object keeps track of it's own axes, so plt.gca() is just plt.gcf().gca().
Normally, when you call plt.figure(), it:
Creates the figure object that's returned
Creates a FigureManager for that figure using the appropriate backend
The figure manager creates a FigureCanvas, gui window (as needed), and NavigationToolbar2 (zoom buttons, etc)
The figure manager instance is then added to _pylab_helpers.Gcf's list of figures.
It's this last step that we want to bypass.
Here's a quick example using a non-interactive backend. Note that because we're not worried about interacting with the plot, we can skip the entire figure manager and just create a Figure and FigureCanvas instance. (Technically we could skip the FigureCanvas, but it will be needed as soon as we want to save the plot to an image, etc.)
import matplotlib.backends.backend_agg as backend
from matplotlib.figure import Figure
# The pylab figure manager will be bypassed in this instance. `plt.gca()`
# can't access the axes created here.
fig = Figure()
canvas = backend.FigureCanvas(fig)
ax = fig.add_subplot(111)
Just to prove that gca can't see this axes:
import matplotlib.pyplot as plt
import matplotlib.backends.backend_agg as backend
from matplotlib.figure import Figure
# Independent figure/axes
fig = Figure()
canvas = backend.FigureCanvas(fig)
ax = fig.add_subplot(111)
ax.plot(range(10))
# gca() is completely unaware of this axes and will create a new one instead:
ax2 = plt.gca()
print 'Same axes?:', id(ax) == id(ax2)
# And `plt.show()` would show the blank axes of `ax2`
With an interactive backed, it's a touch more complicated. You can't call plt.show(), so you need to start the gui's mainloop yourself. You can do it all "from scratch" (see any of the "embedding matplotlib" examples), but the FigureManager abstracts the backed-specific parts away:
As an example using the TkAgg backend:
import matplotlib.backends.backend_tkagg as backend
from matplotlib.figure import Figure
fig = Figure()
ax = fig.add_subplot(111)
manager = backend.new_figure_manager_given_figure(1, fig)
manager.show()
backend.show.mainloop()
To use one of the other backends, just change the backend import. For example, for Qt4:
import matplotlib.backends.backend_qt4agg as backend
from matplotlib.figure import Figure
fig = Figure()
ax = fig.add_subplot(111)
manager = backend.new_figure_manager_given_figure(1, fig)
manager.show()
backend.show.mainloop()
This actually even works with the nbagg backend used in IPython notebooks. Just change the backend import to import matplotlib.backends.backend_nbagg as backend

Categories

Resources