[Python] Text aligned below each 3d bar in matplotlib

Discussão em 'Python' iniciado por Stack, Maio 29, 2025.

  1. Stack

    Stack Membro Participativo

    I want to position each text below each bar diagonally, because, as you can see in the image, some texts are far from the bars or inside the bars.

    I need to align the text diagonally with each of the bars

    Only the text 'hd_aaaa_DD1-MMMM-01' is the way I want it, so I need all the other texts to be the same way.

    screenshot of a 3d bar chart showing inconsistent spacing between text and bars

    import matplotlib.pyplot as plt
    from matplotlib.lines import Line2D
    import numpy as np
    import pandas as pd

    color_dict = {'used': '#A54836', 'free': '#5375D4'}

    xy_ticklabel_color, xlabel_color, grand_totals_color, legend_color, grid_color, datalabels_color = '#101628',"#101628","#101628","#101628", "#C8C9C9", "#757C85"

    df = pd.DataFrame([
    ['hd_xxxxxx-ppp-d2', 355, 237, 592],
    ['hd_disk_vvvvvvvv', 0, 184, 184],
    ['hd_aaaa-C_DDDDDDDDDDD', 0, 240, 240],
    ['hd_AAAAAAA_DDDDDDDDDDD', 1, 9, 10],
    ['hd_xxxxxx-ppp-d1', 870, 342, 1212],
    ['hd_aaaa_CC2-SSSS-01', 0, 9, 9],
    ['hd_aaaa_DD1-MMMM-01', 387, 286, 673],
    ['hd_wwww_DD2-MMMM-01', 437, 230, 667],
    ['hd_disk_bbbbbbbbbb', 0, 179, 179],
    ])
    df.columns = ['disk_pool_name', 'used_space_kb', 'free_space_kb', 'total_capacity']

    used = df[['disk_pool_name', 'used_space_kb']].rename(columns={"used_space_kb": "TB"})
    used['status'] = 'used'
    free = df[['disk_pool_name', 'free_space_kb']].rename(columns={"free_space_kb": "TB"})
    free['status'] = 'free'

    teste = pd.concat([used, free], axis=0, ignore_index=True, sort=False)
    teste = teste.sort_values(['disk_pool_name', 'status'], ascending=[True, False])

    # map colors
    teste['colors']= teste.status.map(color_dict)

    dp_names = teste.disk_pool_name.unique()
    status = ['used', 'free']
    nr_status = len(status)

    sub_totals = df.sort_values(['disk_pool_name'])['total_capacity']
    colors = teste.colors

    dx = 100
    dy = 100

    dz = teste.TB.to_list()

    xs_separation = 250
    ys_separation = 125

    xs = np.repeat(np.arange(0, len(dp_names)) * xs_separation,len(status)).tolist()
    ys = np.repeat(np.arange(0, len(dp_names)) * ys_separation,len(status)).tolist()

    zs = np.array(teste.groupby('disk_pool_name', sort=False).TB.apply(list).tolist()) #convert to a numpy 2d array
    zs = np.cumsum(zs, axis=1)[:,:nr_status-1] #accumulate sum, remove last item
    zs = np.insert(zs, 0, 0, axis=1).flatten().tolist()

    fig = plt.figure(figsize=(10, 8))
    ax = fig.add_subplot(1, 1, 1, projection='3d')

    ax.bar3d(xs, ys, zs, dx, dy, dz, color=colors, label=status)

    for x, y, z, zdir in zip(xs, ys, zs, dz):
    label_point = zdir if zdir > 0 else ''
    ax.text(x - 1.5, y, z + zdir / 2, label_point, size=10, ha="right", color=datalabels_color)

    lines = [Line2D([0], [0], color=c, marker='s',linestyle='', markersize=10,) for c in reversed(colors.unique())]
    labels = teste.status.unique()
    plt.legend(lines, reversed(labels), labelcolor=legend_color,
    prop=dict(weight='bold', size=12),
    bbox_to_anchor=(0.5, -0.05), loc="lower center",
    ncols=2,frameon=False, fontsize=14)

    for i, (dp, sub_total) in enumerate(zip(dp_names, sub_totals)):
    ax.text(xs[i * nr_status] - 100, ys[i * nr_status] - 710, z=-200, zdir='y',
    s=f"{dp}", color=xy_ticklabel_color, weight="bold", fontsize=7)

    ax.text(xs[i * nr_status] - 130, ys[i * nr_status] + 260, z=zs[i * nr_status] + sub_total + 2, s=f"{sub_total}", fontsize=10, weight="bold", color=grand_totals_color, bbox=dict(facecolor='none', edgecolor='#EBEDEE', boxstyle='round,pad=0.3'))

    ax.set_aspect("equal")


    I tried to make changes with the zdir parameter, but I can't get each of the texts below each of the bars so that they are diagonal and close to the bars.

    Continue reading...

Compartilhe esta Página