You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Pandas GroupBy组间顺序规则疑问及排序异常问题

按月份名称对Pandas DataFrame分组后保持月份顺序的解决方案

你遇到的问题在Pandas 0.20.3这个旧版本里是很典型的——虽然你已经按月份数字对原DataFrame排好了序,但GroupBy默认会按组键的字母顺序来排列最终的分组结果,而不是沿用原DataFrame中的组出现顺序。

为什么会这样?

在Pandas 0.23.0版本之前,GroupBy的默认行为是对组键进行排序(字符串类型就按字母序,数值类型按大小序)。文档里提到的“保留组内顺序”,指的是每个分组内部的行顺序和原DataFrame一致,但组与组之间的顺序是独立处理的,不会继承原DataFrame的排序。你的组键是字符串格式的月份缩写,所以自然会被按augdecjanmar的字母序排列。

解决方法

根据你的Pandas版本(0.20.3),这里提供几种可行的方案:

方法1:基于月份数字分组后替换索引

利用已经生成的Month_dig列分组,再把索引替换为对应的月份名称,这样就能保证顺序正确:

import pandas as pd

df = pd.DataFrame([["dec", 12], ["jan", 40], ["mar", 11], ["aug", 21], ["aug", 11], ["jan", 11], ["jan", 1]], columns=["Month", "Price"])
df["Month_dig"] = pd.to_datetime(df.Month, format='%b', errors='coerce').dt.month
df.sort_values(by="Month_dig", inplace=True)

# 按月份数字分组计算均值
total = df.groupby(df['Month_dig'])['Price'].mean()
# 获取排序后的月份名称列表,替换索引
sorted_months = df.drop_duplicates('Month_dig').sort_values('Month_dig')['Month'].values
total.index = sorted_months

print(total)

输出结果:

jan    17.333333
mar    11.000000
aug    16.000000
dec    12.000000
Name: Price, dtype: float64

方法2:将月份列转为分类类型(推荐)

Month列转换成有序分类,指定好月份的正确顺序,这样GroupBy就会遵循这个顺序来排列分组:

import pandas as pd

df = pd.DataFrame([["dec", 12], ["jan", 40], ["mar", 11], ["aug", 21], ["aug", 11], ["jan", 11], ["jan", 1]], columns=["Month", "Price"])
# 定义全年月份的正确顺序
month_order = ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec']
# 将Month列转为有序分类
df['Month'] = pd.Categorical(df['Month'], categories=month_order, ordered=True)

# 直接分组计算,结果会按分类顺序排列
total = df.groupby('Month')['Price'].mean()
print(total)

输出结果和预期完全一致,而且后续任何基于Month列的分组、排序操作都会自动遵循这个顺序,一劳永逸。

方法3:升级Pandas后使用sort=False参数

如果你可以升级到Pandas 0.23.0及以上版本,GroupBy新增了sort=False参数,开启后会保留原DataFrame中组的出现顺序:

import pandas as pd

df = pd.DataFrame([["dec", 12], ["jan", 40], ["mar", 11], ["aug", 21], ["aug", 11], ["jan", 11], ["jan", 1]], columns=["Month", "Price"])
df["Month_dig"] = pd.to_datetime(df.Month, format='%b', errors='coerce').dt.month
df.sort_values(by="Month_dig", inplace=True)

# 使用sort=False保留原组顺序
total = df.groupby(df['Month'], sort=False)['Price'].mean()
print(total)

这个方法最简单,但依赖版本升级。


内容的提问来源于stack exchange,提问作者Tai

火山引擎 最新活动