Pandas按月分组后序列排序错误及需新增近12个月数据求和功能的问题
Pandas按月分组后序列排序错误及需新增近12个月数据求和功能的问题
看起来你碰到了两个实际的小问题:分组后的月份顺序是按字母排的不是时间顺序,还有需要计算近12个月的数据总和。我来一步步帮你搞定~
一、修复月份排序错误的问题
你当前用dt.strftime("%b-%Y")得到的是类似Aug-2022的字符串,Pandas对字符串分组排序是按字母顺序来的(比如Aug因为首字母A在J前面,就排在Jul前面了),所以要让排序遵循实际的时间逻辑,有两种简单的解决方法:
方法1:用月份周期(Period)分组(推荐)
这种方法直接基于时间类型分组,天然按时间顺序排列,之后再格式化索引成你要的样式:
import pandas as pd import pyodbc # 先获取原始数据(你的原查询不变) monthSQL = pd.read_sql_query('SELECT SDATETIME,max(FE014BPV) as flow,max(FE011BPV) as steam FROM [SCADA].[dbo].[TOTALIZER] GROUP BY SDATETIME ORDER BY SDATETIME ASC', conn) # 按月份周期分组,自动按时间排序 monthdata = monthSQL.groupby(monthSQL['SDATETIME'].dt.to_period('M'), sort=True).sum() # 将索引格式化为"月-年"的字符串样式 monthdata.index = monthdata.index.strftime("%b-%Y") # 重置索引,让SDATETIME成为普通列,匹配你想要的输出格式 monthdata = monthdata.reset_index().rename(columns={'index': 'SDATETIME'}) print(monthdata)
运行后就能得到按May-2022 → Jun-2022 → Jul-2022 → Aug-2022 → Sep-2022顺序排列的结果。
方法2:字符串分组后转为时间类型再排序
如果你更习惯用字符串分组,可以先按字符串分组,再把索引转成时间类型排序,最后转回原格式:
monthSQL = pd.read_sql_query('SELECT SDATETIME,max(FE014BPV) as flow,max(FE011BPV) as steam FROM [SCADA].[dbo].[TOTALIZER] GROUP BY SDATETIME ORDER BY SDATETIME ASC', conn) # 先按字符串分组,关闭自动排序 monthdata = monthSQL.groupby(monthSQL['SDATETIME'].dt.strftime("%b-%Y"), sort=False).sum() # 将索引转为datetime对象,按时间排序 monthdata.index = pd.to_datetime(monthdata.index, format="%b-%Y") monthdata = monthdata.sort_index() # 转回"月-年"字符串格式,重置索引 monthdata.index = monthdata.index.strftime("%b-%Y") monthdata = monthdata.reset_index().rename(columns={'index': 'SDATETIME'}) print(monthdata)
二、新增近12个月数据求和的功能
这里分两种常见场景,你可以根据需求选择:
场景1:对分组后最近12个月份的汇总数据求和
如果你的数据已经按月分组完成,直接取最后12个月的汇总结果求和即可:
# 确保monthdata已经按时间顺序排列好(上面的代码已经处理) # 取最后12行数据(如果数据不足12个月则取全部) last_12_months = monthdata.tail(12) # 计算flow和steam的总和 total_last_12 = last_12_months[['flow', 'steam']].sum() # 整理成和原有数据一致的格式 total_df = pd.DataFrame({'SDATETIME': ['Last 12 Months Total'], 'flow': [round(total_last_12['flow'], 6)], # 保留6位小数和原数据一致 'steam': [round(total_last_12['steam'], 6)]}) # 把总和行追加到原有数据后面 final_data = pd.concat([monthdata, total_df], ignore_index=True) print(final_data)
场景2:基于原始数据中最新日期往前推12个月的所有数据求和
如果需要的是从最新记录日期倒推12个月内的所有原始数据总和,可以这样处理:
monthSQL = pd.read_sql_query('SELECT SDATETIME,max(FE014BPV) as flow,max(FE011BPV) as steam FROM [SCADA].[dbo].[TOTALIZER] GROUP BY SDATETIME ORDER BY SDATETIME ASC', conn) # 找到原始数据中的最新日期 latest_date = monthSQL['SDATETIME'].max() # 计算12个月前的日期 twelve_months_ago = latest_date - pd.DateOffset(months=12) # 筛选出这个时间范围内的数据 filtered_data = monthSQL[monthSQL['SDATETIME'] >= twelve_months_ago] # 计算总和 total_last_12 = filtered_data[['flow', 'steam']].sum() # 整理成输出格式 total_df = pd.DataFrame({'SDATETIME': ['Last 12 Months Total'], 'flow': [round(total_last_12['flow'], 6)], 'steam': [round(total_last_12['steam'], 6)]}) # 先处理好月份排序的主数据,再追加总和行 monthdata = monthSQL.groupby(monthSQL['SDATETIME'].dt.to_period('M'), sort=True).sum() monthdata.index = monthdata.index.strftime("%b-%Y") monthdata = monthdata.reset_index().rename(columns={'index': 'SDATETIME'}) final_data = pd.concat([monthdata, total_df], ignore_index=True) print(final_data)
备注:内容来源于stack exchange,提问作者Rushabh Bhosale




