You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Plotly绘制按分位数分组着色的带标注直方图

自定义直方图实现(Matplotlib 版)

Matplotlib 是面向Python的跨平台数据可视化与图形绘制库,支持高度自定义配置。
Matplotlib 自定义能力极强,以下是基于Matplotlib实现的自定义直方图:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
import matplotlib.cm as cm
import matplotlib.ticker as ticker
from matplotlib.patches import Rectangle
from matplotlib.gridspec import GridSpec
from matplotlib.patches import Polygon
from matplotlib.patches import ConnectionPatch

def customized_Histogram(df, j):
    """
    Generate Histogram

    Parameters:
    ****************

    df:
        pandas dataframe

    j(str):
        column name in str

    ****************
    Generates Advanced Histogram
    """

    # Colours for different percentiles
    perc_25_colour = 'gold'
    perc_50_colour = 'mediumaquamarine'
    perc_75_colour = 'deepskyblue'
    perc_95_colour = 'peachpuff'

    # Plot the Histogram from the random data
    fig, ax = plt.subplots(figsize=(14,8))

    # '''
    # counts  = numpy.ndarray of count of data ponts for each bin/column in the histogram
    # bins    = numpy.ndarray of bin edge/range values
    # patches = a list of Patch objects.
    #         each Patch object contains a Rectnagle object. 
    #         e.g. Rectangle(xy=(-2.51953, 0), width=0.501013, height=3, angle=0)
    # '''
    counts, bins, patches = ax.hist(df[j], facecolor=perc_50_colour, edgecolor='gray')


    # Set the ticks to be at the edges of the bins.
    ax.set_xticks(bins.round(2))
    plt.xticks(rotation=70)

    # Set the graph title and axes titles
    plt.title(f'Distribution of {j}', fontsize=20)
    plt.ylabel('Count', fontsize=15)
    plt.xlabel(j, fontsize=15)

    # Change the colors of bars at the edges
    twentyfifth, seventyfifth, ninetyfifth = np.percentile(df[j], [25, 75, 95])

    for patch, leftside, rightside in zip(patches, bins[:-1], bins[1:]):

        if rightside < twentyfifth:
            patch.set_facecolor(perc_25_colour)
        elif leftside > ninetyfifth:
            patch.set_facecolor(perc_95_colour)
        elif leftside > seventyfifth:
            patch.set_facecolor(perc_75_colour)

    # Calculate bar centre to display the count of data points and %
    bin_x_centers = 0.5 * np.diff(bins) + bins[:-1]
    bin_y_centers = ax.get_yticks()[1] * 0.25

    # Display the the count of data points and % for each bar in histogram
    for i in range(len(bins)-1):
        bin_label = "{0:,}".format(counts[i]) + "  ({0:,.2f}%)".format((counts[i]/counts.sum())*100)
        plt.text(bin_x_centers[i],
                  bin_y_centers, 
                  bin_label, 
                  rotation=90, 
                  rotation_mode='anchor')

    # Annotation for bar values
    ax.annotate('Each bar shows count and percentage of total',
                xy=(.80,.30), 
                xycoords='figure fraction',
                horizontalalignment='center', 
                verticalalignment='bottom',
                fontsize=10, 
                bbox=dict(boxstyle="round", 
                          fc="white"),
                rotation=-90)

    #create legend
    handles = [Rectangle((0,0),1,1,color=c,ec="k") for c in [
                                                             perc_25_colour, 
                                                             perc_50_colour, 
                                                             perc_75_colour, 
                                                             perc_95_colour
                                                             ]
                ]
    labels= ["0-25 Percentile","25-50 Percentile", "50-75 Percentile", ">95 Percentile"]
    plt.legend(handles, labels, bbox_to_anchor=(0.5, 0., 0.80, 0.99))


    # fig.savefig("filename.jpg",dpi=150, bbox_inches='tight')
    plt.show()

测试调用代码:

import seaborn as sns
tips = sns.load_dataset("tips")

customized_Histogram(tips, "total_bill")

自定义直方图效果:
自定义直方图效果图


Plotly 版实现方案

该自定义直方图核心特性包括:按分位数区间给柱子分组着色、每个柱子标注对应数据量和占比、自定义图例、X轴刻度对齐分箱边缘等,以下是完整实现代码:

import numpy as np
import plotly.graph_objects as go
import seaborn as sns

def customized_Histogram_plotly(df, col_name):
    # 分位数对应配色,和Matplotlib版保持一致
    perc_25_colour = 'gold'
    perc_50_colour = 'mediumaquamarine'
    perc_75_colour = 'deepskyblue'
    perc_95_colour = 'peachpuff'
    
    # 计算分位数、分箱结果
    q25, q75, q95 = np.percentile(df[col_name], [25, 75, 95])
    counts, bins = np.histogram(df[col_name])
    bin_centers = (bins[:-1] + bins[1:]) / 2
    total_count = counts.sum()
    
    # 给每个分箱分配颜色
    colors = []
    for left, right in zip(bins[:-1], bins[1:]):
        if right < q25:
            colors.append(perc_25_colour)
        elif left > q95:
            colors.append(perc_95_colour)
        elif left > q75:
            colors.append(perc_75_colour)
        else:
            colors.append(perc_50_colour)
    
    # 初始化画布
    fig = go.Figure()
    
    # 绘制直方图柱子
    fig.add_trace(go.Bar(
        x=bin_centers,
        y=counts,
        width=np.diff(bins),
        marker=dict(color=colors, line=dict(color='gray', width=1)),
        showlegend=False
    ))
    
    # 添加柱子标注:计数+占比,垂直显示
    annotations = []
    for x, count in zip(bin_centers, counts):
        label = f"{count:,} ({count/total_count*100:.2f}%)"
        annotations.append(dict(
            x=x,
            y=count.max()*0.05,
            text=label,
            textangle=-90,
            showarrow=False,
            font=dict(size=10)
        ))
    
    # 添加侧边注释
    annotations.append(dict(
        x=1.1,
        y=0.3,
        xref='paper',
        yref='paper',
        text='Each bar shows count and percentage of total',
        textangle=-90,
        showarrow=False,
        font=dict(size=10),
        bgcolor='white',
        bordercolor='black',
        borderpad=4,
        borderwidth=1
    ))
    
    # 添加自定义图例
    legend_items = [
        ("0-25 Percentile", perc_25_colour),
        ("25-50 Percentile", perc_50_colour),
        ("50-75 Percentile", perc_75_colour),
        (">95 Percentile", perc_95_colour)
    ]
    for label, color in legend_items:
        fig.add_trace(go.Bar(
            x=[None],
            y=[None],
            marker=dict(color=color, line=dict(color='black', width=1)),
            name=label,
            showlegend=True
        ))
    
    # 设置布局
    fig.update_layout(
        title=f'Distribution of {col_name}',
        title_font=dict(size=20),
        xaxis_title=col_name,
        xaxis_title_font=dict(size=15),
        yaxis_title='Count',
        yaxis_title_font=dict(size=15),
        xaxis=dict(
            tickmode='array',
            tickvals=bins.round(2),
            tickangle=70
        ),
        width=1400,
        height=800,
        annotations=annotations,
        legend=dict(
            orientation='h',
            yanchor='bottom',
            y=1.02,
            xanchor='right',
            x=1
        )
    )
    
    fig.show()

# 测试调用
tips = sns.load_dataset("tips")
customized_Histogram_plotly(tips, "total_bill")

内容的提问来源于stack exchange,提问作者Ailurophile

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.25 15:36:03