Can I show a plot created in another file on JupyterLab? - python

I wish to have an interactive map that you can click where, once clicked, a SkewT and Hodograph will be plotted showing the information for that location. I have thus created a class where I add all the necessary informations using the metpy library and I am able to successfully create these graphs:
SkewT and Hodograph plotted
The problem comes when I'm trying to import the classes I've created to generate these plots into jupyterlab. Since the code to actually make these plots is quite cumbersome, I'd rather
keep the code in a separate file and import my SoundingGraphs class, but it's not working. The graphs never get plotted inside a cell, they instead appear in the logs as a Warning and as an Info and I have no idea why:
Graphs appearing inside logs
Tried to use plt.show() inside my file, tried returning plt to then use plt.show() inside a cell of the notebook, tried using %matplotlib widget, %matplotlib notebook and %matplotlib inline, tried changing jupyterlab versions, none of these changed anything.
I have found one solution that I disliked, but that does work, which is rather than doing a plt.show(), to instead do this inside my class:
buffer = BytesIO()
plt.savefig(buffer, format='png')
return buffer
And in the notebook I would do:
image = Image()
display(image)
def on_generate_button_clicked(b):
buffer = SoundingGraphs(infos)
buffer.seek(0)
image.value=buffer.read()
image.format='png'
generate_button.on_click(on_generate_button_clicked)
I don't quite like this approach because further down the line I would like to add interactivity to my plots, like show values of plot when hovered and things like that, thus I don't just want to show an image. So I'd like to know if it is indeed possible to plt.show() a plot created inside another file in a cell.
Using:
Python 3.6.9
jupyterlab==3.2.9
jupyterlab-pygments==0.1.2
jupyterlab-server==2.10.3
jupyterlab-widgets==1.1.0
ipykernel==5.5.6
ipyleaflet==0.14.0
ipympl==0.8.8
ipython==7.16.3
ipython-genutils==0.2.0
ipywidgets==7.7.0
matplotlib==3.3.4
Thanks!

Yes, it is possible after all!
%matplotlib widget needs to be used at the start of the notebook and since the class method will be called from another function (on a button.on_click event), it is possible to use the #out.capture() decorator above it so that the plt.show() gets displayed. It's also possible to make the figure a class attribute to be able to have more control.
So here's a bit of working code if someone would like to replicate:
Notebook
%matplotlib widget
from ipywidgets import Button, Output
from myfile import MyClass
out = Output()
example_button = Button(
description='Example',
disabled=False,
button_style='',
tooltip='Click me'
)
#out.capture()
def on_example_button_clicked(b):
example_button.disabled = True
myclass = MyClass()
myclass.create_plot()
out.clear_output(wait=True)
display(myclass.fig.canvas)
example_button.disabled = False
example_button.on_click(on_example_button_clicked)
display(example_button)
display(out)
myfile.py
import matplotlib.pyplot as plt
class MyClass():
def __init__(self):
plt.ioff() # otherwise it'll also show inside logs
plt.clf()
self.fig = plt.figure()
def create_plot(self):
plt.plot([1, 2, 3, 4])
plt.ylabel('some numbers')

Related

Can I save to disk a plot generated by pandas df.plot? [duplicate]

In ipython Notebook, first create a pandas Series object, then by calling the instance method .hist(), the browser displays the figure.
I am wondering how to save this figure to a file (I mean not by right click and save as, but the commands needed in the script).
Use the Figure.savefig() method, like so:
ax = s.hist() # s is an instance of Series
fig = ax.get_figure()
fig.savefig('/path/to/figure.pdf')
It doesn't have to end in pdf, there are many options. Check out the documentation.
Alternatively, you can use the pyplot interface and just call the savefig as a function to save the most recently created figure:
import matplotlib.pyplot as plt
s.hist()
plt.savefig('path/to/figure.pdf') # saves the current figure
Plots from multiple columns
Added from a comment toto_tico made on 2018-05-11
If you are getting this error AttributeError: 'numpy.ndarray' object has no attribute 'get_figure', then it is likely that you are plotting multiple columns.
In this case, ax will be an array of all the axes.
ax = s.hist(columns=['colA', 'colB'])
# try one of the following
fig = ax[0].get_figure()
fig = ax[0][0].get_figure()
fig.savefig('figure.pdf')
You can use ax.figure.savefig():
import pandas as pd
s = pd.Series([0, 1])
ax = s.plot.hist()
ax.figure.savefig('demo-file.pdf')
This has no practical benefit over ax.get_figure().savefig() as suggested in Philip Cloud's answer, so you can pick the option you find the most aesthetically pleasing. In fact, get_figure() simply returns self.figure:
# Source from snippet linked above
def get_figure(self):
"""Return the `.Figure` instance the artist belongs to."""
return self.figure
You can simply save your (e.g. histogram) plot like this:
df.plot.hist().get_figure().savefig('name')
Just wanted to add that the default resolution is 100dpi, which is fine for screen but won't work if you want to enlarge or print it. You can pass a 'dpi' parameter to get a high-resolution file:
ax = s.hist() # s is an instance of Series
ax.figure.savefig('/path/to/figure.png', dpi=300)

"Replot" a matplotlib inline plot in a IPython notebook

if I work on a matplotlib inline plot in an ipython notebook like this:
figure = plt.figure()
ax = figure.gca(projection="3d")
graph = np.empty([len(thetaYield),3])
for g, tY in zip(graph, thetaYield):
sample = HWtoPS(xiYield, rhoYield, tY)
g[...] = sample[:]
ax.plot(graph[:,0],graph[:,1], graph[:,2])
plt.show()
the plot is drawn inline in my notebook as it intended.
Now I want to add to add some data to this plot:
principalStress, vectors = eig(sigma)
ax.scatter(principalStress[0], principalStress[1], principalStress[2])
plt.show()
no error, but also no plot is drawn.
I expected to get an "updated" version of my plot with the additional data.
How can this be done?
A: This can be done at a cost of changed matplotlib Renderer
Currently, this cannot be done for the IPython "inline" graphs, however, if you opt to change a Renderer part of the matplotlib framework, to another one, the limitation of a singleton call of the .show() method does not hurt and you can ex post modify the object's content and it gets re-processed by the Renderer.
Simply:
add a directive ( IPython magic) %matplotlib qt
&
use additional matplotlib calls as you expect 'em to modify/update the figure object
( I love using this both during prototyping phases & for interactive 3D-viewing of complex data visualisations (which I heavily miss in notebook's "inline"s) )
BTW: do you have about any methodology, which would allow to store a matplotlib 3D-view plot, as a complete, state-full container, that can be sent to some other user for her/his "load" and UI-interactive review? Would be great to hear about any such working :o)

Differences between figure saved and figure displayed

I am creating a plot with matplotlib. Once I have the figure I am showing it inside a layout in my MainWindow, and the figure looks like this one:
Which looks good and everything is ok.
The problem is that, before showing it I am savin the figure with fig.savefig('EvolLine.png')
And the figure saved looks different. Then when I try to use this saved figure in a PDF file it does not look good.
Here it is the saved figure:
In addition, if I save the figure manually using the tool from the tool bar in the plot, then the saved figure looks good again.
I do not why if I save the figure "programatically" it looks bad, like compressed.
So how could I programatically save the figure and make it look like in the display??.
EDIT:
If that helps, this is the process that I am doing to save and plot:
fig = generateFigure(someData)
fig.savefig('EvolLine.png')
MyCanvas = FigureCanvas(fig)
navi_toolbar = NavigationToolbar(MyCanvas, self)
self.ui.verticalLayoutGraph4_3.addWidget(navi_toolbar)
self.ui.verticalLayoutGraph4_3.addWidget(MyCanvas)
#just to test
fig.savefig('EvolLine_AfterPlot.png')
Your problem is that when showing your figure in your program you are controlling the aspect ratio, meaning that the x-ticks do not overlap. However when saving the figure you are allowing matplotlib to automatically guess what it should be doing, and it's getting this wrong.
You can use fig.set_size_inches() to control the aspect ratio by setting an appropriate size, the code below demonstrates this as well as shows the different results.
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0,100,1000)
y = np.sin(x)
plt.plot(x,y)
plt.xticks(range(0,100,2))
fig = plt.gcf()
fig.set_size_inches(16,4)
plt.savefig('filename.png')
Original
Fixed

