Why does matplotlib's Slider only allow a range of 0-7? - python

I am trying to plot the values of a bitwise circular shift on 1 byte. I'd like to have a slider let me change the original input value. I'm using the slider example on the matplotlib site for reference, but for some reason even though I pass in 0-255 as my slider range when I run my script the range is always 0-7. I'm guessing that somehow the slider is getting locked to my maximum number of x values, but I don't see how. How do I get the slider to let me pick the full 0-255 range?
Also, despite the min/max I've given the slider it inserts some padding for going below 0 at the front, and randomly draws a verticle line in the middle of my slider. How do I get rid of it? (also what is it for? The purpose isn't obvious to me)
Picture of slider only going up to 7:
Code:
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
from numpy import uint8
from numpy import uint16
from numpy import uint32
from numpy import uint64
def sizeof(x):
return [uint8, uint16, uint32, uint64].index(x) + 1
def rot(x, i):
return type(x)((x >> i) | (x << (sizeof(type(x))*8 - i)))
def plotShifts(x):
origType = type(x)
maxval = type(x)(-1)
numrots = sizeof(type(x)) * 8
vals = [rot(x, i) for i in range(numrots)]
print vals
l, = plt.plot(range(numrots), vals, 'ro')
axcolor = 'lightgoldenrodyellow'
inputax = plt.axes([0.15, 0.05, 0.65, 0.03], axisbg=axcolor)
inputsl = Slider(inputax, 'Input', 0, maxval, valinit=0, valfmt="%d")
def update(x):
vals = [rot(origType(x), i) for i in range(numrots)]
l.set_ydata(vals)
plt.draw()
inputsl.on_changed(update)
plt.axis([-0.5, numrots-1 + 0.5, -2, maxval + 2])
plotShifts(uint8(1))
plt.show()

The problem is in the last line plt.axis([-0.5, numrots-1 + 0.5, -2, maxval + 2]) which is acting on the axes that holds the slider, not on the axis with the data.
I would recommend using the OO interface to matplotlib rather than the pyplot interface for anything programmatic. The pyplot interface is good for interactive stuff, but it has a good deal of hidden state.
You also need to return a reference to the slider object due to the way call backs work.
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
from numpy import uint8
from numpy import uint16
from numpy import uint32
from numpy import uint64
def sizeof(x):
return 2 ** [uint8, uint16, uint32, uint64].index(x)
def rot(x, i):
return type(x)((x >> i) | (x << (sizeof(type(x))*8 - i)))
def plotShifts(x):
fig = plt.figure() # make a new figure
ax = fig.add_axes([0.15, 0.2, 0.65, 0.7]) # add data axes
origType = type(x)
maxval = type(x)(-1)
numrots = sizeof(type(x)) * 8
vals = [rot(x, type(x)(i)) for i in range(numrots)]
print vals
print maxval
l, = ax.plot(range(numrots), vals, 'ro') # plot to data axes
axcolor = 'lightgoldenrodyellow'
inputax = fig.add_axes([0.15, 0.05, 0.65, 0.03], axisbg=axcolor)
inputsl = Slider(inputax, 'Input', 0, maxval, valinit=0, valfmt="%d")
def update(x):
vals = [rot(origType(x), origType(i)) for i in range(numrots)]
l.set_ydata(vals)
plt.draw()
inputsl.on_changed(update)
ax.set_ylim([-2,maxval +2]) # set ylim on data axes
ax.set_xlim([-.5,numrots-1+.05]) # set xlim on data axes
return inputsl
sldr = plotShifts(uint8(1))
plt.show()

most likely because maxval =7 in this line
inputsl = Slider(inputax, 'Input', 0, maxval, valinit=0, valfmt="%d")

Related

"Casting complex values to real discards the imaginary part" using fft

