Python 注释Matplotlib多集合栏时间线

Python 注释Matplotlib多集合栏时间线,python,matplotlib,Python,Matplotlib,我的问题是:当使用“motion\u notify\u event”将鼠标悬停在条形图上时,如何对Matplotlib多集合条形图进行注释?我花了一天的时间来做这项工作,但我就是不能让我的头围绕一个多收集对象和如何索引它。希望有人能帮我解决这个问题,我在这上面花了太多时间,我现在不想放弃 使用“motion\u notify\u event”(运动通知事件)可以很好地解决散点、线条和条形标注问题,请参见以下链接: 但是,上述解决方案不处理“matplotlib.collections.poly

我的问题是:当使用“motion\u notify\u event”将鼠标悬停在条形图上时,如何对Matplotlib多集合条形图进行注释?我花了一天的时间来做这项工作,但我就是不能让我的头围绕一个多收集对象和如何索引它。希望有人能帮我解决这个问题,我在这上面花了太多时间,我现在不想放弃

使用“motion\u notify\u event”(运动通知事件)可以很好地解决散点、线条和条形标注问题,请参见以下链接:

但是,上述解决方案不处理“matplotlib.collections.polycollection”对象。我正在努力寻找使用上述解决方案中的代码访问此对象中的顶点和数组的方法。是否可以为多个集合编制索引;使用索引访问原始数据中的字符串值;然后在悬停在其上时标注位置

我使用了下面链接中的一个修改后的Polycollection条时间线示例,该示例与上面链接中的悬停/注释示例融合在一起。


获取所需数据的方法有点复杂,可以通过深入调试器找到:

  • bar.get_paths()
    提供了
    Path
    对象(多边形)的列表
  • 可以对其进行索引以访问单个条
  • 条。获取路径()[ind]。获取范围()
    给出条的边界框
  • 条。获取路径()[ind]。获取范围()。获取点()
    给出边界框的左下和右上点
