如何基于月度活跃/逾期统计DataFrame生成无小数占比的100%堆叠条形图?
轻松实现100%堆叠条形图的简便方法
嘿,这事儿用Python的pandas+可视化工具就能快速搞定,分两步走就行,完全不用复杂操作!我给你捋捋具体步骤:
第一步:生成每月活跃/逾期统计DataFrame
首先得把原始数据按月份聚合出活跃数和逾期数。假设你的原始数据集有日期列(比如date)、活跃标记(比如is_active,1代表活跃,0代表不活跃)、逾期标记(比如is_overdue,1代表逾期)。用pandas的resample或者groupby就能一键统计:
import pandas as pd # 先把日期列转成datetime格式(如果还没转的话) df['date'] = pd.to_datetime(df['date']) # 按月份分组,统计每月活跃数和逾期数 monthly_stats = df.resample('M', on='date').agg( 每月活跃数=('is_active', 'sum'), # sum就是统计活跃用户的数量 每月逾期数=('is_overdue', 'sum') ).reset_index() # 把日期转成「年-月」格式,x轴显示更友好 monthly_stats['月份'] = monthly_stats['date'].dt.strftime('%Y-%m')
要是你的活跃/逾期判断逻辑不一样(比如是从状态字段判断),只需要调整agg里的函数就行,比如用lambda x: (x['user_status'] == 'active').sum()这种,灵活得很。
第二步:生成带整数标注的100%堆叠条形图
这里给你两种方案,选你顺手的来:
方案1:用Matplotlib(基础通用)
手动计算占比,然后堆叠绘图,还能精准控制标注显示整数:
import matplotlib.pyplot as plt # 提取需要的数据 months = monthly_stats['月份'] active_counts = monthly_stats['每月活跃数'] overdue_counts = monthly_stats['每月逾期数'] total_counts = active_counts + overdue_counts # 每月总用户数 # 转成整数百分比(避免小数) active_pct = (active_counts / total_counts * 100).astype(int) overdue_pct = (overdue_counts / total_counts * 100).astype(int) # 开始画图 plt.figure(figsize=(10, 6)) # 先画活跃用户占比,再画逾期的叠在上面 bar_active = plt.bar(months, active_pct, label='活跃用户占比') bar_overdue = plt.bar(months, overdue_pct, bottom=active_pct, label='逾期用户占比') # 给每个条形加整数标注(占比) for rect in bar_active: height = rect.get_height() plt.text(rect.get_x() + rect.get_width()/2., height/2, f'{height}%', ha='center', va='center', fontsize=10) for rect in bar_overdue: height = rect.get_height() bottom_pos = rect.get_y() plt.text(rect.get_x() + rect.get_width()/2., bottom_pos + height/2, f'{height}%', ha='center', va='center', fontsize=10) # 要是你想标注数量而不是占比,换成这段代码就行: # for idx, (a, o) in enumerate(zip(active_counts, overdue_counts)): # plt.text(idx, a/2, f'{a}', ha='center', va='center', fontsize=10) # plt.text(idx, a + o/2, f'{o}', ha='center', va='center', fontsize=10) plt.title('每月活跃/逾期用户100%堆叠占比图', fontsize=12) plt.xlabel('月份', fontsize=10) plt.ylabel('占比(%)', fontsize=10) plt.legend() plt.xticks(rotation=45) plt.tight_layout() # 自动调整布局,避免标签被截断 plt.show()
方案2:用Plotly Express(更快捷,带交互)
不想手动算占比?用Plotly一键生成,还能自动加标注,交互性拉满:
import plotly.express as px # 把宽表转成长表(Plotly更适合这种格式) monthly_long = monthly_stats.melt( id_vars='月份', value_vars=['每月活跃数', '每月逾期数'], var_name='用户类型', value_name='数量' ) # 生成100%堆叠条形图,直接显示整数数量 fig = px.bar( monthly_long, x='月份', y='数量', color='用户类型', text='数量', # 显示数量,精准整数 barmode='relative' # 这个参数就是开启100%堆叠 ) # 要是想显示占比,先提前计算整数占比列: # monthly_long['占比(%)'] = ( # monthly_long['数量'] / monthly_long.groupby('月份')['数量'].transform('sum') * 100 # ).astype(int) # fig = px.bar(monthly_long, x='月份', y='占比(%)', color='用户类型', text='占比(%)', barmode='relative') # 调整样式,让标注在条形内部 fig.update_layout( title='每月活跃/逾期用户100%堆叠图', xaxis_title='月份', yaxis_title='统计数量' ) fig.update_traces(textposition='inside') fig.show()
小提醒
- 如果活跃和逾期不是互斥的(比如有些用户既活跃又逾期),那得调整统计逻辑,比如统计「活跃且未逾期」「活跃且逾期」的数量,这样堆叠起来才是100%的总用户数。
- 所有标注都是整数的关键:用
astype(int)把占比转成整数,或者直接用原始的整数统计数量,完全不会出现小数~
内容的提问来源于stack exchange,提问作者Agustina
相关产品推荐
相关产品推荐

