如何修正Matplotlib 3D折线图中标签位置以实现预期效果
如何修正Matplotlib 3D折线图中标签位置以实现预期效果
我有一段生成3D折线图的代码,尝试给折线添加标签,但标签位置总是不符合预期:
import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D import numpy as np # Data names = [ "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L" ] wins = [ [0, 14, 20, 24, 29, 33, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39], [0, 7, 13, 17, 23, 27, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30], [0, 5, 8, 11, 15, 16, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19], [0, 7, 11, 17, 20, 25, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29], [0, 9, 14, 22, 29, 36, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [0, 6, 10, 16, 20, 24, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29], [0, 7, 13, 20, 26, 31, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34], [0, 10, 13, 18, 24, 29, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33], [0, 5, 11, 13, 16, 21, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26], [0, 12, 15, 18, 21, 25, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30], [0, 9, 11, 12, 17, 20, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26], [0, 15, 20, 25, 27, 33, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37], ] weeks = [f"Week{i+1}" for i in range(18)] # X-axis categories (nb of weeks) # Convert names into numerical indices for the Y-axis x = np.arange(len(weeks)) # X-axis y = np.arange(len(names)) # Y-axis wins_array = np.array(wins) # wins accumulated per weeks and per names # Create a 3D plot fig = plt.figure(figsize=(18, 18)) ax = fig.add_subplot(111, projection='3d') # Plot each line and add labels for i, y_week in enumerate(y): ax.plot(x, wins_array[i], zs=y_week, zdir='x', label=weeks[i]) # Add shaded area below the line ax.bar(x, wins_array[i], zs=y_week, zdir='x', alpha=0.1) # Add labels directly on the curves for j, my_label in enumerate(wins_array[i]): ax.text(i, my_label, y_week, f"{my_label}", color="red", fontsize=8, ha="center") # Customize labels ax.set_zlabel('Wins') # Z-axis is wins ax.set_xticks(y) # X-ticks are names ax.set_xticklabels(names, rotation=45) # Name labels on X-axis ax.set_yticks(x) # Y-ticks are weeks ax.set_yticklabels(weeks) # Week labels on Y-axis plt.show()
当前的效果:
我想要实现的效果:
请问如何修改代码来达到预期效果?
问题分析与修正方案
你的问题主要出在3D文本的坐标顺序和标签逻辑上,我帮你一步步调整:
1. 修正3D文本的坐标顺序
Matplotlib的ax.text()方法参数顺序是(x, y, z),但你之前完全搞混了坐标对应关系:
- X轴对应
names(即代码中的y数组元素) - Y轴对应
weeks(即代码中的x数组元素) - Z轴对应
wins(即wins_array中的数值)
所以你之前的ax.text(i, my_label, y_week)顺序错误,需要调整为符合轴定义的坐标。
2. 调整标签逻辑(贴合预期图)
你原来给每个数据点都加了红色标签,而预期图是在每条折线末端添加对应的名称(A、B等),所以我们不需要循环每个点,只在折线的最后一个位置添加标签即可,还能避免画面杂乱。
修正后的完整代码
import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D import numpy as np # Data names = [ "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L" ] wins = [ [0, 14, 20, 24, 29, 33, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39], [0, 7, 13, 17, 23, 27, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30], [0, 5, 8, 11, 15, 16, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19], [0, 7, 11, 17, 20, 25, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29], [0, 9, 14, 22, 29, 36, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42], [0, 6, 10, 16, 20, 24, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29], [0, 7, 13, 20, 26, 31, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34], [0, 10, 13, 18, 24, 29, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33], [0, 5, 11, 13, 16, 21, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26], [0, 12, 15, 18, 21, 25, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30], [0, 9, 11, 12, 17, 20, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26], [0, 15, 20, 25, 27, 33, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37], ] weeks = [f"Week{i+1}" for i in range(18)] # X-axis categories (nb of weeks) # 明确轴对应关系:y是名称索引(X轴),x是周数索引(Y轴) y = np.arange(len(names)) # X轴:名称的索引 x = np.arange(len(weeks)) # Y轴:周数的索引 wins_array = np.array(wins) # Z轴:累计获胜数 # 创建3D画布 fig = plt.figure(figsize=(18, 18)) ax = fig.add_subplot(111, projection='3d') # 遍历每个名称对应的折线 for i, name_idx in enumerate(y): # 绘制折线:zdir='x'表示X轴对应name_idx ax.plot(x, wins_array[i], zs=name_idx, zdir='x', label=names[i]) # 添加折线下方的阴影区域 ax.bar(x, wins_array[i], zs=name_idx, zdir='x', alpha=0.1) # 在折线最后一个点的右侧添加名称标签 last_week = x[-1] last_win = wins_array[i][-1] ax.text(name_idx, last_week + 0.5, last_win, names[i], color="black", fontsize=10, ha="center", va="center") # 自定义轴标签 ax.set_zlabel('Wins') # Z轴:获胜数 ax.set_xticks(y) # X轴刻度:名称索引 ax.set_xticklabels(names, rotation=45) # X轴刻度标签:名称 ax.set_yticks(x) # Y轴刻度:周数索引 ax.set_yticklabels(weeks) # Y轴刻度标签:周数 plt.show()
关键修正点说明
- 调整了
ax.text()的坐标顺序,让标签对应正确的3D轴位置,同时给Y轴坐标加了0.5,让标签稍微靠右,避免和折线重叠 - 只在每条折线的末端添加名称标签,和你预期的效果一致
- 修正了折线的
label为对应的名称,如需图例可以取消注释ax.legend() - 调整了标签的颜色和字号,提升可读性
这样修改后,你的3D折线图标签位置就会和预期效果匹配啦!
备注:内容来源于stack exchange,提问作者Ma Poub




