refresh matplotlib in Jupyter when updating with ipywidget - python

I want to draw a line in a Jupyter notebook, which can be moved using an ipywidget slider. I also want to have the mouse coordinates displayed, for which I'm using %matplotlib notebook. Here is what I have so far :
%matplotlib notebook
from ipywidgets import interact
fig, ax = plt.subplots()
#interact(n=(-200, 0))
def show(n):
# fig.clear() #doesn't show anything
y = -n+x
ax.plot(x, y)
plt.show()
When moving the line using the slider, the plot doesn't refresh, all previous positions of the line
remain visible:
I tried to refresh using fig.clear(), but then noting shows.
How can I solve this?

I have an extensive answer about this here: Matplotlib figure is not updating with ipywidgets slider
but the short of my recommendations are:
use ipympl %matplotlib ipympl instead of notebook as this will play nicer with ipywidgets
Use mpl-interactions to handle making plots controlled by sliders.
It will do the optimal thing of using set_data for you rather than clearing and replotting the lines.
It also interprets the shorthand for numbers in a way that (I think) makes more sense when making plots (e.g. using linspace instead of arange) see https://mpl-interactions.readthedocs.io/en/stable/comparison.html for more details.
So for your example I recommend doing:
install libraries
pip install ipympl mpl-interactions
%matplotlib ipympl
from ipywidgets import interact
import matplotlib.pyplot as plt
from mpl_interactions import ipyplot as iplt
x = np.linspace(0,100)
fig, ax = plt.subplots()
def y(x, n):
return x - n
ctrls = iplt.plot(x, y, n=(-200,0))
it got a bit longer because I added the imports you left out of your question and also defined x.
Which gives you this:
That said if you don't want to use those I think what you want is ax.cla() I think when you do fig.clear you are also removing the axes which is why nothing shows up.
%matplotlib notebook
from ipywidgets import interact
fig, ax = plt.subplots()
#interact(n=(-200, 0))
def show(n):
y = -n+x
ax.cla()
ax.plot(x, y)
plt.show()

Related

Interactive graphical visualization with matplotlib