将日期时间导入为dt
将matplotlib.pyplot作为plt导入
将matplotlib.dates导入为mdates
从matplotlib.collections导入PolyCollection
数据=[(dt.datetime(2018,7,17,0,15),dt.datetime(2018,7,17,0,30),‘睡眠’,‘小睡1’,
(dt.datetime(2018,7,17,0,30)、dt.datetime(2018,7,17,0,45)、“吃”、“零食1”),
(dt.datetime(2018,7,17,0,45)、dt.datetime(2018,7,17,1,0)、“工作”、“会议1”),
(dt.datetime(2018,7,17,1,0)、dt.datetime(2018,7,17,1,30)、“睡眠”、“小睡2”),
(dt.datetime(2018,7,17,1,15)、dt.datetime(2018,7,17,1,30)、“吃”、“零食2”),
(dt.datetime(2018,7,17,1,30),dt.datetime(2018,7,17,1,45),“工作”,“项目2”)]
猫={“睡眠”:1,“吃”:2,“工作”:3}
colormapping={“sleep”:“C0”,“eat”:“C1”,“work”:“C2”}
顶点=[]
颜色=[]
对于数据中的d:
v=[(日期为第2个num(d[0]),猫[d[2]]-.4],
(日期2 num(d[0]),猫[d[2]+.4),
(日期为第2天(d[1]),猫[d[2]+.4),
(日期为第2天(d[1]),猫[d[2]-.4),
(日期2 num(d[0]),猫[d[2]-.4]
垂直附加(v)
colors.append(colormapping[d[2]])
条形=多边形集合(顶点、面颜色=颜色)
图,ax=plt.子批次()
ax.添加_集合(条)
ax.自动缩放()
loc=mdates.MinuteLocator(byminute=[0,15,30,45])
ax.xaxis.set\u major\u定位器(loc)
ax.xaxis.set\u major\u格式化程序(mdates.AutoDateFormatter(loc))
ax.set_-yticks([1,2,3])
ax.设置标签([“睡眠”、“吃饭”、“工作”])
annot=ax.annotate(“,xy=(0,0),xytext=(20,20),textcoords=“偏移点”,
bbox=dict(boxstyle=“round”,fc=“w”),
arrowprops=dict(arrowstyle=“->”)
注释:设置为可见(假)
def更新通告(ind):
pos=条。获取路径()[ind]。获取范围()。获取点()[1]
annot.xy=pos
text=f“{data[ind][2]}:{data[ind][3]}”
注释.设置文本(文本)
#annot.get_bbox_patch().set_facecolor(colormapping[data[ind][2])
annot.get_bbox_patch().set_facecolor(bar.get_facecolors()[ind])
annot.get_bbox_patch().set_alpha(0.4)
def悬停(事件):
vis=annot.get_visible()
如果event.inaxes==ax:
cont,ind=条。包含(事件)
如果继续:
更新通知(ind[“ind”][0])
注释:设置为可见(真)
图canvas.draw_idle()
其他:
如果是vis:
注释:设置为可见(假)
图canvas.draw_idle()
图canvas.mpl\u connect(“运动通知事件”,悬停)
plt.show()

不错,约翰!很好的解释!
    import datetime as dt
    import matplotlib.pyplot as plt
    import matplotlib.dates as mdates
    from matplotlib.collections import PolyCollection

    data = [(dt.datetime(2018, 7, 17, 0, 15), dt.datetime(2018, 7, 17, 0, 30), 'sleep','nap_1'),
           (dt.datetime(2018, 7, 17, 0, 30), dt.datetime(2018, 7, 17, 0, 45), 'eat', 'snack_1'),
           (dt.datetime(2018, 7, 17, 0, 45), dt.datetime(2018, 7, 17, 1, 0), 'work', 'meeting_1'),
           (dt.datetime(2018, 7, 17, 1, 0), dt.datetime(2018, 7, 17, 1, 30), 'sleep','nap_2'),
           (dt.datetime(2018, 7, 17, 1, 15), dt.datetime(2018, 7, 17, 1, 30), 'eat', 'snack_2'), 
           (dt.datetime(2018, 7, 17, 1, 30), dt.datetime(2018, 7, 17, 1, 45), 'work', 'project_2')]

    cats = {"sleep" : 1, "eat" : 2, "work" : 3}
    colormapping = {"sleep" : "C0", "eat" : "C1", "work" : "C2"}

    verts = []
    colors = []
    for d in data:
        v =  [(mdates.date2num(d[0]), cats[d[2]]-.4),
              (mdates.date2num(d[0]), cats[d[2]]+.4),
              (mdates.date2num(d[1]), cats[d[2]]+.4),
              (mdates.date2num(d[1]), cats[d[2]]-.4),
              (mdates.date2num(d[0]), cats[d[2]]-.4)]
        verts.append(v)
        colors.append(colormapping[d[2]])

    bars = PolyCollection(verts, facecolors=colors)

    fig, ax = plt.subplots()
    ax.add_collection(bars)
    ax.autoscale()
    loc = mdates.MinuteLocator(byminute=[0,15,30,45])
    ax.xaxis.set_major_locator(loc)
    ax.xaxis.set_major_formatter(mdates.AutoDateFormatter(loc))

    ax.set_yticks([1,2,3])
    ax.set_yticklabels(["sleep", "eat", "work"])

    annot = ax.annotate("", xy=(0, 0), xytext=(20, 20), textcoords="offset points",
      bbox=dict(boxstyle="round", fc="w"),
      arrowprops=dict(arrowstyle="->"))
    annot.set_visible(False)


    def update_annot(ind):
        # how to index the polycollection? get_offsets causes an error
        pos = bars.get_offsets()[ind["ind"][0]] 
        # causes 'IndexError: index 5 is out of bounds for axis 0 with size 1'
        annot.xy = pos
        # If we can get an index, can we use it to access the original 'data' to get the 
        # string value at d[3] from data ('nap_1') and use it to annotate?
        text = ""

        annot.set_text(text)
        annot.get_bbox_patch().set_facecolor(cmap(norm(c[ind["ind"][0]])))
        annot.get_bbox_patch().set_alpha(0.4)


    def hover(event):
        vis = annot.get_visible()
        if event.inaxes == ax:
            cont, ind = bars.contains(event)
            if cont:
                update_annot(ind)
                annot.set_visible(True)
                fig.canvas.draw_idle()
            else:
                if vis:
                    annot.set_visible(False)
                    fig.canvas.draw_idle()

    fig.canvas.mpl_connect("motion_notify_event", hover)

    plt.show()