如何在带有子图的 matplotlib 中使用 SpanSelector?

How to use SpanSelector in matplotlib with subplots?

我正在绘制来自多个来源的数据,并使用 SpanSelector 来分析特定事件期间的数据。我可以 select 每个子图中的数据并提取数据。但是,当我在新的子图中创建新的 selection 时,我希望前一个子图中的 selection 消失。

span.set_visible(True/False)。但是为了让它起作用,我需要知道哪个子图正在 selected。我试图找到使用 lambda 函数的方法,但目前无法正常工作。

我先试了:

for signal in self.signals:
    self.span[k] = SpanSelector(
    self.ax[k],
    onselect = lambda min, max : self.onselect_function(min, max, k),
    # onselect = self.onselect_function,
    direction = 'horizontal',
    minspan = 1,
    useblit = True,
    interactive = True,
    button = 1,
    props = {'facecolor': 'red', 'alpha':0.5},
    drag_from_anywhere = True
    )
    k += 1

def onselect_function(self, min_value, max_value, selected_graph)
    ...

但它一直在为 selected_graph 发送 selected_graph = 3 的值,无论我 select 是哪个子图。有 3 个子图 ( k = 0 - 2).

我认为这是因为 k 不在命名空间中,我试过:

onselect = lambda min, max, k : self.onselect_function(min, max, k)

但是现在,我收到以下错误:

File "C:\Users\Ashu\miniconda3\envs\my_env\lib\site-packages\matplotlib\cbook\__init__.py", line 287, in process
    func(*args, **kwargs)
  File "C:\Users\Ashu\miniconda3\envs\my_env\lib\site-packages\matplotlib\widgets.py", line 1958, in release
    self._release(event)  File "C:\Users\Ashu\miniconda3\envs\my_env\lib\site-packages\matplotlib\widgets.py", line 2359, in _release
    self.onselect(vmin, vmax)
TypeError: <lambda>() missing 1 required positional argument: 'k'

理想情况下,我希望 selection 突出显示所有子图中的 x 轴范围(我仍然可以手动执行此操作,因为我有关于 selected 范围的信息,如图中虚线所示)。但是当新的 selection 开始时,我需要之前的 selection 消失。

当前显示方式的示例。最后一个 selection 是在顶部图表中制作的,因此每个图表都显示了该 selection (51-53) 的文本信息,但底部两个图表中的高亮显示在错误的部分。我也试过清除坐标轴,但还是不行。

编辑:添加最小的可重现示例

import matplotlib.pyplot as plt
from matplotlib.widgets import SpanSelector
from random import randrange
import math
plt.style.use('seaborn')

noOfDataToShow = 10
noOfSignals = 3
fig, ax = plt.subplots(nrows = noOfSignals, ncols = 1)
x_pos = []
signal_data_holder = []
for i in range(noOfDataToShow):
    x_pos.append(i)

for i in range(noOfSignals):
    temp = []
    for j in range (noOfDataToShow):
        temp.append(randrange(0,10))
    signal_data_holder.append(temp)

def onselect_function(min_value, max_value, selected_graph = 0):
    k = 0
    for param in signal_data_holder:
        ax[k].cla()
        ax[k].set_ylim([0,10])
        ax[k].plot(x_pos, param, label = str (math.trunc(param[noOfDataToShow - 1])))
        ax[k].legend(loc = "upper left")
        k += 1

#******Calculating Min_Max (Can be ignored) ******#
    min_value = math.floor(min_value)
    max_value = math.ceil(max_value)
    min_value_data = [None] * noOfSignals
    max_value_data = [None] * noOfSignals
    if (min_value < 0): 
        min_value = 0
    if (max_value >= noOfDataToShow):
        max_value = noOfDataToShow
    for k in range(noOfSignals):
        min_value_data = signal_data_holder[k][min_value]
        max_value_data = signal_data_holder[k][min_value]
        for i in range(min_value, max_value):
            if (signal_data_holder[k][i] < min_value_data):
                min_value_data = signal_data_holder[k][i]
            elif (signal_data_holder[k][i] > max_value_data):
                max_value_data = signal_data_holder[k][i]
        labelText = "Min_Value: " + str (min_value_data) + "\n"
        labelText += "Max_Value: " + str (max_value_data) + "\n"
        labelText += "Range: " + str (min_value) + "-" + str (max_value) + "\n"
        ax[k].legend(labels = [labelText], loc = "upper left")
        print(min_value_data, max_value_data)
        ax[k].axvline(min_value, color = 'red', linestyle = 'dashed')
        ax[k].axvline(max_value, color = 'red', linestyle = 'dashed')
    fig.canvas.draw()
#******Calculating Min_Max (Can be ignored) ******#

    return min_value, max_value

span = [None] * noOfSignals
for k in range(noOfSignals):
    span[k] = SpanSelector(
    ax[k],
    # onselect = lambda min, max, k : onselect_function(min, max, k),
    # onselect = lambda min, max : onselect_function(min, max, k),
    onselect = onselect_function,
    direction = 'horizontal',
    minspan = 1,
    useblit = True,
    interactive = True,
    button = 1,
    props = {'facecolor': 'red', 'alpha':0.5},
    drag_from_anywhere = True
    )

k = 0
for param in signal_data_holder:
    ax[k].cla()
    ax[k].set_ylim([0,10])
    ax[k].plot(x_pos, param, label = str (math.trunc(param[noOfDataToShow - 1])))
    ax[k].legend(loc = "upper left")
    k += 1

plt.show()

我认为您不能直接检索链接到跨度选择器的轴对象(或者至少,我不知道如何检索)。不过我们也可以收集最后一次鼠标点击的轴对象:

import matplotlib.pyplot as plt
from matplotlib.widgets import SpanSelector
import numpy as np

#sample data
fig, axis = plt.subplots(3)
for i, ax in enumerate(axis):
    t=np.linspace(-i, i+1 , 100)
    ax.plot(t, np.sin(2 * np.pi * t))

#list to store the axis last used with a mouseclick
curr_ax = []

#detect the currently modified axis
def on_click(event):
    if event.inaxes:
        curr_ax[:] = [event.inaxes]

#modify the current axis objects
def onselect(xmin, xmax):
    #ignore if accidentally clicked into an axis object
    if xmin==xmax:
        return
    #set all span selectors invisible accept the current 
    for ax, span in zip(axis, list_of_spans):
        if ax != curr_ax[0]:
            span.set_visible(False)
    #do something with xmin, xmax
    print(xmin, xmax)
    fig.canvas.draw_idle()


#collect span selectors in a list in the same order as their axes objects
list_of_spans = [SpanSelector(
        ax,
        onselect,
        "horizontal",
        useblit=True,
        props=dict(alpha=0.5, facecolor="tab:blue"),
        interactive=True,
        drag_from_anywhere=True
        )
        for ax in axis]

plt.connect('button_press_event', on_click)

plt.show()