在 Python 中,对 category 类型的数据进行排序通常涉及两种情况:

- 按类别名称的字母顺序排序:这是最常见的需求,例如按 "A", "B", "C" 的顺序。
- 按自定义的业务逻辑顺序排序:例如按 "Low", "Medium", "High" 或 "Small", "Medium", "Large" 的特定顺序。
这两种情况的实现方法有所不同,下面我将详细解释,并提供使用 Pandas 和 Python 原生 enum 的示例。
按类别名称的字母顺序排序
这是 Pandas Categorical 类型的默认行为,当你创建一个 Categorical 对象时,如果未指定 categories 的顺序,它会自动按类别值的唯一值进行字典序(lexicographical order)排序。
使用 Pandas
import pandas as pd
import numpy as np
# 1. 创建数据
data = np.random.choice(['C', 'A', 'B', 'D'], size=10)
print("原始数据:")
print(data)
# 2. 创建 Categorical 对象 (默认按字母顺序排序)
# categories 会自动排序为 ['A', 'B', 'C', 'D']
cat_data = pd.Categorical(data, ordered=True)
print("\n默认创建的 Categorical (已排序):")
print(cat_data)
print("Categories:", cat_data.categories)
# 3. 如果你的数据已经是 Series,可以直接使用 .astype()
s = pd.Series(data)
cat_s = s.astype('category')
print("\n从 Series 转换的 Categorical (已排序):")
print(cat_s)
print("Categories:", cat_s.cat.categories)
输出:
原始数据:
['B' 'C' 'A' 'D' 'C' 'A' 'B' 'A' 'D' 'B']
默认创建的 Categorical (已排序):
['B', 'C', 'A', 'D', 'C', 'A', 'B', 'A', 'D', 'B']
Categories (4, object): ['A', 'B', 'C', 'D']
从 Series 转换的 Categorical (已排序):
0 B
1 C
2 A
3 D
4 C
5 A
6 B
7 A
8 D
9 B
dtype: category
Categories (4, object): ['A', 'B', 'C', 'D']
如何排序一个包含 Categorical 数据的 DataFrame?

