Matplotlib多轴选取事件

6

我正在处理带有两个轴的图表,支持选取线条。我正在使用matplotlib和twinx()命令。不幸的是,选取事件只针对顶层轴中的图形进行调用(如下例所示)。

import matplotlib.pyplot as plt
import numpy as np

def onPick(event):
    print(event.artist.get_label())
    
def pick():
    fig=plt.figure(figsize=(5, 4), dpi=100, tight_layout=True)
    axis_1=fig.add_subplot(111)
    axis_2=axis_1.twinx()

    axis_1.set_ylim(0, 10)
    axis_2.set_ylim(0, 10)

    x=np.array([1,2,3,4])
    y_1=np.array([1,1,1,1])
    y_2=y_1+4
    
    axis_1.plot(x, y_1, picker=5, label='line_1')
    axis_2.plot(x, y_2, picker=5, label='line_2')
    
    fig.canvas.mpl_connect('pick_event', onPick)
    plt.show()

if __name__=='__main__':
    pick()

有没有一种方法可以从下面的轴中选择线条?


1
这个回答解决了你的问题吗?Matplotlib在次要y轴上选择事件 - JohanC
我知道这篇文章。我正在寻找一般的解决方案,与twinx()无关。如何实现具有两个轴的拾取事件? - EGuy
很抱歉,这是不可能的。 - JohanC
2个回答

2

不可能。^^ 我找到了一个解决方案。我不是从坐标轴中选择,而是从图例中选择。
我认为这是一个很好的折衷方案。

import matplotlib.pyplot as plt
import numpy as np
from numpy.random import rand


def onpick(event):
        print(event.artist.get_label())


if __name__ == '__main__':
    t=np.linspace(1, 10, 100)
    y1, y2=1*t, 2*t
    y3, y4=3*t, 4*t

    fig, ax1=plt.subplots()
    ax2=ax1.twinx()
    ax2._get_lines.prop_cycler = ax1._get_lines.prop_cycler # Send Color cycle state to second axis.
    
    line1, = ax1.plot(t, y1, lw=2, label='1 HZ')
    line2, = ax1.plot(t, y2, lw=2, label='2 HZ')
    line3, = ax2.plot(t, y3, lw=2, label='3 HZ')
    line4, = ax2.plot(t, y4, lw=2, label='4 HZ')
    
    leg=ax1.legend(handles=[line1, line2, line3, line4], bbox_to_anchor=(0,1.02,1,0.2), loc="lower left", mode="expand", borderaxespad=0, ncol=3)
    for line in leg.get_lines(): line.set_picker(5)
    
    fig.canvas.mpl_connect('pick_event', onpick)
    plt.show()

这个确实有效。但由于某种原因,如果我将bbox_to_anchor覆盖在图表上而不是在上方,则它不起作用。将其改为bbox_to_anchor=(0,0.95,1,0.2),您将注意到2Hz的事件没有被触发。 - tomatoeshift
发现了一种解决方法。用 plt.legend() 替代 ax1.legend() - tomatoeshift

1

不是不可能!但是你需要处理button_press_event而不是使用pick_event,通过查找每个绘制的line来确定选择了什么。每个axes也需要自己的annotation,否则你会很难找到正确的注释位置。

这是我所做的,受this answer启发:

from dataclasses import dataclass
from matplotlib.backend_bases import PickEvent
import matplotlib.pyplot as plt
import matplotlib

def on_button_press(event):
    if not event.inaxes:
        return # off canvas.

    all_picked = [dca for dca in dcAxes if dca.line.contains(event)[0]]
    if not all_picked:
        return # nothing was picked

    picked = all_picked[0] # take the first

    ind = picked.line.contains(event)[1]['ind']
    x_index = ind[0]

    x_val = picked.line.get_xdata()[x_index]
    y_val = picked.line.get_ydata()[x_index]

    annotation_visbility = picked.annotation.get_visible()
    if annotation_visbility and picked.annotation.xy==(x_val,y_val):
        picked.annotation.set_visible(False)
        fig.canvas.draw_idle()
    else:
        picked.annotation.xy = (x_val,y_val)

        text_label = f'{picked.line.get_label()}:({x_val},{y_val})'
        picked.annotation.set_text(text_label)

        picked.annotation.set_visible(True)
        fig.canvas.draw_idle()

 # create data to plot
x = []
y = []
y.append([])
y.append([])
for i in range(10):
    x.append(i)
    y[0].append(i)
    y[1].append(i*i)

# create plots, saving axes/line/annotation for lookup
@dataclass
class DataClassAxes:
    ax: plt.axes
    line: matplotlib.lines.Line2D
    annotation: matplotlib.text.Annotation

dcAxes: list[DataClassAxes] = []

for i in range(2):
    if i==0:
        fig, ax = plt.subplots()
        line, = ax.plot(x, y[i], 'o', picker=5, color='red', label='reds')
    else:
        ax = dcAxes[0].ax.twinx()
        line, = ax.plot(x, y[i], 'o', picker=5, color='blue', label='blues')

    annotation = ax.annotate(
        text='',
        xy=(0, 0),
        xytext=(15, 15), # distance from x, y
        textcoords='offset points',
        bbox={'boxstyle': 'round', 'fc': 'w'},
        arrowprops={'arrowstyle': '->'}
    )
    annotation.set_visible(False)

    dcAxes.append(DataClassAxes(ax, line, annotation))

fig.canvas.mpl_connect('button_press_event', on_button_press)

plt.show()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接