I'm trying to make an interactive plot in the jupyter notebook but i don't know exactly how to implement it. Having a dataframe i run a simple regression that is then plotted to see the distribution. I'd like to be able to hover one of the points and get data associated with this point. How can i do that? Right now i can only produce a static plot
import pandas as pd
from sklearn import linear_model
%matplotlib inline
import matplotlib
import matplotlib.pyplot as plt
net = pd.read_csv("network_ver_64.csv")
net = net[net.AWDT12 > 0]
x = net.LOAD_DAILY.values
y = net.AWDT12.values
x_lenght = int(x.shape[0])
y_lenght = int(y.shape[0])
x = x.reshape(x_lenght, 1)
y = y.reshape(y_lenght,1)
regr = linear_model.LinearRegression()
regr.fit(x, y)
plt.scatter(x, y, color='black')
plt.plot(x, regr.predict(x), color='blue', linewidth=1)
plt.xticks(())
plt.yticks(())
plt.show()
First of all it's clear that the %matplotlib inline backend does not allow for interaction, as it is inline (in the sense that the plots are images).
However even in the notebook you can get interaction using the %matplotlib notebook backend. A basic hover took is already implemented: Moving the mouse in the canvas shows the current mouse position in data coordinates in the lower right corner.
Of course you can obtain more sophisticated functionality by writing some custom code. E.g. we can modify the picking example a little bit as follows:
import matplotlib.pyplot as plt
%matplotlib notebook
import numpy as np
fig = plt.figure()
ax = fig.add_subplot(111)
ax.set_title('click on points')
line, = ax.plot(np.random.rand(100), 'o', picker=5) # 5 points tolerance
text = ax.text(0,0,"")
def onpick(event):
thisline = event.artist
xdata = thisline.get_xdata()
ydata = thisline.get_ydata()
ind = event.ind
text.set_position((xdata[ind], ydata[ind]))
text.set_text(zip(xdata[ind], ydata[ind]))
fig.canvas.mpl_connect('pick_event', onpick)
plt.show()
This now shows the data coordinates of the point the mouse has clicked.
You're pretty free to adapt this to any case you like and make it more pretty using the standard matplotlib tools.
Related
I would like to use the interact function to create sliders where the user would input vector coordinates and plot these coordinates. The problem is that changing the input values doesn't make a new graph, would it be possible to make it work?
I'm working with jupyter notebook. My code
import panel as pn
import numpy as np
import matplotlib.pyplot as plt
from panel.interact import interact, interactive, fixed, interact_manual
pn.extension()
def f(u1,u2,v1,v2):
plt.clf()
vetores = np.array([[0,0,u1,u2], [u1,u2,v1,v2]])
X, Y, U, V = zip(*vetores)
plt.figure()
ax = plt.gca()
ax.quiver(X, Y, U, V, angles='xy', scale_units='xy', scale=1, color = ['r','g','b'])
ax.set_xlim([min(-1,u1-1, v1-1), max(u1+v1+1, v1+1)])
ax.set_ylim([min(-1,u2-1, v2-1), max(u2+v2+1, v2+1)])
plt.show()
interact(f, u1=2, u2=0, v1=2, v2=3)
You can use ipywidgets interactive plot. Matplotlib has quiver:
from ipywidgets import interactive
import matplotlib.pyplot as plt
import numpy as np
def f(u1,u2,v1,v2):
plt.figure()
vetores = np.array([[0,0,u1,u2], [u1,u2,v1,v2]])
X, Y, U, V = zip(*vetores)
plt.quiver(X, Y, U, V, angles='xy', scale_units='xy', scale=1, color = ['r','g','b'])
ax = plt.gca()
ax.set_xlim([min(-1,u1-1, v1-1), max(u1+v1+1, v1+1)])
ax.set_ylim([min(-1,u2-1, v2-1), max(u2+v2+1, v2+1)])
plt.show()
interactive_plot = interactive(f, u1=2, u2=0, v1=2, v2=3)
interactive_plot
Your code was adapted into my answer here.
It works in notebooks in Jupyter sessions launched from the link at the bottom there.
It also works in notebooks in Jupyter sessions launched via the holoviz panel MyBinder launch here.
Using Panel
Or using Panel in combination with Matplotlib based on upper part of here and returning a proper Matplotlib figure based on here:
import panel as pn
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
pn.extension()
title = '## Quiver Panel controller'
def f(u1,u2,v1,v2):
pl = plt.figure()
vetores = np.array([[0,0,u1,u2], [u1,u2,v1,v2]])
X, Y, U, V = zip(*vetores)
pl.add_subplot(111).quiver(X, Y, U, V, angles='xy', scale_units='xy', scale=1, color = ['r','g','b'])
ax = plt.gca()
ax.set_xlim([min(-1,u1-1, v1-1), max(u1+v1+1, v1+1)])
ax.set_ylim([min(-1,u2-1, v2-1), max(u2+v2+1, v2+1)])
plt.close(pl)
return pl
interact = pn.interact(f, u1=2, u2=0, v1=2, v2=3)
pn.Row(
pn.Column(title, interact[0], sizing_mode="fixed", width=300),
interact[1]
)
#interact # use this if don't want side-by-side
The layout is nicer in this example, but ipywidgets, which was used in the above option, can be used to arrange the controls side-by-side as well, see bottom of post here for an example.
I would imagine there's a way to make it simpler and instead use return plt.gcf() based on this and associated note in FAQ:
"A: Matplotlib pyplot users often use %matplotlib inline, which shows plots as a "side effect" in a Jupyter notebook, rather than using the cell's return value like Python literals and other objects do. Panel callbacks like those accepted for pn.interact() work on the return value of the callback, which is then provided as the return value of the cell, and thus directly display without any requirements for side effects. So, if you create a Matplotlib plot that would magically appear via %matplotlib inline, for Panel you need to ensure that the callback actually returns a value, rather than counting on this side effect. Specifically, if you have a callback with some Matplotlib plotting calls, you can add return plt.gcf() to your callback to make the current figure be returned, which will ensure that your plot is displayed properly."
However, I wasn't able to easily find the combination where it worked and I didn't see two plots. In fact, just trying the example code there results in two plots as well, only the upper one updating via the slider. The approach earlier in that thread produces no such artifact.
For an assignment, I have to recreate the following plot (including all labels and ticks):
This is what I have tried so far with my code
import numpy as np
import matplotlib.pyplot as plt
nmax=101 # choose a high number to "smooth out" lines in plots
x = np.linspace(0,20,nmax) # create an array x
y_br = np.sin(3*x) # y for the bottom right subplot
fig = plt.figure()
ax4 = plt.subplot(224, projection = 'polar')
ax4.plot(x, y_br, 'tab:blue')
But if you were to run this yourself, this does not replicate the plot. What function could be used here and how can tick marks be changed in polar plots? Thanks in advance?
I am trying to automatically update a scatter plot.
The source of my X and Y values is external, and the data is pushed automatically into my code in a non-predicted time intervals (rounds).
I have only managed to plot all the data when the whole process ended, whereas I am trying to constantly add and plot data into my canvas.
What I DO get (at the end of the whole run) is this:
Whereas, what I am after is this:
A simplified version of my code:
import matplotlib.pyplot as plt
def read_data():
#This function gets the values of xAxis and yAxis
xAxis = [some values] #these valuers change in each run
yAxis = [other values] #these valuers change in each run
plt.scatter(xAxis,yAxis, label = 'myPlot', color = 'k', s=50)
plt.xlabel('x')
plt.ylabel('y')
plt.show()
There are several ways to animate a matplotlib plot. In the following let's look at two minimal examples using a scatter plot.
(a) use interactive mode plt.ion()
For an animation to take place we need an event loop. One way of getting the event loop is to use plt.ion() ("interactive on"). One then needs to first draw the figure and can then update the plot in a loop. Inside the loop, we need to draw the canvas and introduce a little pause for the window to process other events (like the mouse interactions etc.). Without this pause the window would freeze. Finally we call plt.waitforbuttonpress() to let the window stay open even after the animation has finished.
import matplotlib.pyplot as plt
import numpy as np
plt.ion()
fig, ax = plt.subplots()
x, y = [],[]
sc = ax.scatter(x,y)
plt.xlim(0,10)
plt.ylim(0,10)
plt.draw()
for i in range(1000):
x.append(np.random.rand(1)*10)
y.append(np.random.rand(1)*10)
sc.set_offsets(np.c_[x,y])
fig.canvas.draw_idle()
plt.pause(0.1)
plt.waitforbuttonpress()
(b) using FuncAnimation
Much of the above can be automated using matplotlib.animation.FuncAnimation. The FuncAnimation will take care of the loop and the redrawing and will constantly call a function (in this case animate()) after a given time interval. The animation will only start once plt.show() is called, thereby automatically running in the plot window's event loop.
import matplotlib.pyplot as plt
import matplotlib.animation
import numpy as np
fig, ax = plt.subplots()
x, y = [],[]
sc = ax.scatter(x,y)
plt.xlim(0,10)
plt.ylim(0,10)
def animate(i):
x.append(np.random.rand(1)*10)
y.append(np.random.rand(1)*10)
sc.set_offsets(np.c_[x,y])
ani = matplotlib.animation.FuncAnimation(fig, animate,
frames=2, interval=100, repeat=True)
plt.show()
From what I understand, you want to update interactively your plot. If so, you can use plot instead of scatter plot and update the data of your plot like this.
import numpy
import matplotlib.pyplot as plt
fig = plt.figure()
axe = fig.add_subplot(111)
X,Y = [],[]
sp, = axe.plot([],[],label='toto',ms=10,color='k',marker='o',ls='')
fig.show()
for iter in range(5):
X.append(numpy.random.rand())
Y.append(numpy.random.rand())
sp.set_data(X,Y)
axe.set_xlim(min(X),max(X))
axe.set_ylim(min(Y),max(Y))
raw_input('...')
fig.canvas.draw()
If this is the behaviour your are looking for, you just need to create a function appending the data of sp, and get in that function the new points you want to plot (either with I/O management or whatever the communication process you're using).
I hope it helps.
I am trying to automatically update a scatter plot.
The source of my X and Y values is external, and the data is pushed automatically into my code in a non-predicted time intervals (rounds).
I have only managed to plot all the data when the whole process ended, whereas I am trying to constantly add and plot data into my canvas.
What I DO get (at the end of the whole run) is this:
Whereas, what I am after is this:
A simplified version of my code:
import matplotlib.pyplot as plt
def read_data():
#This function gets the values of xAxis and yAxis
xAxis = [some values] #these valuers change in each run
yAxis = [other values] #these valuers change in each run
plt.scatter(xAxis,yAxis, label = 'myPlot', color = 'k', s=50)
plt.xlabel('x')
plt.ylabel('y')
plt.show()
There are several ways to animate a matplotlib plot. In the following let's look at two minimal examples using a scatter plot.
(a) use interactive mode plt.ion()
For an animation to take place we need an event loop. One way of getting the event loop is to use plt.ion() ("interactive on"). One then needs to first draw the figure and can then update the plot in a loop. Inside the loop, we need to draw the canvas and introduce a little pause for the window to process other events (like the mouse interactions etc.). Without this pause the window would freeze. Finally we call plt.waitforbuttonpress() to let the window stay open even after the animation has finished.
import matplotlib.pyplot as plt
import numpy as np
plt.ion()
fig, ax = plt.subplots()
x, y = [],[]
sc = ax.scatter(x,y)
plt.xlim(0,10)
plt.ylim(0,10)
plt.draw()
for i in range(1000):
x.append(np.random.rand(1)*10)
y.append(np.random.rand(1)*10)
sc.set_offsets(np.c_[x,y])
fig.canvas.draw_idle()
plt.pause(0.1)
plt.waitforbuttonpress()
(b) using FuncAnimation
Much of the above can be automated using matplotlib.animation.FuncAnimation. The FuncAnimation will take care of the loop and the redrawing and will constantly call a function (in this case animate()) after a given time interval. The animation will only start once plt.show() is called, thereby automatically running in the plot window's event loop.
import matplotlib.pyplot as plt
import matplotlib.animation
import numpy as np
fig, ax = plt.subplots()
x, y = [],[]
sc = ax.scatter(x,y)
plt.xlim(0,10)
plt.ylim(0,10)
def animate(i):
x.append(np.random.rand(1)*10)
y.append(np.random.rand(1)*10)
sc.set_offsets(np.c_[x,y])
ani = matplotlib.animation.FuncAnimation(fig, animate,
frames=2, interval=100, repeat=True)
plt.show()
From what I understand, you want to update interactively your plot. If so, you can use plot instead of scatter plot and update the data of your plot like this.
import numpy
import matplotlib.pyplot as plt
fig = plt.figure()
axe = fig.add_subplot(111)
X,Y = [],[]
sp, = axe.plot([],[],label='toto',ms=10,color='k',marker='o',ls='')
fig.show()
for iter in range(5):
X.append(numpy.random.rand())
Y.append(numpy.random.rand())
sp.set_data(X,Y)
axe.set_xlim(min(X),max(X))
axe.set_ylim(min(Y),max(Y))
raw_input('...')
fig.canvas.draw()
If this is the behaviour your are looking for, you just need to create a function appending the data of sp, and get in that function the new points you want to plot (either with I/O management or whatever the communication process you're using).
I hope it helps.
In the answers to how to dynamically update a plot in a loop in ipython notebook (within one cell), an example is given of how to dynamically update a plot inside a Jupyter notebook within a Python loop. However, this works by destroying and re-creating the plot on every iteration, and a comment in one of the threads notes that this situation can be improved by using the new-ish %matplotlib nbagg magic, which provides an interactive figure embedded in the notebook, rather than a static image.
However, this wonderful new nbagg feature seems to be completely undocumented as far as I can tell, and I'm unable to find an example of how to use it to dynamically update a plot. Thus my question is, how does one efficiently update an existing plot in a Jupyter/Python notebook, using the nbagg backend? Since dynamically updating plots in matplotlib is a tricky issue in general, a simple working example would be an enormous help. A pointer to any documentation on the topic would also be extremely helpful.
To be clear what I'm asking for: what I want to do is to run some simulation code for a few iterations, then draw a plot of its current state, then run it for a few more iterations, then update the plot to reflect the current state, and so on. So the idea is to draw a plot and then, without any interaction from the user, update the data in the plot without destroying and re-creating the whole thing.
Here is some slightly modified code from the answer to the linked question above, which achieves this by re-drawing the whole figure every time. I want to achieve the same result, but more efficiently using nbagg.
%matplotlib inline
import time
import pylab as pl
from IPython import display
for i in range(10):
pl.clf()
pl.plot(pl.randn(100))
display.display(pl.gcf())
display.clear_output(wait=True)
time.sleep(1.0)
Here is an example that updates a plot in a loop. It updates the data in the figure and does not redraw the whole figure every time. It does block execution, though if you're interested in running a finite set of simulations and saving the results somewhere, it may not be a problem for you.
%matplotlib notebook
import numpy as np
import matplotlib.pyplot as plt
import time
def pltsin(ax, colors=['b']):
x = np.linspace(0,1,100)
if ax.lines:
for line in ax.lines:
line.set_xdata(x)
y = np.random.random(size=(100,1))
line.set_ydata(y)
else:
for color in colors:
y = np.random.random(size=(100,1))
ax.plot(x, y, color)
fig.canvas.draw()
fig,ax = plt.subplots(1,1)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_xlim(0,1)
ax.set_ylim(0,1)
for f in range(5):
pltsin(ax, ['b', 'r'])
time.sleep(1)
I put this up on nbviewer here.
There is an IPython Widget version of nbagg that is currently a work in progress at the Matplotlib repository. When that is available, that will probably be the best way to use nbagg.
EDIT: updated to show multiple plots
I'm using jupyter-lab and this works for me (adapt it to your case):
from IPython.display import clear_output
from matplotlib import pyplot as plt
import numpy as np
import collections
%matplotlib inline
def live_plot(data_dict, figsize=(7,5), title=''):
clear_output(wait=True)
plt.figure(figsize=figsize)
for label,data in data_dict.items():
plt.plot(data, label=label)
plt.title(title)
plt.grid(True)
plt.xlabel('epoch')
plt.legend(loc='center left') # the plot evolves to the right
plt.show();
Then in a loop you populate a dictionary and you pass it to live_plot():
data = collections.defaultdict(list)
for i in range(100):
data['foo'].append(np.random.random())
data['bar'].append(np.random.random())
data['baz'].append(np.random.random())
live_plot(data)
make sure you have a few cells below the plot, otherwise the view snaps in place each time the plot is redrawn.
If you don't want to clear all outputs, you can use display_id=True to obtain a handle and use .update() on it:
import numpy as np
import matplotlib.pyplot as plt
import time
from IPython import display
def pltsin(ax, *,hdisplay, colors=['b']):
x = np.linspace(0,1,100)
if ax.lines:
for line in ax.lines:
line.set_xdata(x)
y = np.random.random(size=(100,1))
line.set_ydata(y)
else:
for color in colors:
y = np.random.random(size=(100,1))
ax.plot(x, y, color)
hdisplay.update(fig)
fig,ax = plt.subplots(1,1)
hdisplay = display.display("", display_id=True)
ax.set_xlabel('X')
ax.set_ylabel('Y')
ax.set_xlim(0,1)
ax.set_ylim(0,1)
for f in range(5):
pltsin(ax, colors=['b', 'r'], hdisplay=hdisplay)
time.sleep(1)
plt.close(fig)
(adapted from #pneumatics)
I've adapted #Ziofil answer and modified it to accept x,y as list and output a scatter plot plus a linear trend on the same plot.
from IPython.display import clear_output
from matplotlib import pyplot as plt
%matplotlib inline
def live_plot(x, y, figsize=(7,5), title=''):
clear_output(wait=True)
plt.figure(figsize=figsize)
plt.xlim(0, training_steps)
plt.ylim(0, 100)
x= [float(i) for i in x]
y= [float(i) for i in y]
if len(x) > 1:
plt.scatter(x,y, label='axis y', color='k')
m, b = np.polyfit(x, y, 1)
plt.plot(x, [x * m for x in x] + b)
plt.title(title)
plt.grid(True)
plt.xlabel('axis x')
plt.ylabel('axis y')
plt.show();
you just need to call live_plot(x, y) inside a loop.
here's how it looks:
The canvas.draw method of the figure dynamically updates its graphs, for the current figure:
from matplotlib import pyplot as plt
plt.gcf().canvas.draw()