
在数据可视化中,我们经常需要展示不同类别数据的分布。Matplotlib作为Python中强大的绘图库,提供了多种图表类型,其中水平条形图(barh)常用于比较不同类别的数据量。然而,当需求不仅仅是展示总计数,而是需要将条形图内部的每个独立数据点(例如,每天的每一次检查结果)都根据其特定状态进行颜色映射时,传统的堆叠条形图可能无法直接满足。
例如,设想一个场景:我们需要可视化一系列按日期排序的检查结果,每个结果都有一个状态('0'代表成功,'1'代表失败)。如果使用Matplotlib的barh函数,通常的做法是统计每天成功和失败的总数,然后将它们堆叠起来。这会生成一个显示每天成功和失败总量的条形图,但无法直观地展示当天每一次检查的具体状态序列(例如,“绿红绿红红”)。原始需求是希望能够为每个独立的检查结果绘制一个色块,并根据其状态(例如,'0'为绿色,'1'为红色)进行着色,形成一个类似序列的视觉效果。
以下是原始Matplotlib尝试的代码示例,它展示了按天统计并堆叠的条形图:
import matplotlib.pyplot as plt
from collections import defaultdict
def generate_graph_stacked(day_check_data):
"""
生成按天统计成功/失败总数的堆叠水平条形图。
"""
daily_data = defaultdict(lambda: {'0': 0, '1': 0})
for timestamp, status in day_check_data:
# 提取日期,例如 '2023-01-01'
day = timestamp.split(' ')[0]
daily_data[day][status] += 1
days = sorted(list(daily_data.keys()), reverse=True) # 按日期倒序
zeros = [daily_data[day]['0'] for day in days] # 状态'0'(成功)的数量
ones = [daily_data[day]['1'] for day in days] # 状态'1'(错误)的数量
fig, ax = plt.subplots(figsize=(10, 8))
# 绘制堆叠条形图
bar1 = ax.barh(days, zeros, 1.0, label='Success', color='green')
bar2 = ax.barh(days, ones, 1.0, label='Errors', color='red', left=zeros)
ax.set_xlabel('Checks Count')
ax.set_ylabel('Day')
ax.set_title('Daily Check Status (Stacked)')
ax.legend()
plt.tight_layout()
plt.savefig('stacked_graph.png')
plt.show()
# 示例数据
day_check_data = [
("2023-01-01 12:30:00", '0'), ("2023-01-01 13:00:00", '1'), ("2023-01-01 14:00:00", '0'),
("2023-01-02 14:45:00", '1'), ("2023-01-02 15:00:00", '0'), ("2023-01-02 16:00:00", '1'),
("2023-01-03 10:15:00", '0'), ("2023-01-03 11:00:00", '1'), ("2023-01-03 12:00:00", '0'),
("2023-01-03 13:00:00", '1'), ("2023-01-03 14:00:00", '0'),
]
# generate_graph_stacked(day_check_data) # 取消注释可运行此部分这段代码会生成一个按日期堆叠的条形图,绿色部分代表成功,红色部分代表错误。然而,它并不能像用户期望的那样,将每一天的每一次检查结果以独立的、颜色映射的方块形式展现出来。
为了实现这种高度定制化的“每个检查一个色块”的视觉效果,我们可以转向更底层的绘图工具,例如Python的tkinter库。tkinter提供了创建图形用户界面(GUI)的能力,其中的Canvas组件允许我们在画布上绘制各种图形元素,如矩形、线条、文本等,从而实现像素级的精细控制。
以下是使用tkinter实现所需可视化效果的详细步骤和代码:
首先,我们需要准备好包含时间戳和状态的数据。这里沿用原始数据格式:一个包含元组的列表,每个元组包含时间戳字符串和状态字符串('0'或'1')。
day_check_data = [
("2023-01-01 12:30:00", '0'),
("2023-01-02 14:45:00", '1'),
("2023-01-03 10:15:00", '0'),
("2023-02-03 12:30:00", '1'),
("2023-02-04 14:45:00", '0'),
("2023-02-05 10:15:00", '1'),
("2023-03-05 12:30:00", '0'),
("2023-03-06 14:45:00", '1'),
("2023-03-07 10:15:00", '0'),
("2023-04-07 12:30:00", '1'),
("2023-04-08 14:45:00", '0'),
("2023-04-09 10:15:00", '1'),
]由于日期标签可能较长,水平放置会占用大量空间。为了美观和紧凑,我们可以将日期文本垂直显示。这里定义一个简单的辅助函数将字符串转换为多行垂直文本。
def vertical_text(text: str) -> str:
"""
将字符串转换为每个字符一行的垂直文本。
"""
return '\n'.join(list(text))创建一个Tkinter根窗口(tk.Tk())和Canvas组件,这是我们绘图的区域。
import tkinter as tk
root = tk.Tk()
root.geometry('800x600') # 设置窗口初始大小
root.title('Daily Check Status Visualization')
canvas = tk.Canvas(root, width=780, height=580, bg='white') # 设置画布大小和背景色
canvas.pack(padx=10, pady=10) # 将画布放置到窗口中核心逻辑是遍历day_check_data中的每个条目,根据其状态绘制一个矩形,并为其添加日期标签。我们需要计算每个矩形的位置,确保它们横向排列且有适当的间隔。
# 定义绘图参数
x_start = 50 # 第一个矩形的起始X坐标
y_start = 50 # 矩形的起始Y坐标
bar_width = 40 # 每个矩形的宽度
bar_height = 100 # 每个矩形的高度
space = 5 # 矩形之间的水平间距
label_offset_y = 20 # 标签相对于矩形底部的Y偏移
current_x = x_start # 当前绘制位置的X坐标
for day_data in day_check_data:
timestamp = day_data[0].split(' ')[0] # 提取日期部分
value = day_data[1] # 提取状态值
# 根据状态值确定颜色
# 原始需求是 0s green 1s red,但提供的答案代码是 1 red 0 green
# 这里我们遵循答案代码的颜色映射:'1'为红色(错误),'0'为绿色(成功)
color = 'red' if value == '1' else 'green'
# 绘制矩形
canvas.create_rectangle(
current_x, y_start,
current_x + bar_width, y_start + bar_height,
fill=color,
outline='black' # 添加边框使矩形更清晰
)
# 绘制日期标签
# 标签位于矩形下方,并使用垂直文本
canvas.create_text(
current_x + bar_width / 2, # 标签X坐标居中
y_start + bar_height + label_offset_y, # 标签Y坐标
text=vertical_text(timestamp),
font='Consolas 10 bold',
anchor='n' # 文本锚点设置为顶部,确保文本从顶部向下扩展
)
# 更新下一个矩形的X坐标
current_x += bar_width + space
# 添加图例(可选,但对于理解颜色很重要)
# 可以手动绘制图例,或者在Tkinter中创建简单的标签
canvas.create_rectangle(x_start, y_start + bar_height + label_offset_y + 80, x_start + 20, y_start + bar_height + label_offset_y + 100, fill='green', outline='black')
canvas.create_text(x_start + 25, y_start + bar_height + label_offset_y + 90, text='Status 0 (Success)', anchor='w', font='Consolas 10')
canvas.create_rectangle(x_start, y_start + bar_height + label_offset_y + 110, x_start + 20, y_start + bar_height + label_offset_y + 130, fill='red', outline='black')
canvas.create_text(x_start + 25, y_start + bar_height + label_offset_y + 120, text='Status 1 (Error)', anchor='w', font='Consolas 10')
# 运行Tkinter事件循环
root.mainloop()将上述所有部分组合起来,形成一个完整的Tkinter应用程序:
import tkinter as tk
def vertical_text(text: str) -> str:
"""
将字符串转换为每个字符一行的垂直文本。
"""
return '\n'.join(list(text))
# 示例数据
day_check_data = [
("2023-01-01 12:30:00", '0'),
("2023-01-02 14:45:00", '1'),
("2023-01-03 10:15:00", '0'),
("2023-02-03 12:30:00", '1'),
("2023-02-04 14:45:00", '0'),
("2023-02-05 10:15:00", '1'),
("2023-03-05 12:30:00", '0'),
("2023-03-06 14:45:00", '1'),
("2023-03-07 10:15:00", '0'),
("2023-04-07 12:30:00", '1'),
("2023-04-08 14:45:00", '0'),
("2023-04-09 10:15:00", '1'),
]
# 创建Tkinter根窗口
root = tk.Tk()
root.geometry('800x600') # 设置窗口初始大小
root.title('Daily Check Status Visualization (Tkinter)')
# 创建Canvas画布
canvas = tk.Canvas(root, width=780, height=580, bg='white')
canvas.pack(padx=10, pady=10)
# 定义绘图参数
x_start = 50
y_start = 50
bar_width = 40
bar_height = 100
space = 5
label_offset_y = 20
current_x = x_start
# 遍历数据并绘制每个检查的状态矩形和日期标签
for day_data in day_check_data:
timestamp = day_data[0].split(' ')[0]
value = day_data[1]
# 根据状态值确定颜色
color = 'red' if value == '1' else 'green'
# 绘制矩形
canvas.create_rectangle(
current_x, y_start,
current_x + bar_width, y_start + bar_height,
fill=color,
outline='black'
)
# 绘制日期标签
canvas.create_text(
current_x + bar_width / 2,
y_start + bar_height + label_offset_y,
text=vertical_text(timestamp),
font='Consolas 10 bold',
anchor='n'
)
current_x += bar_width + space
# 添加图例
legend_y_start = y_start + bar_height + label_offset_y + 80
canvas.create_rectangle(x_start, legend_y_start, x_start + 20, legend_y_start + 20, fill='green', outline='black')
canvas.create_text(x_start + 25, legend_y_start + 10, text='Status 0 (Success)', anchor='w', font='Consolas 10')
canvas.create_rectangle(x_start, legend_y_start + 30, x_start + 20, legend_y_start + 50, fill='red', outline='black')
canvas.create_text(x_start + 25, legend_y_start + 40, text='Status 1 (Error)', anchor='w', font='Consolas 10')
# 启动Tkinter事件循环
root.mainloop()通过上述Tkinter方案,我们成功地将每个独立的检查结果以颜色映射的方块形式直观地展现出来,满足了对数据精细化展示的特定需求,突破了传统堆叠条形图在表达个体状态序列方面的局限。
以上就是Matplotlib与Tkinter:实现精细化状态映射的自定义条形图的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号