当你对包含 Categorical 列的 DataFrame 进行排序时,Pandas 会智能地按照该列的 categories 顺序进行排序,而不是简单的字母顺序。
import pandas as pd
df = pd.DataFrame({
'product': ['Apple', 'Banana', 'Cherry', 'Date', 'Apple'],
'size': pd.Categorical(['M', 'S', 'L', 'M', 'S'], categories=['S', 'M', 'L'], ordered=True)
})
print("原始 DataFrame:")
print(df)
print("\n'size' 列的 categories:", df['size'].cat.categories)
# 按 'size' 列排序
# 排序顺序将是 S -> M -> L
df_sorted = df.sort_values('size')
print("\n按 'size' 列排序后的 DataFrame:")
print(df_sorted)
输出:
原始 DataFrame:
product size
0 Apple M
1 Banana S
2 Cherry L
3 Date M
4 Apple S
'size' 列的 categories: Index(['S', 'M', 'L'], dtype='object')
按 'size' 列排序后的 DataFrame:
product size
1 Banana S
4 Apple S
0 Apple M
3 Date M
2 Cherry L
按自定义的业务逻辑顺序排序
在很多实际应用中,我们需要自定义类别顺序,"Low, Medium, High" 或 "Small, Medium, Large",这时,我们必须在创建 Categorical 对象时显式地指定 categories 参数。
使用 Pandas
这是实现自定义排序的关键。
import pandas as pd
import numpy as np
# 1. 创建数据
data = np.random.choice(['High', 'Low', 'Medium'], size=10)
print("原始数据:")
print(data)
# 2. 创建 Categorical 对象,并指定自定义的 categories 顺序
# ordered=True 表示这是一个有序的分类,可以进行 <, >, <=, >= 等比较
custom_order = ['Low', 'Medium', 'High']
cat_data = pd.Categorical(data, categories=custom_order, ordered=True)
print("\n指定了自定义顺序的 Categorical:")
print(cat_data)
print("Categories:", cat_data.categories)
# 3. 在 DataFrame 中使用并排序
df = pd.DataFrame({
'task_id': range(10),
'priority': cat_data
})
print("\n原始 DataFrame:")
print(df)
# 按 'priority' 列排序
# 排序顺序将是 Low -> Medium -> High
df_sorted = df.sort_values('priority')
print("\n按自定义 'priority' 顺序排序后的 DataFrame:")
print(df_sorted)
输出:
原始数据:
['High' 'Low' 'Medium' 'High' 'Medium' 'Low' 'High' 'Medium' 'Low' 'Medium']
指定了自定义顺序的 Categorical:
['High', 'Low', 'Medium', 'High', 'Medium', 'Low', 'High', 'Medium', 'Low', 'Medium']
Categories (3, object): ['Low' < 'Medium' < 'High']
原始 DataFrame:
task_id priority
0 0 High
1 1 Low
2 2 Medium
3 3 High
4 4 Medium
5 5 Low
6 6 High
7 7 Medium
8 8 Low
9 9 Medium
按自定义 'priority' 顺序排序后的 DataFrame:
task_id priority
1 1 Low
5 5 Low
8 8 Low
2 2 Medium
4 4 Medium
7 7 Medium
9 9 Medium
0 0 High
3 3 High
6 6 High
高级技巧:使用 pd.CategoricalDtype
为了使代码更清晰、更具可重用性,推荐使用 pd.CategoricalDtype 来定义分类类型。
import pandas as pd
# 1. 定义分类类型
# 这样做的好处是可以重用这个类型定义
size_dtype = pd.CategoricalDtype(categories=['XS', 'S', 'M', 'L', 'XL'], ordered=True)
# 2. 创建数据
data = {'product': ['T-Shirt', 'Jeans', 'Socks', 'Jacket'],
'size': ['M', 'L', 'S', 'XL']}
df = pd.DataFrame(data)
# 3. 使用 .astype() 应用分类类型
df['size'] = df['size'].astype(size_dtype)
print("DataFrame with CategoricalDtype:")
print(df)
print("\n'size' 列的 dtype:", df['size'].dtype)
# 4. 排序
df_sorted = df.sort_values('size')
print("\n按 'size' 排序后的 DataFrame:")
print(df_sorted)
输出:
DataFrame with CategoricalDtype:
product size
0 T-Shirt M
1 Jeans L
2 Socks S
3 Jacket XL
'size' 列的 dtype: category
Categories (5, object): ['XS' < 'S' < 'M' < 'L' < 'XL']
按 'size' 排序后的 DataFrame:
product size
2 Socks S
0 T-Shirt M
1 Jeans L
3 Jacket XL
使用 Python enum 进行排序
如果你的类别是固定的、有限的集合,并且希望它们在代码层面就有明确的顺序,使用 Python 的 Enum 是一个非常好的选择。Enum 成员本身就有定义顺序。
from enum import Enum
import pandas as pd
# 1. 定义一个枚举类,成员的顺序就是它们的排序顺序
class Priority(Enum):
LOW = 1
MEDIUM = 2
HIGH = 3
# 2. 创建数据
data = {'task': ['Task A', 'Task B', 'Task C', 'Task D'],
'priority': [Priority.HIGH, Priority.LOW, Priority.MEDIUM, Priority.HIGH]}
df = pd.DataFrame(data)
print("DataFrame with Enum:")
print(df)
print("\n'priority' 列的类型:", df['priority'].dtype)
# 3. 排序
# Pandas 可以直接按 Enum 的值进行排序
df_sorted = df.sort_values('priority', key=lambda x: x.map(lambda e: e.value))
print("\n按 'priority' 的 Enum 值排序后的 DataFrame:")
print(df_sorted)
输出:
DataFrame with Enum:
task priority
0 Task A HIGH
1 Task B LOW
2 Task C MEDIUM
3 Task D HIGH
'priority' 列的类型: object
按 'priority' 的 Enum 值排序后的 DataFrame:
task priority
1 Task B LOW
2 Task C MEDIUM
0 Task A HIGH
3 Task D HIGH
这里我们使用了 sort_values 的 key 参数,它允许我们提供一个函数来在排序之前转换值。lambda x: x.map(lambda e: e.value) 的意思是:对于 'priority' 列中的每一个元素 e,获取其 .value (即 1, 2, 或 3),然后根据这些值进行排序。
| 需求 | 推荐方法 | 示例代码 |
|---|---|---|
| 按字母顺序排序 | 使用 Pandas Categorical,不指定 categories,让其自动排序。 |
pd.Categorical(data) 或 s.astype('category') |
| 按自定义顺序排序 | 使用 Pandas Categorical,并在创建时显式指定 categories 参数。 |
pd.Categorical(data, categories=['Low', 'High', 'Medium']) |
| 代码清晰、可重用 | 使用 pd.CategoricalDtype 定义类型,然后通过 .astype() 应用。 |
dtype = pd.CategoricalDtype(...), df['col'] = df['col'].astype(dtype) |
| 类别固定且代表状态 | 使用 Python Enum,利用其成员的天然顺序。 |
class Priority(Enum): ..., df.sort_values('col', key=lambda x: x.map(lambda e: e.value)) |
选择哪种方法取决于你的具体需求和代码风格,对于数据分析,Pandas 的 Categorical 类型是处理此类问题的标准且最强大的工具。