I want to know what is wrong with the code.
I just want to made a fourier transform graph and change the values by sliders.
But this is what happened to my graph.
I just want to made a Parametric EQ graph interface like this, only the graph part with the sliders
Here the source code:
import matplotlib as mpl
import matplotlib.pyplot as plt
from numpy import pi, sin
import numpy as np
from matplotlib.widgets import Slider, Button, RadioButtons
import scipy.fftpack
"""import warnings
warnings.simplefilter("ignore",np.ComplexWarning)"""
# Eq many times calcule
def fermi(A1, F1, A2, F2):
peak1 = A1 * sin(2.0 * pi * F1 * x)
pFl = A2 * sin(2.0 * pi * F2 * x)
y = peak1 + pFl
yf = scipy.fftpack.fft(y)
return yf
fig = plt.figure(figsize=(5, 5))
# Create main axis
ax = fig.add_subplot(111)
ax.set_xlim([0,30])
ax.set_ylim([-2, 10])
fig.subplots_adjust(bottom=0.5, top=0.95)
# Create axes for sliders
ax_a1 = fig.add_axes([0.3, 0.10, 0.4, 0.05])
ax_a1.spines['top'].set_visible(True)
ax_a1.spines['right'].set_visible(True)
ax_f1 = fig.add_axes([0.3, 0.01, 0.4, 0.05])
ax_f1.spines['top'].set_visible(True)
ax_f1.spines['right'].set_visible(True)
ax_a2 = fig.add_axes([0.3, 0.20, 0.4, 0.05])
ax_a2.spines['top'].set_visible(True)
ax_a2.spines['right'].set_visible(True)
ax_f2 = fig.add_axes([0.3, 0.30, 0.4, 0.05])
ax_f2.spines['top'].set_visible(True)
ax_f2.spines['right'].set_visible(True)
# Create sliders
s_a1 = Slider(ax=ax_a1, label='amp1 ', valmin=-2, valmax=6, valinit=0, valfmt=' %1.1f eV', facecolor='#cc7000')
s_f1 = Slider(ax=ax_f1, label='f1 ', valmin=0, valmax=30, valinit=1.5, valfmt=' %i K', facecolor='#cc7000')
s_a2 = Slider(ax=ax_a2, label='amp2 ', valmin=-2, valmax=6, valinit=0, valfmt=' %1.1f eV', facecolor='#cc7000')
s_f2 = Slider(ax=ax_f2, label='f2 ', valmin=0, valmax=30, valinit=3.946, valfmt=' %i K', facecolor='#cc7000')
N = 10500
T = 1.0/ 800.0
# Plot default data
x = np.linspace(-0, 30, 1000)
a1_0 = 5
f1_0 = 0
a2_0 = 0
f2_0 = 0
y = fermi(a1_0, f1_0, a2_0, f2_0)
f_d, = ax.plot(x, y, linewidth=0.5, color='#000000')
# Update values
def update(val):
aa1 = s_a1.val
ff1 = s_f1.val
aa2 = s_a2.val
ff2 = s_f2.val
f_d.set_data(x.real, (fermi(aa1, ff1, aa2, ff2)).imag)
fig.canvas.draw_idle()
s_a1.on_changed(update)
s_f1.on_changed(update)
s_a2.on_changed(update)
s_f2.on_changed(update)
plt.show()
I suspect the error is in the function fermi.
The function fermi looks fine by itself. However it is important to note that the FFT results are complex numbers. Matplotlib on the other hand plots real-valued data and doesn't know what to do with complex numbers. It then issues the warning that you saw, as it throws away the imaginary part of your data before plotting.
For your specific application it looks like the (real-valued) magnitude of the FFT might be more what you're after. If you are only ever going to need the magnitude of the FFT, you could change your fermi function to only return the computed magnitude:
yf = np.abs(scipy.fftpack.fft(y))

matplotlib: How to add linear secondary_yaxis for log-scale primary axis?

I am having a plot with two y-axes, where the second one is some function of the values of the first one. For this I am using the secondary_yaxis function. I want to make this work for the case that the primary axis is logarithmic.
This works very well if the first axis is linear, e.g.:
import matplotlib.pyplot as plt
import numpy as np
def inverse(x):
return -7 * np.log(x / 1013.25)
def forward(x):
return 1013.25 * np.exp(-x / 7)
data = np.array([0.1, 0.4, 0.3, 0.3, 0.8, 0.9])
alt = np.array([0, 5, 8, 15, 17, 20])
fig, ax = plt.subplots(nrows=1)
ax.plot(data, alt)
ax.set_ylim([0, 20])
ax1 = ax.secondary_yaxis("right", functions=(forward, inverse))
This creates this plot with a correct secondary axis
Now I try to do the same, but the other way round, i.e. the logarithmic (pressure)
axis is the primary axis:
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.ticker import ScalarFormatter
def forward(x):
return -7 * np.log(x / 1013.25)
def inverse(x):
return 1013.25 * np.exp(-x / 7)
data = np.array([0.1, 0.4, 0.3, 0.3, 0.8, 0.9])
pres = np.array([1000, 800, 600, 500, 300, 100])
fig, ax = plt.subplots(nrows=1)
ax.plot(data, pres)
ax.set_yscale("log")
ax.get_yaxis().set_major_formatter(ScalarFormatter())
ax.get_yaxis().set_minor_formatter(ScalarFormatter())
ax.set_ylim([1000, 100])
ax1 = ax.secondary_yaxis("right", functions=(forward, inverse))
The values on the secondary axis seem to be correct, but the ticks are logarithmic, as inherited from the primary axis:
Now I can add ax1.set_yscale("linear"), but the the values are also transformed, resulting in the wrong values on correct linear ticks:
I am not finding a way to fix this. Any ideas?
(In the real-world script, I am using an interpolation function for the inverse/forward functions here).

How to plot an animated matrix in matplotlib

