如何使用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
相关产品推荐
相关产品推荐

