matplotlib 中多个直方图的交互式叠加

Interactive overlay of multiple histograms in matplotlib

我有多个图可以显示它们相互重叠。

import matplotlib.pyplot as plt

a = [1,2,2,3,3,3,4,4,4,4,5,5,5,5,5]
b = [5,4,4,3,3,3,2,2,2,2,1,1,1,1,1]
c = [4,5,5,6,6,7,7,7,8,8,8,9,9,10,10]

x_min, x_max = min(a + b + c), max(a + b + c)

plt.hist(a, range=(x_min,x_max), bins = 10, alpha=0.5, label="a")
plt.hist(b, range=(x_min,x_max), bins = 10, alpha=0.5, label="b")
plt.hist(c, range=(x_min,x_max), bins = 10, alpha=0.5, label="c")

plt.legend()
plt.show()

我是否可以一步生成所有单独的图,然后允许用户在第二步中交互式地选择要叠加的图?

在此示例中,正确的解决方案应包含三个交互式复选框(每个图对应一个)。因为有 3 个复选框,所以用户可以通过 2^3=8 种可能的方式指定绘图。

显然,您必须编写自己的函数。 Matplotlib hist() returns a BarContainer - 矩形对象列表的奇特名称,即直方图的条形。我们可以设置每个矩形的可见性,就像我们可以设置线图中每条线的可见性一样。因此,实现可能如下所示:

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


def select_plots(list_of_inputs, labels=list("abcdefghijklmn"), bins=10):
    #list_of_input and labels must have the same length
    nr_of_hists=len(list_of_inputs)
    labels = labels[:nr_of_hists]
      
    x_min, x_max = min(min(l) for l in list_of_inputs), max(max(l) for l in list_of_inputs)
    #collect list of barcontainers generated by hist plots
    barcontainers = []
    fig, ax  = plt.subplots(figsize=(10, 8))
    for label, list in zip(labels, list_of_inputs):
        _, _, curr_barcontainer  = ax.hist(list, range=(x_min,x_max), bins=bins, alpha=0.5, label=label) 
        barcontainers.append(curr_barcontainer)
    
    #create checkboxes and color them to correspond with the plot
    ax_chkbox = plt.axes([0.8, 0.9-0.05*nr_of_hists, 0.1, 0.05*nr_of_hists])
    check_boxes = CheckButtons(ax_chkbox, labels, np.ones(nr_of_hists, dtype=bool))  
    handles, _ = ax.get_legend_handles_labels()
    for rect, handle in zip(check_boxes.rectangles, handles):
        rect.set_facecolor(handle.get_facecolor())

    #redraw figure when checkbox status has changed
    def chkboxcall(label):
        for barcontainer, status in zip(barcontainers, check_boxes.get_status()):
            [rect.set_visible(status) for rect in barcontainer]
        fig.canvas.draw_idle()            
            
    #connect widget function   
    check_boxes.on_clicked(chkboxcall)   
    plt.show()
    #return last status of checkboxes after figure has been closed  
    return [i for i, status in enumerate(check_boxes.get_status()) if status]   


#input lists with their corresponding label names
a = [1,2,2,3,3,3,4,4,4,4,5,5,5,5,5,6,6]
b = [5,4,4,3,3,3,2,2,2,2,1,1,1,1,1]
c = [4,5,5,6,6,7,7,7,8,8,8,9,9,10,10]

all_lists = [a, b, c]
all_labels = ["a", "b", "c"]
#bins can be integers or ranges like [0, 2, 6, 7, 10]
bins = 10

idx = select_plots(all_lists, all_labels, bins)

print("The following data are valid:", *[all_labels[i] for i in idx])

示例输出:

>>>The following data are valid: a c

直方图引用线例的问题在于,直方图实际上是多条线的组合。这意味着,当按钮被调用时,您需要遍历这些行以获得预期的行为。

在下面的示例中,我们将看到如何遍历直方图的每个部分 p。我们根据其当前值更改透明度。注意:jupyter notebook 需要 %matplotlib qt

import matplotlib.pyplot as plt
from matplotlib.widgets import CheckButtons
%matplotlib qt 

### Specify Data And Range
a = [1,2,2,3,3,3,4,4,4,4,5,5,5,5,5]
b = [5,4,4,3,3,3,2,2,2,2,1,1,1,1,1]
c = [4,5,5,6,6,7,7,7,8,8,8,9,9,10,10]

x_min, x_max = min(a + b + c), max(a + b + c)

### Create Plots
fig, ax = plt.subplots()

p1 = ax.hist(a, range=(x_min,x_max), bins = 10, alpha=0.3, label="a")[2]
p2 = ax.hist(b, range=(x_min,x_max), bins = 10, alpha=0.3, label="b")[2]
p3 = ax.hist(c, range=(x_min,x_max), bins = 10, alpha=0.3, label="c")[2]
plt.subplots_adjust(left=0.2)

plots = [p1, p2, p3]

# Make Check Buttons
rax    = plt.axes([0.05, 0.4, 0.1, 0.15])
labels = ["a", "b", "c"]
check  = CheckButtons(rax, labels)

def action(label, default_transparency = 0.3):
    
    index = labels.index(label)
    for p in plots[index]:
        if p.get_alpha() == default_transparency:
            p.set_alpha(0.0)
        else:
            p.set_alpha(default_transparency)
        
    plt.draw()

### Run Widget
check.on_clicked(action)

plt.legend()
plt.show()