I want to show specific data (not the columns I used in plotting my scatter plot but other columns in the dataframe) whenever I hover my mouse on each data point using matpotlib. I don't want to use plotly because I need to implement another part of matplotlib.
I wrote the following script but it is not implementing the hovering part when I run it in my jupyter notebook.
import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd
%matplotlib widget
# Load sample data
diamonds = sns.load_dataset('diamonds')
# Create a scatter plot with hue and use matplotlib to display the specific column values when hovering over a datapoint
sns.scatterplot(x="carat", y="price", hue="cut", data=diamonds)
def hover(event):
# Get the current mouse position
ax = event.inaxes
x, y = event.xdata, event.ydata
# Find the nearest datapoint
distances = ((diamonds['carat'] - x)**2 + (diamonds['price'] - y)**2)
idx = distances.idxmin()
# Display the specific column values in a pop-up window
text = f"Cut: {diamonds.loc[idx, 'cut']}\n" \
f"Clarity: {diamonds.loc[idx, 'clarity']}\n" \
f"Color: {diamonds.loc[idx, 'color']}"
plt.gcf().text(x, y, text, ha='left', va='bottom', fontsize=10, backgroundcolor='white', alpha=0.7)
# Connect the hover function to the figure
fig = plt.gcf()
fig.canvas.mpl_connect("motion_notify_event", hover)
# Show the plot
plt.show()
This is easier with mplcursors. There's a similar example of extracting the data and labels for the annotation from a dataframe to plug into. Implemented with your example it is:
%matplotlib ipympl
# based on https://mplcursors.readthedocs.io/en/stable/examples/dataframe.html
import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd
from matplotlib.patheffects import withSimplePatchShadow
import mplcursors
df = sns.load_dataset('diamonds')[:10]
sbsp = sns.scatterplot(x="carat", y="price", hue="cut", data=df)
def show_hover_panel(get_text_func=None):
cursor = mplcursors.cursor(
hover=2, # Transient
annotation_kwargs=dict(
bbox=dict(
boxstyle="square,pad=0.5",
facecolor="wheat",
edgecolor="#ddd",
linewidth=0.5,
path_effects=[withSimplePatchShadow(offset=(1.5, -1.5))],
),
linespacing=1.5,
arrowprops=None,
),
highlight=True,
highlight_kwargs=dict(linewidth=2),
)
if get_text_func:
cursor.connect(
event="add",
func=lambda sel: sel.annotation.set_text(get_text_func(sel.index)),
)
return cursor
def on_add(index):
item = df.iloc[index]
parts = [
f"Cut: {item.cut}", # f"Cut: {diamonds.loc[idx, 'cut']}\n"
f"Clarity: {item.clarity}", # f"Clarity: {diamonds.loc[idx, 'clarity']}\n"
f"Color: {item.color}", #f"Color: {diamonds.loc[idx, 'color']}"
]
return "\n".join(parts)
sbsp.figure.canvas.header_visible = False # Hide the Figure name at the top of the figure;based on https://matplotlib.org/ipympl/examples/full-example.html
show_hover_panel(on_add)
plt.show();
That's for running it in JupyterLab where ipympl has been installed.
For running it in Jupyter Notebook at present, change the first line to %matplotlib notebook.
I saw it be smoother in JupyterLab.
I'll point to ways to try the code in both interfaces without installing anything on your system below.
(Your approach may be able to work with more incorporation of things here. However, there's already a very similar answer with mplcursors)
(The places and steps to run the code outlined below were worked out primarily for the Matplotlib option here yesterday. Noting that here as there were some quirks to the ipympl offering launching and I worry that I may miss updating each place if something changes.)
Try it in JupyterLab in conjunction with ipympl without touching your system
This will allow you to try the code in JupyterLab without installing anything on your own system.
Go to here. Sadly the link currently goes to a dead end and doesn't seem to build a new image right now. Fortunately, right now this offering works for launching.
When that session opens, run in a notebook %pip install mplcursors seaborn. Let that installation command run to install both mplcursors and seaborn and then restart the kernel.
Make a new cell and paste in the code from above and run it.
Try it Jupyter Notebook classic interface without touching your system
This will allow you to try the code in the present classic Jupyter Notebook interface (soon-to-be more-often-referenced as 'the document-centric interface' as the underlying machinery for Jupyter Notebook 7 will use JupyterLab components) without installing anything on your own system.
Go here and press 'launch binder'. A temporary, remote session will spin up served via MyBinder.
Make a new cell and simply add %matplotlib notebook at the top of the cell. Then add the code above to the cell, leaving off the first line of the suggested code, so that you effectively substitute %matplotlib notebook in place of %matplotlib ipympl.
Run the cell.

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 do you update inline images in Ipython?

Edit: My question is not in regards to an "animation" per se. My question here, is simply about how to continuously show, a new inline image, in a for loop, within an Ipython notebook.
In essence, I would like to show an updated image, at the same location, inline, and have it update within the loop to show. So my code currently looks something like this:
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from IPython import display
%matplotlib inline
fig, ax = plt.subplots(nrows = 1, ncols = 1, figsize=(10, 10))
for ii in xrange(10):
im = np.random.randn(100,100)
ax.cla()
ax.imshow(im, interpolation='None')
ax.set_title(ii)
plt.show()
The problem is that this currently just..., well, shows the first image, and then it never changes.
Instead, I would like it to simply show the updated image at each iteration, inline, at the same place. How do I do that? Thanks.
I am not sure that you can do this without animation. Notebooks capture the output of matplotlib to include in the cell once the plotting is over. The animation framework is rather generic and covers anything that is not a static image. matplotlib.animation.FuncAnimation would probably do what you want.
I adapted your code as follows:
%matplotlib notebook
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation
f = plt.figure()
ax = f.gca()
im = np.random.randn(100,100)
image = plt.imshow(im, interpolation='None', animated=True)
def function_for_animation(frame_index):
im = np.random.randn(100,100)
image.set_data(im)
ax.set_title(str(frame_index))
return image,
ani = matplotlib.animation.FuncAnimation(f, function_for_animation, interval=200, frames=10, blit=True)
Note: You must restart the notebook for the %matplotlib notebook to take effect and use a backend that supports animation.
EDIT: There is normally a way that is closer to your original question but it errors on my computer. In the example animation_demo there is a plain "for loop" with a plt.pause(0.5) statement that should also work.
You can call figure.canvas.draw() each time you append something new to the figure. This will refresh the plot (from here). Try:
import numpy as np
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
from IPython import display
from time import sleep
fig = plt.figure()
ax = fig.gca()
fig.show()
for ii in range(10):
im = np.random.randn(100, 100)
plt.imshow(im, interpolation='None')
ax.set_title(ii)
fig.canvas.draw()
sleep(0.1)
I could not test this in an IPython Notebook, however.

Jupyter, Interactive Matplotlib: Hide the toolbar of the interactive view

I am starting using the interactive plotting from Matplotlib:
%matplotlib notebook
import matplotlib.pyplot as plt
fig, axes = plt.subplots(1, figsize=(8, 3))
plt.plot([i for i in range (10)],np.random.randint(10, size=10))
plt.show()
Anyone knows if there is a way to hide the toolbars of the interactive mode?
I disabled the interactive mode buttons and toolbar with some python generated css.
Run the following in one of the notebook cells:
%%html
<style>
.output_wrapper button.btn.btn-default,
.output_wrapper .ui-dialog-titlebar {
display: none;
}
</style>
Unfortunately there's no good css selectors on the buttons, so I've tried to use as specific selector as possible, though this may end up disabling other buttons that you might generate in the output cell.
Indeed, this approach affects all output cells in the notebook.
Use the magic %matplotlib ipympl with canvas. toolbar_visible=False. To prevent double-appearence of figure, use plt. ioff() while instantiate figure:
import matplotlib.pyplot as plt
plt.ioff()
fig, ax = plt.subplots()
plt.ion()
fig.canvas.toolbar_visible = False
display(fig.canvas)
It's a little bit doubly, but so you know how to play with plt
Edit: Haven't mind you on jupyter. This works on jupyterlab

Categories

Resources