prevent plot from showing in jupyter notebook

How can I prevent a specific plot to be shown in Jupyter notebook? I have several plots in a notebook but I want a subset of them to be saved to a file and not shown on the notebook as this slows considerably.
A minimal working example for a Jupyter notebook is:
%matplotlib inline
from numpy.random import randn
from matplotlib.pyplot import plot, figure
a=randn(3)
b=randn(3)
for i in range(10):
fig=figure()
plot(b)
fname='s%03d.png'%i
fig.savefig(fname)
if(i%5==0):
figure()
plot(a)
As you can see I have two types of plots, a and b. I want a's to be plotted and shown and I don't want the b plots to be shown, I just want them them to be saved in a file. Hopefully this will speed things a bit and won't pollute my notebook with figures I don't need to see.
Thank you for your time
Perhaps just clear the axis, for example:
fig= plt.figure()
plt.plot(range(10))
fig.savefig("save_file_name.pdf")
plt.close()
will not plot the output in inline mode. I can't work out if is really clearing the data though.
I was able to prevent my figures from displaying by turning interactive mode off using the function
plt.ioff()
To prevent any output from a jupyter notebook cell you may start the cell with
%%capture
This might be usefull in cases all other methods shown here fail.
From IPython 6.0 on, there is another option to turn the inline output off (temporarily or persistently). This has been introduced in this pull request.
You would use the "agg" backend to not show any inline output.
%matplotlib agg
It seems though that if you had activated the inline backend first, this needs to be called twice to take effect.
%matplotlib agg
%matplotlib agg
Here is how it would look in action
I'm a beginner though,off the inline mode when you don't want to see the output in your notebook by:
%matplotlib auto
or:
%matplotlib
to use it back:
%matplotlib inline
more better solution would be to use:
plt.ioff()
which says inline mode off.
hope it helps.
On Jupyter 6.0, I use the following snippet to selectively not display the matplot lib figures.
import matplotlib as mpl
...
backend_ = mpl.get_backend()
mpl.use("Agg") # Prevent showing stuff
# Your code
mpl.use(backend_) # Reset backend
Building off #importanceofbeingernest's answer, one may call some function in a loop, and at each iteration, want to render a plot. However, between the each plot, you may want to render additional stuff.
Concretely:
Iterate a list of IDs
Call a function so a plot is rendered for each "ID"
Between each plot, render some markdown
# <cell begins>
def render(id):
fig, axes = plt.subplots(2, 1)
plt.suptitle(f'Metrics for {id}')
df.ColA.plot.bar(ax=axes[0])
df.ColB.plot.bar(ax=axes[1])
return fig
# <cell ends>
# -------------------------------------
# <cell begins>
%matplotlib agg
for id in df.ID.value_counts().index:
fig = render(id)
display(fig)
display(Markdown('---'))
# <cell ends>

How to have a function return a figure in python (using matplotlib)?

Assume that I have some data, and I want to create a plot of this data by passing it to a custom plotting function (myplot()). I am using the matplotlib's modules in myplot().
I would like myplot() to return the handle to a figure, and not plot display the plot when I call this function. Here is a sample code and output from iPython.
I have two questions regarding this:
Why do I still see a plot, even though I am assigning the output of
myplot() to f?
What do I need to supress this plot when I am assigning the output of myplot() to a variable?
Start ipython with
ipython notebook
rather than
ipython notebook --pylab=inline
If you do not want to start the whole notebook in non-inline-modus you can just use the following code:
%config InlineBackend.close_figures = False
def myplot(t,x):
fig = figure()
x = plot(t,x)
fig.savefig('plot.png') # This is just to show the figure is still generated
return fig
t = arange(0,6,0.01)
x = sin(t)
f = myplot(t,x)

Categories

Resources