如何在Matplotlib柱状图中按双变量分组X轴(避免重复)
在Matplotlib柱状图中实现国家-城市的分组X轴标签
我需要在Matplotlib制作的柱状图X轴上按「国家、城市」两个变量分组,但当前代码会把两个变量拼接,导致国家名称重复,不符合需求。以下是相关数据代码、绘图代码,以及当前和期望的图表效果。
数据代码
import pandas as pd import random # 欧洲国家列表 paises_europa = ['España', 'Francia', 'Alemania', 'Italia', 'Reino Unido', 'Portugal', 'Países Bajos'] # 对应每个国家的城市列表 ciudades = [['Madrid', 'Barcelona'], ['París'], ['Berlín', 'Múnich', 'Hamburgo'], ['Roma', 'Milán'], ['Londres', 'Birmingham', 'Manchester'], ['Lisboa', 'Oporto'], ['Ámsterdam', 'Róterdam']] # 创建国家-城市-店铺数的关联列表 data = [] for pais, lista_ciudades in zip(paises_europa, ciudades): for ciudad in lista_ciudades: data.append((pais, ciudad, random.randint(5, 50))) # 生成DataFrame df = pd.DataFrame(data, columns=['Pais', 'Ciudad', 'Tiendas'])
原绘图代码
import matplotlib.pyplot as plt # 计算柱状图最大值 max_valor = df['Tiendas'].max() # 设置Y轴范围 y_min = 0 y_max = max_valor + 5 # 创建画布和坐标轴 fig, ax = plt.subplots(figsize=(12, 6)) # 定义柱状图颜色 bar_color = '#4a8a52' # 绘制柱状图 ax.bar(df.index, df['Tiendas'], color=bar_color) # 设置X轴刻度和标签(拼接国家和城市,导致重复) ax.set_xticks(df.index) ax.set_xticklabels([f'{pais} - {ciudad}' for pais, ciudad in zip(df['Pais'], df['Ciudad'])], rotation=90) # 设置Y轴范围 ax.set_ylim(y_min, y_max) # Y轴显示整数刻度 ax.yaxis.set_major_locator(plt.MaxNLocator(integer=True)) # 设置图表标题 plt.title('Count of Stores by Country and City') # 设置X轴标签 ax.set_xlabel('Country / City') # 设置Y轴标签 ax.set_ylabel('Stores') # 为柱状图添加数值标签 for i, val in enumerate(df['Tiendas']): ax.text(i, val + 1, str(val), ha='center', va='bottom', fontsize=10) # 调整布局 plt.tight_layout() # 显示图表 plt.show()
当前与期望效果
当前图表效果

期望图表效果

解决方案
要实现国家分组、仅显示一次国家名称的X轴布局,我们可以通过双X轴的方式实现:下方轴显示城市,上方轴按分组居中显示国家名称,避免重复。
修改后的完整绘图代码
import matplotlib.pyplot as plt import pandas as pd import random # ---------------------- 保留原数据生成逻辑 ---------------------- paises_europa = ['España', 'Francia', 'Alemania', 'Italia', 'Reino Unido', 'Portugal', 'Países Bajos'] ciudades = [['Madrid', 'Barcelona'], ['París'], ['Berlín', 'Múnich', 'Hamburgo'], ['Roma', 'Milán'], ['Londres', 'Birmingham', 'Manchester'], ['Lisboa', 'Oporto'], ['Ámsterdam', 'Róterdam']] data = [] for pais, lista_ciudades in zip(paises_europa, ciudades): for ciudad in lista_ciudades: data.append((pais, ciudad, random.randint(5, 50))) df = pd.DataFrame(data, columns=['Pais', 'Ciudad', 'Tiendas']) # ------------------------------------------------------------------ # 绘图核心逻辑 max_valor = df['Tiendas'].max() y_min = 0 y_max = max_valor + 5 fig, ax = plt.subplots(figsize=(12, 6)) bar_color = '#4a8a52' # 绘制柱状图 ax.bar(df.index, df['Tiendas'], color=bar_color) # ---------------------- 核心修改:分组X轴设置 ---------------------- # 1. 下方X轴:显示城市名称,优化旋转角度提升可读性 ax.set_xticks(df.index) ax.set_xticklabels(df['Ciudad'], rotation=45, ha='right') ax.set_xlabel('Ciudad') # 2. 上方X轴:显示国家分组,居中对齐对应城市组 ax2 = ax.twiny() country_positions = [] country_labels = [] current_idx = 0 # 计算每个国家分组的中心位置 for pais in paises_europa: city_count = len(df[df['Pais'] == pais]) center_pos = current_idx + (city_count - 1) / 2 country_positions.append(center_pos) country_labels.append(pais) current_idx += city_count # 设置上方X轴的刻度与标签 ax2.set_xticks(country_positions) ax2.set_xticklabels(country_labels) ax2.set_xlabel('País') # 隐藏上方X轴顶部边框,让布局更简洁 ax2.spines['top'].set_visible(False) # ------------------------------------------------------------------ # 其他绘图设置 ax.set_ylim(y_min, y_max) ax.yaxis.set_major_locator(plt.MaxNLocator(integer=True)) plt.title('Cantidad de Tiendas por País y Ciudad') ax.set_ylabel('Tiendas') # 添加柱状图数值标签 for i, val in enumerate(df['Tiendas']): ax.text(i, val + 1, str(val), ha='center', va='bottom', fontsize=10) plt.tight_layout() plt.show()
关键调整说明
- 拆分X轴为两级:下方显示城市,上方仅在对应城市组的中心位置显示一次国家名称,完全避免重复;
- 调整城市标签的旋转角度和对齐方式,比原90度旋转更易阅读;
- 保留原有的数值标签、Y轴整数刻度等实用设置,同时优化了图表标题和标签的语言(贴合数据中的西班牙语国家名称)。
内容的提问来源于stack exchange,提问作者Eduardo
相关产品推荐
相关产品推荐

