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()
我有多个图可以显示它们相互重叠。
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()