Matplotlib雷达图xtick标签遮挡截断问题解决求助
Matplotlib雷达图长标签重叠/截断问题的两种解决方案
我从Stack Overflow找到一个Python Matplotlib雷达图绘制示例,当前xtick标签过长,伸入雷达图内部被线条遮挡,部分标签还被图表区域截断。试过plt.tight_layout()无效,现提供两种解决方案:
方案1:将标签移至雷达图外侧(优先方案)
通过调整刻度标签的内边距和对齐方式,让标签完全显示在雷达图外部,彻底避免与图表重叠。关键修改点:
- 增大标签与雷达图的间距(
pad参数) - 根据极坐标角度为每个标签设置对应的水平/垂直对齐方式,保证排版整齐
- 扩大图表尺寸,为外侧标签预留足够显示空间
修改后的完整代码:
import pandas as pd import numpy as np from matplotlib import pyplot as plt def create_radar(df, *, id_column, title=None, max_values=None, padding=1.25, width=12, length=12): cm = 1/2.54 categories = df._get_numeric_data().columns.tolist() data = df[categories].to_dict(orient='list') ids = df[id_column].tolist() if max_values is None: max_values = {key: padding*max(value) for key, value in data.items()} normalized_data = {key: np.array(value) / max_values[key] for key, value in data.items()} num_vars = len(data.keys()) tiks = list(data.keys()) angles = np.linspace(0, 2 * np.pi, num_vars, endpoint=False).tolist() fig, ax = plt.subplots(figsize=(width*cm, length*cm), subplot_kw=dict(polar=True)) for i, model_name in enumerate(ids): values = [normalized_data[key][i] for key in data.keys()] actual_values = [data[key][i] for key in data.keys()] values += values[:1] # 闭合雷达图 ax.plot(angles + [angles[0]], values, label=model_name) ax.fill(angles + [angles[0]], values, alpha=0.15) for _x, _y, t in zip(angles, values[:-1], actual_values): if isinstance(t, float) and t > 1: t = f'{t:.0f}' elif isinstance(t, float) and t <= 1: t = f'{t:.2%}' else: str(t) if i % 2 == 0: ax.text(_x, _y+.02, t, size='xx-small', va='top', ha='right') else: ax.text(_x, _y-.02, t, size='xx-small', va='bottom', ha='left') ax.fill(angles + [angles[0]], np.ones(num_vars + 1), alpha=0.05) ax.set_yticklabels([]) ax.set_xticks(angles) # 核心修改:调整标签位置与对齐方式 ax.tick_params(axis='x', pad=15) # 增大标签与雷达图的距离 tick_labels = [] for angle, label in zip(angles, tiks): # 根据角度动态设置对齐方式 if angle == 0: ha, va = 'left', 'center' elif np.pi/2 < angle < np.pi: ha, va = 'right', 'top' elif angle == np.pi: ha, va = 'right', 'center' elif np.pi < angle < 3*np.pi/2: ha, va = 'right', 'bottom' elif angle == 3*np.pi/2: ha, va = 'center', 'bottom' else: ha, va = 'left', 'top' tick_labels.append(plt.Text(angle, 1, label, ha=ha, va=va, size="xx-small")) ax.set_xticklabels(tick_labels) ax.legend(loc='lower right') if title is not None: plt.suptitle(title) plt.tight_layout(pad=2) plt.show() create_radar( pd.DataFrame({ 'x': [*'abcde'], 'supeeeer long category name 1': [10,11,12,13,14], 'supeeeer long category name 2': [0.1, 0.3, 0.4, 0.1, 0.9], 'supeeeer long category name 4': [1e5, 2e5, 3.5e5, 8e4, 5e4], 'supeeeer long category name 5': [9, 12, 5, 2, 0.2], 'supeeeer long category name 6': [1,1,1,1,5] }), id_column='x', )
方案2:将标签置于图表前端并完整显示
若不想移动标签位置,可通过设置标签层级让其显示在图表线条上方,同时调整布局避免标签被截断。关键修改点:
- 给标签设置更高的
zorder层级,确保显示在图表元素之上 - 降低雷达图线条和填充的层级,避免遮挡标签
- 调整子图位置,为标签预留显示空间
修改后的完整代码:
import pandas as pd import numpy as np from matplotlib import pyplot as plt def create_radar(df, *, id_column, title=None, max_values=None, padding=1.25, width=12, length=12): cm = 1/2.54 categories = df._get_numeric_data().columns.tolist() data = df[categories].to_dict(orient='list') ids = df[id_column].tolist() if max_values is None: max_values = {key: padding*max(value) for key, value in data.items()} normalized_data = {key: np.array(value) / max_values[key] for key, value in data.items()} num_vars = len(data.keys()) tiks = list(data.keys()) angles = np.linspace(0, 2 * np.pi, num_vars, endpoint=False).tolist() fig, ax = plt.subplots(figsize=(width*cm, length*cm), subplot_kw=dict(polar=True)) for i, model_name in enumerate(ids): values = [normalized_data[key][i] for key in data.keys()] actual_values = [data[key][i] for key in data.keys()] values += values[:1] # 闭合雷达图 # 降低图表元素层级,避免遮挡标签 ax.plot(angles + [angles[0]], values, label=model_name, zorder=1) ax.fill(angles + [angles[0]], values, alpha=0.15, zorder=1) for _x, _y, t in zip(angles, values[:-1], actual_values): if isinstance(t, float) and t > 1: t = f'{t:.0f}' elif isinstance(t, float) and t <= 1: t = f'{t:.2%}' else: str(t) if i % 2 == 0: ax.text(_x, _y+.02, t, size='xx-small', va='top', ha='right', zorder=2) else: ax.text(_x, _y-.02, t, size='xx-small', va='bottom', ha='left', zorder=2) ax.fill(angles + [angles[0]], np.ones(num_vars + 1), alpha=0.05, zorder=0) ax.set_yticklabels([]) ax.set_xticks(angles) # 核心修改:设置标签层级与对齐方式 tick_labels = [] for angle, label in zip(angles, tiks): if angle == 0: ha, va = 'right', 'center' elif np.pi/2 < angle < np.pi: ha, va = 'left', 'top' elif angle == np.pi: ha, va = 'left', 'center' elif np.pi < angle < 3*np.pi/2: ha, va = 'left', 'bottom' elif angle == 3*np.pi/2: ha, va = 'center', 'top' else: ha, va = 'right', 'top' # 设置标签zorder为10,确保显示在最上层 tick_labels.append(plt.Text(angle, 1, label, ha=ha, va=va, size="xx-small", zorder=10)) ax.set_xticklabels(tick_labels) ax.legend(loc='lower right', zorder=11) if title is not None: plt.suptitle(title) # 调整子图位置,预留标签显示空间 plt.subplots_adjust(left=0.15, right=0.15, top=0.9, bottom=0.15) plt.show() create_radar( pd.DataFrame({ 'x': [*'abcde'], 'supeeeer long category name 1': [10,11,12,13,14], 'supeeeer long category name 2': [0.1, 0.3, 0.4, 0.1, 0.9], 'supeeeer long category name 4': [1e5, 2e5, 3.5e5, 8e4, 5e4], 'supeeeer long category name 5': [9, 12, 5, 2, 0.2], 'supeeeer long category name 6': [1,1,1,1,5] }), id_column='x', )
内容的提问来源于stack exchange,提问作者Maeaex1
相关产品推荐
相关产品推荐

