如何在可滚动、matplotlib 和 multiplot 中更新艺术家 [英] How to update artists in scrollable, matplotlib and multiplot

查看:38
本文介绍了如何在可滚动、matplotlib 和 multiplot 中更新艺术家的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试根据以下问题的答案创建可滚动的多图:

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

  ln3,= ax.fill_between(dfs [0] .index,y1 = dfs [0] ['col2']-0.5,y2 = dfs [0] ['col2'] + 0.5,其中=dfs [0] ['col2'] == 5,facecolor ='r',edgecolors ='none',alpha = 0.5)TypeError:"PolyCollection"对象不可迭代

这就是它在每一帧上的样子

解决方案

fill_between 返回一个 PolyCollection,它在创建时需要一个(或多个)顶点列表.不幸的是,我还没有找到一种方法来检索用于创建给定 PolyCollection 的顶点,但是在您的情况下,直接创建 PolyCollection 很容易(因此避免使用 fill_between),然后在帧更改时更新其顶点.

下面是您要执行的操作的代码版本:

将 numpy 导入为 np将熊猫作为pd导入导入matplotlib.pyplot作为plt导入 matplotlib.gridspec 作为 gridspec从matplotlib.widgets导入Slider从matplotlib.collections导入PolyCollection#创建数据框dfs = {}对于范围内的 x(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})#创建图形,轴,子图无花果= 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])#滑块帧=0axframe = plt.axes([0.13, 0.02, 0.75, 0.03])sframe = Slider(axframe, 'frame', 0, 99, valinit=0,valfmt='%d')#plotsln1,=ax.plot(dfs[0].index,dfs[0]['col1'])ln2,=ax.plot(dfs[0].index,dfs[0]['col2'],c='black')##additional 代码来更新 PolyCollectionsval_r = 5val_b = 8val_g = 7def update_collection(collection, value, frame = 0):xs = np.array(dfs[frame].index)ys = np.array(dfs [frame] ['col2'])##我们需要捕捉不存在带有 y == 值的点的情况:尝试:minx = np.min(xs [ys == value])maxx = np.max(xs [ys == value])miny =值-0.5maxy = 值+0.5verts = np.array([[[minx,miny],[maxx,miny],[maxx,maxy],[minx,maxy]])除了ValueError:verts = np.zeros((0,2))最后:collection.set_verts([verts])#艺术家## ax.fill_between(dfs [0] .index,y1 = dfs [0] ['col2']-0.5,y2 = dfs [0] ['col2'] + 0.5,其中= dfs [0] ['col2']==5,facecolor='r',edgecolors='none',alpha=0.5)红色= PolyCollection([],facecolors = ['r'],alpha = 0.5)ax.add_collection(红人)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(蓝调)update_collection(蓝色,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)绿色 = PolyCollection([],facecolors = ['g'], alpha = 0.5)ax.add_collection(绿色)update_collection(绿色,val_g)ax.vlines(x=dfs[0]['col3'].index,ymin=0,ymax=dfs[0]['col3'],color='black')#更新图def update(val):框架= np.floor(sframe.val)ln1.set_ydata(dfs [frame] ['col1'])ln2.set_ydata(dfs [frame] ['col2'])ax.set_title('Frame ' + str(int(frame)))##更新 PolyCollections:update_collection(红色,val_r,框架)update_collection(蓝调,val_b,框架)update_collection(绿色,val_g,框架)plt.draw()#connect 回调到滑块sframe.on_changed(更新)plt.show()

三个 PolyCollections ( reds blues greens )中的每一个只有四个顶点(矩形的边缘),这是根据给定的数据确定的(在 update_collections 中完成).结果看起来像这样:

在Python 3.5中进行了测试

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

这篇关于如何在可滚动、matplotlib 和 multiplot 中更新艺术家的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