I need to do step by step some numerical calculation algorithms visually, as in the figure below: (gif)
Font
How can I do this animation with matplotlib? Is there any way to visually present these transitions? As transformation of matrices, sum, transposition, using a loop and it presenting the transitions etc.
My goal is not to use graphics but the same matrix representation. This is to facilitate the understanding of the algorithms.
Since matrices can be plotted easily with imshow, one could create such table with an imshow plot and adjust the data according to the current animation step.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as mticker
import matplotlib.animation
#####################
# Array preparation
#####################
#input array
a = np.random.randint(50,150, size=(5,5))
# kernel
kernel = np.array([[ 0,-1, 0], [-1, 5,-1], [ 0,-1, 0]])
# visualization array (2 bigger in each direction)
va = np.zeros((a.shape[0]+2, a.shape[1]+2), dtype=int)
va[1:-1,1:-1] = a
#output array
res = np.zeros_like(a)
#colorarray
va_color = np.zeros((a.shape[0]+2, a.shape[1]+2))
va_color[1:-1,1:-1] = 0.5
#####################
# Create inital plot
#####################
fig = plt.figure(figsize=(8,4))
def add_axes_inches(fig, rect):
w,h = fig.get_size_inches()
return fig.add_axes([rect[0]/w, rect[1]/h, rect[2]/w, rect[3]/h])
axwidth = 3.
cellsize = axwidth/va.shape[1]
axheight = cellsize*va.shape[0]
ax_va = add_axes_inches(fig, [cellsize, cellsize, axwidth, axheight])
ax_kernel = add_axes_inches(fig, [cellsize*2+axwidth,
(2+res.shape[0])*cellsize-kernel.shape[0]*cellsize,
kernel.shape[1]*cellsize,
kernel.shape[0]*cellsize])
ax_res = add_axes_inches(fig, [cellsize*3+axwidth+kernel.shape[1]*cellsize,
2*cellsize,
res.shape[1]*cellsize,
res.shape[0]*cellsize])
ax_kernel.set_title("Kernel", size=12)
im_va = ax_va.imshow(va_color, vmin=0., vmax=1.3, cmap="Blues")
for i in range(va.shape[0]):
for j in range(va.shape[1]):
ax_va.text(j,i, va[i,j], va="center", ha="center")
ax_kernel.imshow(np.zeros_like(kernel), vmin=-1, vmax=1, cmap="Pastel1")
for i in range(kernel.shape[0]):
for j in range(kernel.shape[1]):
ax_kernel.text(j,i, kernel[i,j], va="center", ha="center")
im_res = ax_res.imshow(res, vmin=0, vmax=1.3, cmap="Greens")
res_texts = []
for i in range(res.shape[0]):
row = []
for j in range(res.shape[1]):
row.append(ax_res.text(j,i, "", va="center", ha="center"))
res_texts.append(row)
for ax in [ax_va, ax_kernel, ax_res]:
ax.tick_params(left=False, bottom=False, labelleft=False, labelbottom=False)
ax.yaxis.set_major_locator(mticker.IndexLocator(1,0))
ax.xaxis.set_major_locator(mticker.IndexLocator(1,0))
ax.grid(color="k")
###############
# Animation
###############
def init():
for row in res_texts:
for text in row:
text.set_text("")
def animate(ij):
i,j=ij
o = kernel.shape[1]//2
# calculate result
res_ij = (kernel*va[1+i-o:1+i+o+1, 1+j-o:1+j+o+1]).sum()
res_texts[i][j].set_text(res_ij)
# make colors
c = va_color.copy()
c[1+i-o:1+i+o+1, 1+j-o:1+j+o+1] = 1.
im_va.set_array(c)
r = res.copy()
r[i,j] = 1
im_res.set_array(r)
i,j = np.indices(res.shape)
ani = matplotlib.animation.FuncAnimation(fig, animate, init_func=init,
frames=zip(i.flat, j.flat), interval=400)
ani.save("algo.gif", writer="imagemagick")
plt.show()
This example sets up the animation inline in a Jupyter notebook. I suppose there's probably also a way to export as a gif, but I haven't looked into that so far.
Anyway, first thing to do is set up the table. I borrowed heavily from Export a Pandas dataframe as a table image for the render_mpl_table code.
The (adapted) version for this problem is:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import animation
from IPython.display import HTML
import six
width = 8
data = pd.DataFrame([[0]*width,
[0, *np.random.randint(95,105,size=width-2), 0],
[0, *np.random.randint(95,105,size=width-2), 0],
[0, *np.random.randint(95,105,size=width-2), 0]])
def render_mpl_table(data, col_width=3.0, row_height=0.625, font_size=14,
row_color="w", edge_color="black", bbox=[0, 0, 1, 1],
ax=None, col_labels=data.columns,
highlight_color="mediumpurple",
highlights=[], **kwargs):
if ax is None:
size = (np.array(data.shape[::-1]) + np.array([0, 1])) *
np.array([col_width, row_height])
fig, ax = plt.subplots(figsize=size)
ax.axis('off')
mpl_table = ax.table(cellText=data.values, bbox=bbox, colLabels=col_labels,
**kwargs)
mpl_table.auto_set_font_size(False)
mpl_table.set_fontsize(font_size)
for k, cell in six.iteritems(mpl_table._cells):
cell.set_edgecolor(edge_color)
if k in highlights:
cell.set_facecolor(highlight_color)
elif data.iat[k] > 0:
cell.set_facecolor("lightblue")
else:
cell.set_facecolor(row_color)
return fig, ax, mpl_table
fig, ax, mpl_table = render_mpl_table(data, col_width=2.0, col_labels=None,
highlights=[(0,2),(0,3),(1,2),(1,3)])
In this case, the cells to highlight in a different color are given by an array of tuples that specify the row and column.
For the animation, we need to set up a function that draws the table with different highlights:
def update_table(i, *args, **kwargs):
r = i//(width-1)
c = i%(width-1)
highlights=[(r,c),(r,c+1),(r+1,c),(r+1,c+1)]
for k, cell in six.iteritems(mpl_table._cells):
cell.set_edgecolor("black")
if k in highlights:
cell.set_facecolor("mediumpurple")
elif data.iat[k] > 0:
cell.set_facecolor("lightblue")
else:
cell.set_facecolor("white")
return (mpl_table,)
This forcibly updates the colors for all cells in the table. The highlights array is computed based on the current frame. The width and height of the table are kind of hard-coded in this example, but that shouldn't be super hard to change based on the shape of your input data.
We create an animation based on the existing fig and update function:
a = animation.FuncAnimation(fig, update_table, (width-1)*3,
interval=750, blit=True)
And lastly we show it inline in our notebook:
HTML(a.to_jshtml())
I put this together in a notebook on github, see https://github.com/gurudave/so_examples/blob/master/mpl_animation.ipynb
Hope that's enough to get you going in the right direction!

