如何为Matplotlib分组柱状图添加轻微3D效果?附3D实现方案
问题说明
我有如下DataFrame数据:
YEAR CODE ANOM% 0 1964 152101 -85.294118 1 1965 152101 -62.352941 2 1965 152111 -85.862069 3 1966 152101 -81.352941 4 1966 152111 -77.873563 .. ... ... ... 108 2020 152101 -38.823529 109 2021 152101 -59.352941 110 2021 152111 74.137931 111 2022 152101 -95.235294 112 2022 152111 -76.436782 [113 rows x 3 columns]
已经用以下代码生成透视表:
df.sort_values('YEAR', axis=0, ascending=True, inplace=True, ignore_index=True) years = df["YEAR"].unique() prv= df["CODE"].unique() # 分类转换 df["YEAR"] = pd.Categorical(df["YEAR"], categories=years) df["CODE"] = pd.Categorical(df["CODE"], categories=prv) # 创建透视表 df_pivot = pd.pivot_table( df, values="ANOM%", index="YEAR", columns="CODE", aggfunc=np.sum ) df_pivot.columns = df_pivot.columns.add_categories('AVERAGE') df_pivot['AVERAGE']=df_pivot.mean(axis=1).astype(int)
接着用这段代码绘制分组柱状图时,尝试通过depthshade=True添加轻微3D效果,结果报错:AttributeError: Rectangle.set() got an unexpected keyword argument 'depthshade'。当前使用Matplotlib 3.7.0版本,需要两个解决方案:
- 如何实现柱状图的轻微3D效果(非完整3D)
- 若必须绘制完整3D柱状图,该怎么操作
解决方案
一、模拟轻微3D效果(2D伪立体)
depthshade是Matplotlib 3D绘图的专属参数,2D柱状图不支持,所以会报错。要做轻微立体效果,用2D图形模拟即可,推荐两种方法:
方法1:叠加阴影柱
在主柱子下方绘制一层颜色更深、轻微偏移的柱子,模拟立体侧面,简单易实现:
import matplotlib.pyplot as plt import numpy as np fig = plt.figure('Grouped Bars', figsize=(16,10), dpi=100) ax1 = fig.add_axes([0.1, 0.1, 0.85, 0.75]) bar_width = 0.2 x = np.arange(len(df_pivot.index)) n_cols = len(df_pivot.columns) # 遍历每一列数据,先画阴影再画主柱 for i, col in enumerate(df_pivot.columns): # 阴影柱:向右向下偏移一点,用深灰色 ax1.bar(x + i*bar_width + 0.02, df_pivot[col], width=bar_width, color='#333333', alpha=0.5, zorder=0) # 主柱:正常位置,保留原颜色设置 color = 'black' if col != 'AVERAGE' else 'red' ax1.bar(x + i*bar_width, df_pivot[col], width=bar_width, color=color, edgecolor='black', linewidth=2, alpha=0.7, zorder=1) # 配置轴标签和图例 ax1.set_xticks(x + bar_width*(n_cols-1)/2) ax1.set_xticklabels(df_pivot.index, fontsize=12) ax1.legend(df_pivot.columns, fontsize=12) plt.show()
方法2:手动绘制斜面矩形
用matplotlib.patches模块绘制柱子的主体和斜面,模拟更精细的立体效果:
from matplotlib.patches import Rectangle, Polygon fig, ax1 = plt.subplots(figsize=(16,10), dpi=100) x = np.arange(len(df_pivot.index)) bar_width = 0.2 depth = 0.03 # 斜面的深度,控制立体程度 for i, col in enumerate(df_pivot.columns): # 主色和深色(用于斜面) color = 'black' if col != 'AVERAGE' else 'red' dark_color = '#333333' if col != 'AVERAGE' else '#cc0000' # 遍历每一年的数据,绘制单个柱子 for j, val in enumerate(df_pivot[col]): # 绘制柱子主体 main_rect = Rectangle((x[j]+i*bar_width, 0), bar_width, val, color=color, edgecolor='black', linewidth=1, alpha=0.7) ax1.add_patch(main_rect) # 绘制右侧斜面 if val > 0: right_verts = [ (x[j]+i*bar_width+bar_width, 0), (x[j]+i*bar_width+bar_width+depth, depth), (x[j]+i*bar_width+bar_width+depth, val+depth), (x[j]+i*bar_width+bar_width, val) ] else: # 负值柱子的斜面方向调整 right_verts = [ (x[j]+i*bar_width+bar_width, 0), (x[j]+i*bar_width+bar_width+depth, -depth), (x[j]+i*bar_width+bar_width+depth, val-depth), (x[j]+i*bar_width+bar_width, val) ] right_poly = Polygon(right_verts, color=dark_color, alpha=0.5) ax1.add_patch(right_poly) # 绘制顶部斜面 top_verts = [ (x[j]+i*bar_width, val), (x[j]+i*bar_width+depth, val+depth), (x[j]+i*bar_width+bar_width+depth, val+depth), (x[j]+i*bar_width+bar_width, val) ] top_poly = Polygon(top_verts, color=dark_color, alpha=0.5) ax1.add_patch(top_poly) # 调整轴范围和标签 ax1.set_xlim(x[0]-0.5, x[-1]+n_cols*bar_width+0.5) ax1.set_ylim(df_pivot.min().min()-10, df_pivot.max().max()+10) ax1.set_xticks(x + bar_width*(n_cols-1)/2) ax1.set_xticklabels(df_pivot.index, fontsize=12) ax1.legend(df_pivot.columns, fontsize=12) plt.show()
二、绘制完整3D柱状图
如果需要真正的3D效果,使用Matplotlib的3D轴组件,需要处理坐标映射:
import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D fig = plt.figure(figsize=(16,10), dpi=100) ax = fig.add_subplot(111, projection='3d') # 生成x、y轴的网格坐标 x = np.arange(len(df_pivot.index)) y = np.arange(len(df_pivot.columns)) xpos, ypos = np.meshgrid(x, y) xpos = xpos.flatten() ypos = ypos.flatten() zpos = np.zeros_like(xpos) # 柱子底部在z=0处 # 设置柱子的宽度、深度和高度 dx = dy = 0.5 dz = df_pivot.values.flatten() # 匹配每根柱子的颜色 colors = ['black' if col != 'AVERAGE' else 'red' for col in df_pivot.columns] colors = np.repeat(colors, len(df_pivot.index)) # 绘制3D柱状图 ax.bar3d(xpos, ypos, zpos, dx, dy, dz, color=colors, alpha=0.7, edgecolor='black') # 配置轴标签 ax.set_xticks(x + dx/2) ax.set_xticklabels(df_pivot.index, fontsize=10) ax.set_yticks(y + dy/2) ax.set_yticklabels(df_pivot.columns, fontsize=10) ax.set_zlabel('ANOM%', fontsize=12) # 调整视角,让显示更清晰 ax.view_init(elev=20., azim=-30) plt.show()
内容的提问来源于stack exchange,提问作者Javier
相关产品推荐
相关产品推荐

