如何用Pandas绘制3年周期内36个月份的企业营收趋势图
解决按自然月份(含年份)展示36个月营收趋势的问题
我明白你的问题了——原来的分组逻辑只按月份数字聚合,会把2016年10月、2017年10月、2018年10月的营收合并到一起,自然只能看到12个汇总结果,丢失了年份维度,没法呈现完整的36个月趋势。下面是具体的修正方案:
核心思路
要保留每个自然月的独立性,必须同时按年份+月份分组,或者直接用Pandas的dt.to_period('M')方法提取「年月周期」作为分组键,这样每个YYYY-MM都是唯一的分组单元,不会跨年份合并。
步骤1:预处理数据(确保日期格式正确)
首先确认你的Datetime列是Pandas的datetime类型,如果不是先转换:
import pandas as pd import matplotlib.pyplot as plt # 转换日期列(如果原始数据是字符串格式) df['Datetime'] = pd.to_datetime(df['Datetime'])
步骤2:按年月周期分组汇总
用dt.to_period('M')直接生成「年月」分组键,这是最简洁的方式:
# 按公司+年月周期分组,计算每月总营收 monthly_sums = df.groupby( ['Company Name', df['Datetime'].dt.to_period('M')] )['PaidTotal'].sum().reset_index(name='MonthlyTotal') # 将周期类型转为字符串(方便绘图时显示) monthly_sums['Year-Month'] = monthly_sums['Datetime'].astype(str)
这个操作会把每一行的日期转为对应的YYYY-MM格式(比如2016-10-14变成2016-10),每个自然月都会作为独立的分组,不会和其他年份的同月合并。
步骤3:绘制36个月趋势图
确保每个公司的数据按时间排序后再绘图,这样趋势是连续的:
# 创建画布 fig, ax = plt.subplots(figsize=(12, 6)) # 遍历每个公司绘制趋势线 for company in monthly_sums['Company Name'].unique(): # 筛选当前公司数据并按时间排序 company_data = monthly_sums[monthly_sums['Company Name'] == company].sort_values('Datetime') # 绘制折线图 company_data.plot( x='Year-Month', y='MonthlyTotal', label=company, ax=ax, marker='o' # 添加标记点更清晰 ) # 美化图表 plt.title('3-Year Monthly Revenue Trend by Company') plt.xlabel('Year-Month') plt.ylabel('Total Paid Revenue') plt.xticks(rotation=45, ha='right') # 旋转x轴标签避免重叠 plt.legend(title='Company') plt.tight_layout() plt.show()
额外优化:补全缺失月份(可选)
如果你的数据中有某个公司某月份没有营收记录(比如示例中CompanyA 2016年11月没有数据),上面的结果会跳过该月份。如果需要展示连续的36个月(缺失月份填0),可以用pivot_table生成宽表:
# 生成宽表,缺失值填充0 pivot_df = monthly_sums.pivot( index='Datetime', columns='Company Name', values='MonthlyTotal' ).fillna(0).astype(int) # 直接绘制连续趋势 pivot_df.plot(figsize=(12,6), marker='o') plt.title('Continuous 3-Year Monthly Revenue Trend') plt.xlabel('Year-Month') plt.ylabel('Total Paid Revenue') plt.xticks(rotation=45) plt.tight_layout() plt.show()
灵活扩展:任意月份周期
如果想要查看特定时间范围的趋势(比如2017-01到2018-12的24个月),只需先筛选数据再分组:
# 筛选目标时间范围 mask = (df['Datetime'] >= '2017-01-01') & (df['Datetime'] <= '2018-12-31') filtered_df = df[mask] # 后续分组和绘图逻辑和上面一致 monthly_sums_filtered = filtered_df.groupby( ['Company Name', filtered_df['Datetime'].dt.to_period('M')] )['PaidTotal'].sum().reset_index(name='MonthlyTotal')
内容的提问来源于stack exchange,提问作者Azm