How to plot 4D array with 2 sliders using PyPlot?

I have a 4D data set (for those who care, its an astronomical Position-Position-Temperature-Opacity image) in a numpy array, that I need to plot in an interactive way. While there are programs to do this, none of them can handle the unusual form that my data steps in (but I can worry about that, thats not part of the question).
I know how to get it plotting with one Slider, but really I need to plot the image with 2 Sliders, one for each of temperature and opacity.
My MWE of a 3D array code is below:
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
import numpy as np
array = np.random.rand(300,300,10)
axis = 2
s = [slice(0, 1) if i == axis else slice(None) for i in xrange(array.ndim)]
im = array[s].squeeze()
fig = plt.figure()
ax = plt.subplot(111)
l = ax.imshow(im, origin = 'lower')
axcolor = 'lightgoldenrodyellow'
ax = fig.add_axes([0.2, 0.95, 0.65, 0.03], axisbg=axcolor)
slider = Slider(ax, 'Temperature', 0, array.shape[axis] - 1,
valinit=0, valfmt='%i')
def update(val):
ind = int(slider.val)
s = [slice(ind, ind + 1) if i == axis else slice(None)
for i in xrange(array.ndim)]
im = array[s].squeeze()
l.set_data(im)
fig.canvas.draw()
slider.on_changed(update)
plt.show()
Any way to do it with 2 sliders?
EDIT: The problem I am having is I dont know how to expand to 2 sliders. Particularly how to adapt the line
s = [slice(0, 1) if i == axis else slice(None) for i in xrange(array.ndim)]
and how to modify the update function when I go from np.random.rand(300,300,10) to np.random.rand(300,300,10,10). I supposed I will need to declare both a T_axis = 2 and B_axis = 3 rather than simply an axis = 2, but beyond that, I am rather stuck as to how to modify it.
As I interprete the data structure, you have an array of shape (300,300,n,m), where n is the number of temperatures and m is the number of opacities. The image to show for the ith temperature and the jth opacity is hence, array[:,:,i,j].
You now need of course two different silders where one determines the value of i and the other of j.
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
import numpy as np
array = np.random.rand(300,300,10,9)
# assuming you have for each i=Temperature index and j =Opacity index
# an image array(:,:,i,j)
fig, ax = plt.subplots()
l = ax.imshow(array[:,:,0,0], origin = 'lower')
axT = fig.add_axes([0.2, 0.95, 0.65, 0.03])
axO = fig.add_axes([0.2, 0.90, 0.65, 0.03])
sliderT = Slider(axT, 'Temperature', 0, array.shape[2]-1, valinit=0, valfmt='%i')
sliderO = Slider(axO, 'Opacity', 0, array.shape[3]-1, valinit=0, valfmt='%i')
def update(val):
i = int(sliderT.val)
j = int(sliderO.val)
im = array[:,:,i,j]
l.set_data(im)
fig.canvas.draw_idle()
sliderT.on_changed(update)
sliderO.on_changed(update)
plt.show()

How to update artists in scrollable, matplotlib and multiplot

I'm trying to create a scrollable multiplot based on the answer to this question:
Creating a scrollable multiplot with python's pylab
Lines created using ax.plot() are updating correctly, however I'm unable to figure out how to update artists created using xvlines() and fill_between().
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
from matplotlib.widgets import Slider
#create dataframes
dfs={}
for x in range(100):
col1=np.random.normal(10,0.5,30)
col2=(np.repeat([5,8,7],np.round(np.random.dirichlet(np.ones(3),size=1)*31)[0].tolist()))[:30]
col3=np.random.randint(4,size=30)
dfs[x]=pd.DataFrame({'col1':col1,'col2':col2,'col3':col3})
#create figure,axis,subplot
fig = plt.figure()
gs = gridspec.GridSpec(1,1,hspace=0,wspace=0,left=0.1,bottom=0.1)
ax = plt.subplot(gs[0])
ax.set_ylim([0,12])
#slider
frame=0
axframe = plt.axes([0.13, 0.02, 0.75, 0.03])
sframe = Slider(axframe, 'frame', 0, 99, valinit=0,valfmt='%d')
#plots
ln1,=ax.plot(dfs[0].index,dfs[0]['col1'])
ln2,=ax.plot(dfs[0].index,dfs[0]['col2'],c='black')
#artists
ax.fill_between(dfs[0].index,y1=dfs[0]['col2']-0.5,y2=dfs[0]['col2']+0.5,where=dfs[0]['col2']==5,facecolor='r',edgecolors='none',alpha=0.5)
ax.fill_between(dfs[0].index,y1=dfs[0]['col2']-0.5,y2=dfs[0]['col2']+0.5,where=dfs[0]['col2']==8,facecolor='b',edgecolors='none',alpha=0.5)
ax.fill_between(dfs[0].index,y1=dfs[0]['col2']-0.5,y2=dfs[0]['col2']+0.5,where=dfs[0]['col2']==7,facecolor='g',edgecolors='none',alpha=0.5)
ax.vlines(x=dfs[0]['col3'].index,ymin=0,ymax=dfs[0]['col3'],color='black')
#update plots
def update(val):
frame = np.floor(sframe.val)
ln1.set_ydata(dfs[frame]['col1'])
ln2.set_ydata(dfs[frame]['col2'])
ax.set_title('Frame ' + str(int(frame)))
plt.draw()
#connect callback to slider
sframe.on_changed(update)
plt.show()
This is what it looks like at the moment
I can't apply the same approach as for plot(), since the following produces an error message:
ln3,=ax.fill_between(dfs[0].index,y1=dfs[0]['col2']-0.5,y2=dfs[0]['col2']+0.5,where=dfs[0]['col2']==5,facecolor='r',edgecolors='none',alpha=0.5)
TypeError: 'PolyCollection' object is not iterable
This is what it's meant to look like on each frame
fill_between returns a PolyCollection, which expects a list (or several lists) of vertices upon creation. Unfortunately I haven't found a way to retrieve the vertices that where used to create the given PolyCollection, but in your case it is easy enough to create the PolyCollection directly (thereby avoiding the use of fill_between) and then update its vertices upon frame change.
Below a version of your code that does what you are after:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
from matplotlib.widgets import Slider
from matplotlib.collections import PolyCollection
#create dataframes
dfs={}
for x in range(100):
col1=np.random.normal(10,0.5,30)
col2=(np.repeat([5,8,7],np.round(np.random.dirichlet(np.ones(3),size=1)*31)[0].tolist()))[:30]
col3=np.random.randint(4,size=30)
dfs[x]=pd.DataFrame({'col1':col1,'col2':col2,'col3':col3})
#create figure,axis,subplot
fig = plt.figure()
gs = gridspec.GridSpec(1,1,hspace=0,wspace=0,left=0.1,bottom=0.1)
ax = plt.subplot(gs[0])
ax.set_ylim([0,12])
#slider
frame=0
axframe = plt.axes([0.13, 0.02, 0.75, 0.03])
sframe = Slider(axframe, 'frame', 0, 99, valinit=0,valfmt='%d')
#plots
ln1,=ax.plot(dfs[0].index,dfs[0]['col1'])
ln2,=ax.plot(dfs[0].index,dfs[0]['col2'],c='black')
##additional code to update the PolyCollections
val_r = 5
val_b = 8
val_g = 7
def update_collection(collection, value, frame = 0):
xs = np.array(dfs[frame].index)
ys = np.array(dfs[frame]['col2'])
##we need to catch the case where no points with y == value exist:
try:
minx = np.min(xs[ys == value])
maxx = np.max(xs[ys == value])
miny = value-0.5
maxy = value+0.5
verts = np.array([[minx,miny],[maxx,miny],[maxx,maxy],[minx,maxy]])
except ValueError:
verts = np.zeros((0,2))
finally:
collection.set_verts([verts])
#artists
##ax.fill_between(dfs[0].index,y1=dfs[0]['col2']-0.5,y2=dfs[0]['col2']+0.5,where=dfs[0]['col2']==5,facecolor='r',edgecolors='none',alpha=0.5)
reds = PolyCollection([],facecolors = ['r'], alpha = 0.5)
ax.add_collection(reds)
update_collection(reds,val_r)
##ax.fill_between(dfs[0].index,y1=dfs[0]['col2']-0.5,y2=dfs[0]['col2']+0.5,where=dfs[0]['col2']==8,facecolor='b',edgecolors='none',alpha=0.5)
blues = PolyCollection([],facecolors = ['b'], alpha = 0.5)
ax.add_collection(blues)
update_collection(blues, val_b)
##ax.fill_between(dfs[0].index,y1=dfs[0]['col2']-0.5,y2=dfs[0]['col2']+0.5,where=dfs[0]['col2']==7,facecolor='g',edgecolors='none',alpha=0.5)
greens = PolyCollection([],facecolors = ['g'], alpha = 0.5)
ax.add_collection(greens)
update_collection(greens, val_g)
ax.vlines(x=dfs[0]['col3'].index,ymin=0,ymax=dfs[0]['col3'],color='black')
#update plots
def update(val):
frame = np.floor(sframe.val)
ln1.set_ydata(dfs[frame]['col1'])
ln2.set_ydata(dfs[frame]['col2'])
ax.set_title('Frame ' + str(int(frame)))
##updating the PolyCollections:
update_collection(reds,val_r, frame)
update_collection(blues,val_b, frame)
update_collection(greens,val_g, frame)
plt.draw()
#connect callback to slider
sframe.on_changed(update)
plt.show()
Each of the three PolyCollections (reds, blues, and greens) has only four vertices (the edges of the rectangles), which are determined based on the given data (which is done in update_collections). The result looks like this:
Tested in Python 3.5
Your error
TypeError: 'PolyCollection' object is not iterable
can be avoided by removing the comma after l3:
l3 = ax.fill_between(xx, y1, y2, **kwargs)
The return value is a PolyCollection, you need to update its vertices during the update() function. An alternative to the other answer posted here is to make fill_between() give you a new PolyCollection, and then get its vertices and use them to update those of l3:
def update(val):
dummy_l3 = ax.fill_between(xx, y1, y2, **kwargs)
verts = [ path._vertices for path in dummy_l3.get_paths() ]
codes = [ path._codes for path in dummy_l3.get_paths() ]
dummy_l3.remove()
l3.set_verts_and_codes(verts, codes)
plt.draw()
The above code does not run for me; however, to refresh fill_between the following works for me
%matplotlib inline
import numpy as np
from IPython import display
import matplotlib.pyplot as plt
import time
hdisplay = display.display("", display_id=True)
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
x = np.linspace(0,1,100)
ax.set_title("Test")
ax.set_xlim()
y = np.random.random(size=(100))
dy = 0.1
l = ax.plot(x,y,":",color="red")
b = ax.fill_between( x, y-dy, y+dy, color="red", alpha=0.2 )
hdisplay.update(fig)
for i in range(5):
time.sleep(1)
ax.set_title("Test %ld" % i)
y = np.random.random(size=(100))
l[0].set_ydata( y )
b.remove()
b = ax.fill_between( x, y-dy, y+dy, color="red", alpha=0.2 )
plt.draw()
hdisplay.update(fig)
plt.close(fig)

Categories

Resources