如何在scrollable,matplotlib和multiplot

时间:2015-11-20 13:45:50

标签: python matplotlib slider

我正在尝试根据这个问题的答案创建一个可滚动的多重绘图: Creating a scrollable multiplot with python's pylab

使用ax.plot()创建的行正在正确更新,但我无法弄清楚如何更新使用xvlines()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()

这就是目前的样子 enter image description here

我无法应用与plot()相同的方法,因为以下内容会产生错误消息:

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

这就是每个框架的外观 enter image description here

2 个答案:

答案 0 :(得分:2)

fill_between返回PolyCollection,它在创建时需要一个列表(或几个列表)顶点。不幸的是,我还没有找到一种方法来检索用于创建给定PolyCollection的顶点,但在您的情况下,直接创建PolyCollection很容易(从而避免使用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

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()

三个PolyCollectionsredsbluesgreens)中的每一个都只有四个顶点(矩形的边缘),这些顶点是根据给定数据(在update_collections中完成)。结果如下:

example result of given code

在Python 3.5中测试

答案 1 :(得分:0)

您的错误

TypeError: 'PolyCollection' object is not iterable
通过在l3之后删除逗号可以避免

l3 = ax.fill_between(xx, y1, y2, **kwargs)

返回值为PolyCollection,您需要在update()函数期间更新其顶点。这里发布的其他答案的替代方法是使fill_between()为您提供一个新的PolyCollection,然后获取其顶点并使用它们来更新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()