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

Plot disappears after executing cell second time #60

Open
tiagopereira opened this issue Jul 18, 2018 · 13 comments
Open

Plot disappears after executing cell second time #60

tiagopereira opened this issue Jul 18, 2018 · 13 comments

Comments

@tiagopereira
Copy link

If I have a plot in a notebook cell, and execute it twice, the plot output disappears. This is not what is expected. For example, using this code in one jupyter notebook cell:

%matplotlib widget
import matplotlib.pyplot as plt

and this in another cell:

x = [0, 1, 2, 3]
y = [0, 1, 2, 3]

plt.plot(x, y)

The first time I run this, everything plots fine. But when I execute the second cell twice (or any following calls), the plot disappears. The expected behaviour is that it re-plots (using a different colour). If I then re-run the first cell (with %matplotlib widget), and then the second cell again, it works (but this basically just resets the plot).

If I include a plot command in the first cell, then I can re-run the second cell as many times as I want and it works as expected.

This does not happen if I use %matplotlib inline. I've tested in both jupyter lab and jupyter notebook. My versions are the following:

matplotlib                2.2.2                    py36_1    conda-forge
ipympl                    0.2.1                    py36_0    conda-forge
@jupyter-widgets/jupyterlab-manager
        @jupyter-widgets/jupyterlab-manager v0.35.0  enabled  OK
@thomasaarholt
Copy link
Contributor

thomasaarholt commented Jul 18, 2018

This is because plt.plot() creates and shows a figure if a figure does not exist. The plot figure has already been created on the second call, so it doesn't show the figure.

I'm not sure what the "correct" behaviour should be. I feel that notebook users can be tricked by the plot call producing a figure, when they shouldn't be expecting plt.plot() to always produce a figure - what should be the behaviour of calling a second plt.plot() in a second cell? Should the plot appear or not?

The solution is to explicitly call fig = plt.figure() in a first cell, and then any plot calls in a separate cell (or multiple separate cells).

@SylvainCorlay
Copy link
Member

Indeed this appears to be the expected behavior.

I think that a better way to use this is to turn off interacrive mode with plt.ioff(). Then, call plt.show() to show the plots.

@thomasaarholt
Copy link
Contributor

Agreed. That is how "normal" matplotlib users do it :)

@tiagopereira
Copy link
Author

I don't see how this is the expected behaviour since it differs from the behaviour in every other backend. If I use inline, a new plot/figure is created every time I re-run the cell. If I use qt, the original figure is updated every time I re-run. With widget, the original figure disappears and nothing gets plotted in the second and subsequent runs. It is not logical that a call to plot will erase an existing figure and plot nothing new.

I see that if I call plt.figure() in the first cell, the behaviour is indeed what is expected. But why does the widget backend require this additional step when no other backend does?

This takes me to another point of using the widget backend. I've taught a course using jupyter and the widget backend, and the top complaint from students was figures updating in a different cell / forgetting to call plt.figure(). I've used matplotlib for a long time and understand the architecture of figure/axes/plot. However, for notebook usage this should be rethought. Many jupyter uses make use of inline, and this will create a new figure in the output of every cell that calls plotting functions. This makes sense coming from Mathematica. With widget, however, it creates a figure in the output of the first cell that has plotting calls, but absent a new call for figure(), it will keep updating the output of the first cell, when running in later cells. This is confusing for users. It would make more sense to by default launch a new figure after every cell, unless the axes of the original figure were explicitly used in subsequent calls.

@thomasaarholt
Copy link
Contributor

thomasaarholt commented Jul 19, 2018

The reason the behaviour is different from qt, is that in qt the plot window does not appear in the output of the cell. The output is reset each time the cell is called. This isn't the case for qt, since it appears in a separate window.

For the case of inline, the figure is closed after each plot call. What you see with the inline figure is a static png. Hence a figure does not exist when the next plot is called, and the figure is created.

I guess what you would want/expect, is for the figure to appear in some place on the screen where it will not be hidden from view by further calls to that cell.

@hadim
Copy link

hadim commented Aug 3, 2018

I "fix" this behavior by always calling plt.figure() before plt.plot() now.

@bshinnebarger
Copy link

I've had the same issue switching from jupyter to jupyter lab and going from %matplotlib inline or notebook to %matplotlib widget. For me, I just do:

plt.close('chart_name')
plt.figure('chart_name')
plt.plot([1,2,3])

Then it redraws it for me. The solution by thomasaarholt worked for me as well, but I prefer to not have all my plots in a separate cells

@TheIdealis
Copy link

Since the temporary solution of @bshinnebarger is still somewhat cumbersome, I tried to simplify the matter even further. You can make a template notebook with the help of this extension and add the following line to your template using ipympl:

%matplotlib ipympl
def figure(name, *args, **kwargs):
    plt.close(name)
    plt.figure(name, *args, **kwargs)
import pylab as plt

Afterwards, in any cell that contains a plot, you can simply use

figure('name')

without the namespace plt, to get the desired behavoir.

@tacaswell
Copy link
Member

@tiagopereira I would argue that this is an issue with the display model of jupyter notebooks rather than Matplotlib. @thomasaarholt got the mechanism exactly right.

Rather than manually closing the figures manually etc, I suggest

fig, ax = plt.subplots()
ax.plot(...)

instead. This create a new figure every run and by using the OO interface you can be sure what plot you will be putting things into. This future proofs you against wanting to split plotting across more than one cell, wanting to add sub-plots, moving to jupyterlab, and pulling your plotting out into a function.

@jfemiani
Copy link

@thomasaarholt @tacaswell this does not close the old (hidden) figure and can create a huge number of figures that stay open. Using the same name indicates that I want to recreate a figure, not make a new one. I would expect that if a figure's output widget is removed, then the figure is closed automatically. Like closing the window. I do not know the full mechanism of how widgets are rendered, but one would hope there is some event/hook to respond when a widget is removed from the output cell. My preference would be that re-evaluating a cell should cause the display-widget to close the figure.

@tacaswell
Copy link
Member

@jfemiani If we are leaking invisible figures I agree that is a bug!

@01baftb
Copy link

01baftb commented Nov 19, 2021

I am using Seaborn to plot data with %matplotlib widget so I can have the graphs appear interactively. I expected a new plot to appear for ever new cell in which I plot with Seaborn. However, the result was not what I expected. The plot for subsequent cells appeared to combined on top of plot from the first cell that called the Seaborn's ploting function. It took me forever to figure why my plots weren't appearing. The behavior was unexpected. When I switch to %matplotlib inline, each plot appeared below the respective cell in which Seaborn's plotting function was called.

ipympl 0.8.2
jupyterlab 3.2.4
matplotlib 3.5.0
seaborn 0.11.2

Expected behavior should be similar to %matplotlib inline https://i.imgur.com/YS9GsUA.png
Unexpected behavior: https://i.imgur.com/ki1lMjw.png

@tacaswell
Copy link
Member

@01baftb Please see long discussion in #171 .

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants