Matplotlib 不仅适用于静态图。 虽然 GUI 通常是使用 GUI 库和框架创建的,例如 PyQt的, 特金特, Kivy 和 蟒蛇,虽然 Python 确实与 PyQt、Tkinter 和 wxPython——对于一些基本的 GUI 功能,不需要使用其中的任何一个,通过 Matplotlib 小部件.
matplotlib.widgets
模块有几个类,包括 AxesWidget
, 其中有 Button
s, CheckButton
s, Slider
s, TextBox
es等是派生出来的。 这些都接受 Axes
它们被添加为唯一且唯一的强制构造函数参数,并且必须手动设置它们的位置。 需要注意的是, 小部件是轴,所以你将创建一个 Axes
每个小部件的实例。
还有一点要注意的是 您必须保留对小部件的引用 否则,他们可能会被垃圾收集。
它们中的每一个也可以通过设置禁用 active
至 False
,在这种情况下,它们不会响应任何事件,例如被点击。 话虽如此,我们可以通过各种 GUI 元素和组件为我们的绘图引入一种新型的交互性。
请注意: Matplotlib 不适合用于高质量的 GUI 创建,也不适合用户友好的系统。 这些小部件很简陋,看起来不太好并且功能有限。 它们的目的是作为原型和测试的一种方式,而不是实际交付它们。
如果您以前使用过 PyQt,您可能会注意到添加这些小部件以及将它们连接到事件处理程序的一般语法和方法是相当熟悉的。
添加按钮
让我们开始 按钮 - 在 matplotlib.widgets
模块定义了一个 Button
班级。 为了连接到它,我们调用 on_clicked()
函数,它执行我们提供的函数。 一旦检测到点击,该函数就会执行。
在创建按钮时,我们分配一个 Axes
到它,用于定位。 我们也可以传入一个 label
那时,为用户添加一些文本并对其进行注释。 这 color
和 hovercolor
参数定义按钮悬停前后的颜色。
由于我们负责所有小部件的定位和空间——让我们创建一个 Figure
和 Axes
,在底部留出一些间距以添加一个按钮,并绘制一个空的散点图。 然后,我们将定义一个 EventHandler
类,只有一个方法 add_random()
. 该方法生成两个随机数,并在 Axes
我们之前创建并调用 plt.draw()
,它重新绘制 Figure
. 更新地块时,我们总是需要调用 plt.draw()
再次实际更新它:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Button fig, ax = plt.subplots()
fig.subplots_adjust(bottom=0.2)
plot = ax.scatter([], []) class EventHandler: def add_random(self, event): x = np.random.randint(0, 100) y = np.random.randint(0, 100) ax.scatter(x, y) plt.draw() # Axes for the Button and positioning
# xposition and yposition in percentages, width, height
button_ax = plt.axes([0.7, 0.05, 0.2, 0.07])
# Create Button and assign it to `button_ax` with label
button = Button(button_ax, 'Add Random', color='green', hovercolor='red')
# On a detected click, execute add_random()
button.on_clicked(EventHandler().add_random) plt.show()
这导致 Figure
, 有空 Axes
在它里面和屏幕右上角的一个按钮,在它自己的 Axes
:
在按下按钮几十次后,我们的 ax
将填充随机标记:
在更实际的情况下,我们可以创建一个 周期 在每次按下按钮时绘制的特征。 这需要对 EventHandler
,以及返回该循环的另一个按钮。
让我们使用 红酒品质 再次数据集,并针对 酒精 特征。 因为我们不必费心通过编写代码来绘制一个特性与另一个特性,然后修改该代码以绘制另一个特性与另一个特性来单独绘制这些特性。
创建一个散点矩阵可能对我们有帮助,但如果数据集有很多特征,它就会相当不可读,我们也不会走得太远。 如果你想拥有 都 您可以轻松查看和解释的大型绘图, 以及 由于无需任何额外努力即可循环使用多个功能 - 您可以使用按钮自动执行此过程:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.widgets import Button fig, ax = plt.subplots()
fig.subplots_adjust(bottom=0.2) df = pd.read_csv('winequality-red.csv')
plot = ax.scatter([], []) class EventHandler: i = 0 # Find and plot next feature, re-draw the Axes def next_feature(self, event): # If the counter is at the end of the columns # Revert it back to 0 to cycle through again if self.i >= len(df.columns): self.i = 0 # Clear Axes from last plot ax.cla() # Plot a feature against a feature located on the `i` column ax.scatter(df['alcohol'], df.iloc[:,self.i]) # Set labels ax.set_xlabel('Alcohol') ax.set_ylabel(df.columns[self.i]) # Increment i self.i += 1 # Update Figure plt.draw() def previous_feature(self, event): # If the counter is at the start of the columns # Revert it back to the last column to cycle through if self.i <= 0: self.i = len(df.columns)-1 ax.cla() ax.scatter(df['alcohol'], df.iloc[:,self.i]) ax.set_xlabel('Alcohol') ax.set_ylabel(df.columns[self.i]) self.i -= 1 plt.draw() # Add buttons
button1_ax = plt.axes([0.7, 0.02, 0.2, 0.07])
next_button = Button(button1_ax, 'Next Feature')
next_button.on_clicked(EventHandler().next_feature) button2_ax = plt.axes([0.45, 0.02, 0.2, 0.07])
previous_button = Button(button2_ax, 'Previous Feature')
previous_button.on_clicked(EventHandler().previous_feature) plt.show()
EventHandler
类现在有两种方法—— next_feature()
和 previous_feature()
. 这两个都检查计数器是否 i
已到达列列表的结尾或开头 - 并避免 IndexError
,我们将索引重置为相反的值并模拟 周期. 往下走 0
会让我们回到 结束 列列表,并且超过最后一列将使我们恢复到第一列。
在确定了我们所在的位置之后——我们 清除 Axes
, 因为我们将在现有地块之上再次绘制而不通过清除它 cla()
(cl耳朵 axes)。 您也可以通过在彼此之上绘制并使用 cla()
在循环结束/开始时重置索引时的语句。
清除后 Axes
– 我们有一块干净的画布可以用 ax.scatter()
功能。 在这个例子中,固定特征是 酒精,所以它一直存在。 其他功能各不相同,可以通过 iloc[]
,传入列的索引。 这会返回一个 Series
我们可以在这个情节中使用。 同样,我们可以访问 列名 也通过他们的索引—— df.columns[index]
,用于设置Y轴标签。
最后,我们增加/减少计数器并调用 plt.draw()
更新 Figure
:
一旦我们点击 下一功能 按钮,列列表中的下一个特征将被绘制 酒精,并 Figure
将适当更新——标签、标记和比例。 反过来也是一样—— 以前的功能 将以相反的方向遍历列表,允许我们来回循环,并使用安全机制在每次到达循环结束或开始时重置我们的计数器。
添加单选按钮和复选框
单选按钮 用于允许用户选择 一个值 位客人评分中获得平均 几个值. 一次只能选择一个单选按钮,它们通常代表一种选择。 复选框 如果您想让用户一次选择多个选项,可以使用。
请注意: 检查复选框是否存在的能力非常有限 on or 折扣. 事实上,没有开箱即用的。 您只能检查该框是否为 压 or 不能,这对它的使用方式造成了严重的限制,因为我们不知道在此之前它处于哪个状态。 唯一的选择是使用布尔值保留您自己的计数器/检查框的当前状态,并基于此更改逻辑。
例如,这将允许您为每个 自定义参数 某个情节,允许用户设置它们 True
or False
(选中或未选中),或任何其他基于这些状态的非冲突映射。
不过,由于 API 本身是有限的,我们也会将自己限制在预期的用途上——打开和关闭。 我们将有两个功能,我们可以转向 on 和 折扣 通过复选框。 请注意,即使此功能也仅限于您可以检查它们是否可见的对象。
另一方面,我们不想让用户一次应用两个刻度,或者一次设置两个 X 限制,因为只会应用序列中名为 second 的语句。 对于这些——我们会使用单选按钮。
让我们添加几个单选按钮,让用户通过几个单选按钮选择轴范围,但也允许他们转动特征可视化 on 和 折扣:
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.widgets import CheckButtons
from matplotlib.widgets import RadioButtons fig, ax = plt.subplots()
fig.subplots_adjust(bottom=0.2) df = pd.read_csv('winequality-red.csv') # Plot two line plots for two features, and turn them invisible
line1, = ax.plot(df['fixed acidity'], visible=False)
line2, = ax.plot(df['citric acid'], visible=False) class EventHandler: # set_range handler def set_range(label): if (label == 'Small Range'): ax.set_xlim(0, 1600) ax.set_ylim(0, 25) else: ax.set_xlim(0, 1600) ax.set_ylim(0, 50) plt.draw() # Turn off, if on, and on if off def apply_features(label): if (label == 'Fixed Acidity'): line1.set_visible(not line1.get_visible()) elif (label == 'Citric Acid'): line2.set_visible(not line2.get_visible()) plt.draw() # Add radio buttons and checkboxes
ranges_ax = plt.axes([0.7, 0.02, 0.2, 0.1])
range_radio_buttons = RadioButtons(ranges_ax, ('Small Range', 'Large Range'))
range_radio_buttons.on_clicked(EventHandler.set_range) checkboxes_ax = plt.axes([0.4, 0.02, 0.25, 0.1])
checkboxes = CheckButtons(checkboxes_ax, ('Fixed Acidity', 'Citric Acid'))
checkboxes.on_clicked(EventHandler.apply_features) plt.show()
同样,我们在 EventHandler()
班级 - set_range()
和 apply_features()
。 该 set_range()
方法将范围设置为“小”或“大”,通过调整 Axes
' X 和 Y 限制。 这 apply_features()
功能改变了 visible
我们之前制作的线图的字段,基于它们当前的 visible
地位。 如果 visible == True
,我们关闭 Line Plot,反之亦然。
我们必须依靠内置功能来检查线图的可见性,因为我们无法检查复选框之前是否被选中。 同样的能力可以用 status
范围内的布尔值 EventHandler()
类,设置为 True
和 False
每次点击时,对于不支持检查它们是否开箱即用的绘图类型。
运行此代码会导致 Figure
底部有两组按钮。 如果我们选中两个复选框,两个线图都会出现:
我们可以单独关闭它们:
我们可以改变范围 Axes
通过单选按钮:
添加文本框
文本框 习惯了 收集 来自用户的数据——我们可以根据这些数据改变绘图。 例如,我们可以要求用户输入一个特征的名称,或者插入一个函数让我们的绘图可视化。 当然,处理用户输入可能会很棘手——总有一些边缘情况需要注意。
让我们编写一个脚本,允许用户输入一个 功能名称 的数据集,以及 Axes
更新每个提交以反映输入。 为了方便用户,如果输入无法与列名匹配,我们会通知他们:
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.widgets import TextBox fig, ax = plt.subplots()
fig.subplots_adjust(bottom=0.2) df = pd.read_csv('winequality-red.csv') class EventHandler: def submit(feature_name): if feature_name != "" or feature_name != None: if feature_name in df: ax.cla() ax.plot(df[feature_name]) else: if len(textbox_ax.texts) > 2: del textbox_ax.texts[-1] textbox_ax.text(-2, 0.4, feature_name + ' was not found.') plt.draw() textbox_ax = plt.axes([0.7, 0.02, 0.2, 0.1])
textbox = TextBox(textbox_ax, 'Feature Name')
textbox.on_submit(EventHandler.submit) plt.show()
我们有一个简单的检查,看看是否提供 feature_name
为空白或 None
,在这种情况下,我们什么都不做。 如果没有,我们检查是否 feature_name
存在于 DataFrame
,附加一条消息,说明如果该功能不存在,则找不到该功能。 不过,在附加文本之前,我们必须确保之前的消息已被删除,以便新消息不会与它重叠。 这 axes.texts
属性是所有的列表 Text
上的实例 Axes
。 由于 Axes
已经有一个 Text
例如,属于我们的 TextBox
,如果有 2 个或更少,我们不想删除任何内容 Text
存在的实例 - 错误消息和 TextBox
带有
如果以上两个,我们已经收到一条错误消息,应该将其删除。
如果特征 is 出现在 DataFrame
,但是,我们清除 Axes
并绘制它:
添加跨度选择器
跨度选择器 可用于允许用户选择数据范围并专注于它,根据该选择设置轴限制。 默认情况下,许多库都支持此功能,但不幸的是,Matplotlib 不支持,我们必须手动执行此操作。 此外,我们必须添加一个额外的 “重启” 按钮,如果我们想 缩小 以及。
添加一个 跨度选择器,我们不需要奉献一个全新的 Axes
对于它——我们可以将它附加到现有的,这很有意义。 当生成一个 SpanSelector
,我们提供 Axes
它属于,以及事件处理程序,其次是 'horizontal'
or 'vertical'
,它旋转 Axes
和 跨度选择器两者.
useblit
参数通常设置为 True
因为它提高了大多数后端的性能。 此外,我们还添加了一些样式属性,例如设置 alpha
作为跨度选择器创建的矩形的 0.5
和 facecolor
一个不错的 tab:blue
:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.widgets import SpanSelector
from matplotlib.widgets import Button fig, ax = plt.subplots()
fig.subplots_adjust(bottom=0.2) df = pd.read_csv('AmesHousing.csv') ax.scatter(x = df['Year Built'], y = df['Total Bsmt SF'], alpha = 0.6) class EventHandler: def select_horizontal(x, y): ax.set_xlim(x, y) plt.draw() def reset(self): ax.set_xlim(df['Year Built'].min(), df['Year Built'].max()) plt.draw span_horizontal = SpanSelector(ax, EventHandler.select_horizontal, 'horizontal', useblit=True, rectprops=dict(alpha=0.5, facecolor='tab:blue')) button_ax = plt.axes([0.7, 0.02, 0.2, 0.07])
button = Button(button_ax, 'Reset')
button.on_clicked(EventHandler.reset) plt.show()
运行它会生成一个图,我们可以在该图上选择跨度并通过将 Axes-limits 设置为提供的值来放大它们:
添加滑块
滑轨 允许用户通过滑动标记并选择一个值来直观地在多个值之间进行选择。 通常,滑块用于不断更新绘图上的某些值,例如其范围 or 甚至是一个功能。 例如,您可以通过滑块调整常数的值,从而影响依赖该常数的函数。
让我们编写一个脚本,允许我们通过滑块更改 Y 轴和 X 轴限制,这将让我们更改查看数据的角度:
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider fig, ax = plt.subplots()
fig.subplots_adjust(bottom=0.2, left=0.2) df = pd.read_csv('winequality-red.csv')
plot, = ax.plot(df['volatile acidity']) class EventHandler: def update(val): ax.set_ylim(0, yslider.val) ax.set_xlim(0, xslider.val) plt.draw() xslider_ax = plt.axes([0.35, 0.03, 0.5, 0.07])
xslider = Slider( ax=xslider_ax, label="X-limit", valmin=0, valmax=len(df['volatile acidity']), valinit=len(df['volatile acidity']), orientation="horizontal"
) yslider_ax = plt.axes([0.03, 0.2, 0.07, 0.5])
yslider = Slider( ax=yslider_ax, label="Y-limit", valmin=0, valmax=3, valinit=1.5, orientation="vertical"
) xslider.on_changed(EventHandler.update)
yslider.on_changed(EventHandler.update) plt.show()
我们调整了填充以允许滑块的左侧和底部 Axes
,并绘制了一个简单的线图。 添加一个滑块需要我们做一个 Axes
对于它,与大多数其他小部件一样,并将其分配给 ax
的论点 Slider
通过构造函数。 此外,我们可以设置滑块的最小值、最大值和初始值。 这些通常是动态范围,基于您绘制的数据,但也可以手动设置标量值。
最后, Slider
s 可以水平或垂直定向。 由于它们旨在通过鼠标滑动不断更新 - on_changed()
函数用于在用户输入时触发响应。 我们已经调整了 EventHandler
类与 update()
简单地调整 X 和 Y 限制值的函数 val
ue 的各个滑块。
运行此代码将生成一个带有两个滑块的图,我们可以使用它来更改 Axes
: