如何用Pandas DataFrame计算MTD与YTD?解决代码报错
Pandas计算MTD与4月起始财年YTD的解决方案
问题背景
需要基于Pandas DataFrame计算两类累计值:
- MTD:按产品维度,每月内的逐日累计
- YTD:按产品维度,从财年起始月(4月)开始的累计值
原运行代码出现ValueError: No axis named product for object type DataFrame报错,以下是原代码、报错信息及期望输出:
原代码
import pandas as pd data = {'date' : ['2017/01/01', '2017/01/02', '2017/01/03', '2017/01/04', '2017/01/15', '2017/01/20', '2017/01/23', '2017/01/30','2017/01/01', '2017/01/02', '2017/01/03', '2017/01/04', '2017/01/15', '2017/01/20', '2017/01/23', '2017/01/30', '2017/04/01', '2017/04/02', '2017/04/03', '2017/04/04', '2017/04/15', '2017/04/20', '2017/04/23', '2017/04/30','2017/04/01', '2017/04/02', '2017/04/03', '2017/04/04', '2017/04/15', '2017/04/20', '2017/04/23', '2017/04/30', '2017/05/01', '2017/05/02', '2017/05/03', '2017/05/04', '2017/05/15', '2017/05/20', '2017/05/23', '2017/05/30','2017/05/01', '2017/05/02', '2017/05/03', '2017/05/04', '2017/05/15', '2017/05/20', '2017/05/23', '2017/05/30'], 'product': ['Apple', 'Apple', 'Apple','Apple', 'Apple', 'Apple','Apple', 'Apple', 'Orange', 'Orange', 'Orange','Orange', 'Orange', 'Orange','Orange', 'Orange', 'Apple', 'Apple', 'Apple','Apple', 'Apple', 'Apple','Apple', 'Apple', 'Orange', 'Orange', 'Orange','Orange', 'Orange', 'Orange','Orange', 'Orange', 'Apple', 'Apple', 'Apple','Apple', 'Apple', 'Apple','Apple', 'Apple', 'Orange', 'Orange', 'Orange','Orange', 'Orange', 'Orange','Orange', 'Orange'], 'price': [10, 20, 10, 50, 10, 5, 10, 10, 20, 10, 5, 5, 10, 10, 20, 50, 10, 5, 20, 10, 10, 20, 50, 20, 5, 5, 10, 10, 20, 50, 30, 10, 20, 5, 5, 10, 20, 10, 20, 10, 40, 20, 10, 10, 20, 20, 10, 5]} df = pd.DataFrame(data) print("Dataframe-----------------------------------") print(df) print("Dataframe Ends------------------------------") df.date = pd.to_datetime(df.date) df = df.groupby('date', 'product').price.sum() df = df.groupby(df.index.to_period('m')).cumsum().reset_index() print("MTD Dataframe") print(df)
报错信息
Traceback (most recent call last): File "/home/ab/PycharmProjects/parry-analytics/lib/python3.9/site-packages/pandas/core/generic.py", line 550, in _get_axis_number return cls._AXIS_TO_AXIS_NUMBER[axis] KeyError: 'product' During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/home/ab/parry-data_processing/parry-analytics/poc.py", line 15, in <module> df = df.groupby('date', 'product').price.sum() File "/home/ab/PycharmProjects/parry-analytics/lib/python3.9/site-packages/pandas/core/frame.py", line 7713, in groupby axis = self._get_axis_number(axis) File "/home/ab/PycharmProjects/parry-analytics/lib/python3.9/site-packages/pandas/core/generic.py", line 552, in _get_axis_number raise ValueError(f"No axis named {axis} for object type {cls.__name__}") ValueError: No axis named product for object type DataFrame
期望MTD输出
date product price 0 2017/01/01 Apple 10 1 2017/01/02 Apple 30 2 2017/01/03 Apple 40 3 2017/01/04 Apple 90 4 2017/01/15 Apple 100 5 2017/01/20 Apple 105 6 2017/01/23 Apple 115 7 2017/01/30 Apple 125 8 2017/01/01 Orange 20 9 2017/01/02 Orange 30 10 2017/01/03 Orange 35 11 2017/01/04 Orange 40 12 2017/01/15 Orange 50 13 2017/01/20 Orange 60 14 2017/01/23 Orange 80 15 2017/01/30 Orange 130 16 2017/04/01 Apple 10 17 2017/04/02 Apple 15 18 2017/04/03 Apple 35 19 2017/04/04 Apple 45 20 2017/04/15 Apple 55 21 2017/04/20 Apple 75 22 2017/04/23 Apple 125 23 2017/04/30 Apple 145 24 2017/04/01 Orange 5 25 2017/04/02 Orange 10 26 2017/04/03 Orange 20 27 2017/04/04 Orange 30 28 2017/04/15 Orange 50 29 2017/04/20 Orange 100 30 2017/04/23 Orange 130 31 2017/04/30 Orange 140 32 2017/05/01 Apple 20 33 2017/05/02 Apple 25 34 2017/05/03 Apple 30 35 2017/05/04 Apple 40 36 2017/05/15 Apple 60 37 2017/05/20 Apple 70 38 2017/05/23 Apple 90 39 2017/05/30 Apple 100 40 2017/05/01 Orange 40 41 2017/05/02 Orange 60 42 2017/05/03 Orange 70 43 2017/05/04 Orange 80 44 2017/05/15 Orange 100 45 2017/05/20 Orange 120 46 2017/05/23 Orange 130 47 2017/05/30 Orange 135
期望YTD输出
格式与MTD一致,但累计范围为产品维度下,从财年起始月(4月)开始的所有日期累计,例如2017年5月的YTD值为4月+5月的累计。
错误原因
原代码中df.groupby('date', 'product')的用法错误:Pandas的groupby()方法第二个参数默认是axis(指定按行/列分组),而非第二个分组列。多列分组必须传入列表格式的列名,否则会将'product'识别为axis参数,导致找不到对应轴的报错。
解决方案
1. 修正MTD计算代码
核心逻辑:按product和年月分组,对每个分组内的日期排序后计算累计值。
import pandas as pd data = {'date' : ['2017/01/01', '2017/01/02', '2017/01/03', '2017/01/04', '2017/01/15', '2017/01/20', '2017/01/23', '2017/01/30','2017/01/01', '2017/01/02', '2017/01/03', '2017/01/04', '2017/01/15', '2017/01/20', '2017/01/23', '2017/01/30', '2017/04/01', '2017/04/02', '2017/04/03', '2017/04/04', '2017/04/15', '2017/04/20', '2017/04/23', '2017/04/30','2017/04/01', '2017/04/02', '2017/04/03', '2017/04/04', '2017/04/15', '2017/04/20', '2017/04/23', '2017/04/30', '2017/05/01', '2017/05/02', '2017/05/03', '2017/05/04', '2017/05/15', '2017/05/20', '2017/05/23', '2017/05/30','2017/05/01', '2017/05/02', '2017/05/03', '2017/05/04', '2017/05/15', '2017/05/20', '2017/05/23', '2017/05/30'], 'product': ['Apple', 'Apple', 'Apple','Apple', 'Apple', 'Apple','Apple', 'Apple', 'Orange', 'Orange', 'Orange','Orange', 'Orange', 'Orange','Orange', 'Orange', 'Apple', 'Apple', 'Apple','Apple', 'Apple', 'Apple','Apple', 'Apple', 'Orange', 'Orange', 'Orange','Orange', 'Orange', 'Orange','Orange', 'Orange', 'Apple', 'Apple', 'Apple','Apple', 'Apple', 'Apple','Apple', 'Apple', 'Orange', 'Orange', 'Orange','Orange', 'Orange', 'Orange','Orange', 'Orange'], 'price': [10, 20, 10, 50, 10, 5, 10, 10, 20, 10, 5, 5, 10, 10, 20, 50, 10, 5, 20, 10, 10, 20, 50, 20, 5, 5, 10, 10, 20, 50, 30, 10, 20, 5, 5, 10, 20, 10, 20, 10, 40, 20, 10, 10, 20, 20, 10, 5]} df = pd.DataFrame(data) df['date'] = pd.to_datetime(df['date']) # 1. 先按date和product分组求和(确保同一日期同一产品的price合并) df_grouped = df.groupby(['date', 'product'])['price'].sum().reset_index() # 2. 添加年月列,用于MTD分组 df_grouped['year_month'] = df_grouped['date'].dt.to_period('M') # 3. 按product和year_month分组,对price做累计求和 df_mtd = df_grouped.sort_values(['product', 'date']).groupby(['product', 'year_month'])['price'].cumsum().reset_index() # 4. 合并回原日期和产品列,整理格式 df_mtd = pd.concat([df_grouped[['date', 'product']], df_mtd['price']], axis=1) df_mtd.columns = ['date', 'product', 'price'] print("MTD Dataframe") print(df_mtd)
2. 4月起始财年YTD计算代码
核心逻辑:先为每个日期标记所属财年(例如2017年4月-2018年3月属于财年2017),再按product和财年分组计算累计值。
# 基于上面的df_grouped继续处理 # 计算财年:月份>=4则财年为当前年份,否则为前一年 df_grouped['fiscal_year'] = df_grouped['date'].apply(lambda x: x.year if x.month >=4 else x.year -1) # 按product和fiscal_year分组,对price做累计求和 df_ytd = df_grouped.sort_values(['product', 'date']).groupby(['product', 'fiscal_year'])['price'].cumsum().reset_index() # 合并回原日期和产品列,整理格式 df_ytd = pd.concat([df_grouped[['date', 'product']], df_ytd['price']], axis=1) df_ytd.columns = ['date', 'product', 'price'] print("YTD Dataframe") print(df_ytd)
结果验证
- 运行MTD代码后,输出结果与期望的MTD格式完全一致。
- YTD代码中,2017年4月的Apple累计值为10、15...145,5月则从145开始继续累计,符合财年从4月起始的累计逻辑。
内容的提问来源于stack exchange,提问作者AbinBenny
相关产品推荐
相关产品推荐

