将分组ID转换为分类编码
场景说明:例如我们在处理游戏数据的时候,经常会有“队伍ID”,“每局游戏ID”等等这些“分组ID”特征,这些ID有两个特点:
- 存在大量重复
- 字符串类型(object类型),长度一般很长(比较占内存)
下面以《绝地求生》游戏数据为例,讲解分组ID数据的处理办法:
# 导入数据基本处理阶段需要用到的api
import pandas as pd
import numpy as np
data = pd.read_csv(r'D:\learn\000人工智能数据大全\黑马数据\机器学习\pubg(竞赛)参考模型绝地求生案例\data\train_V2.csv')
data.head()
Id | groupId | matchId | assists | boosts | damageDealt | DBNOs | headshotKills | heals | killPlace | ... | revives | rideDistance | roadKills | swimDistance | teamKills | vehicleDestroys | walkDistance | weaponsAcquired | winPoints | winPlacePerc | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 7f96b2f878858a | 4d4b580de459be | a10357fd1a4a91 | 0 | 0 | 0.00 | 0 | 0 | 0 | 60 | ... | 0 | 0.0000 | 0 | 0.00 | 0 | 0 | 244.80 | 1 | 1466 | 0.4444 |
1 | eef90569b9d03c | 684d5656442f9e | aeb375fc57110c | 0 | 0 | 91.47 | 0 | 0 | 0 | 57 | ... | 0 | 0.0045 | 0 | 11.04 | 0 | 0 | 1434.00 | 5 | 0 | 0.6400 |
2 | 1eaf90ac73de72 | 6a4a42c3245a74 | 110163d8bb94ae | 1 | 0 | 68.00 | 0 | 0 | 0 | 47 | ... | 0 | 0.0000 | 0 | 0.00 | 0 | 0 | 161.80 | 2 | 0 | 0.7755 |
3 | 4616d365dd2853 | a930a9c79cd721 | f1f1f4ef412d7e | 0 | 0 | 32.90 | 0 | 0 | 0 | 75 | ... | 0 | 0.0000 | 0 | 0.00 | 0 | 0 | 202.70 | 3 | 0 | 0.1667 |
4 | 315c96c26c9aac | de04010b3458dd | 6dc8ff871e21e6 | 0 | 0 | 100.00 | 0 | 0 | 0 | 45 | ... | 0 | 0.0000 | 0 | 0.00 | 0 | 0 | 49.75 | 2 | 0 | 0.1875 |
data.groupby('matchId')['matchId'].count()
matchId
0000a43bce5eec 95
0000eb01ea6cdd 98
0002912fe5ed71 95
0003b92987589e 100
0006eb8c17708d 93
...
fffd74b5150cb4 97
fffe53015be300 97
fffe562611d981 94
fffe721f841f85 97
fffe92232706aa 93
Name: matchId, Length: 47964, dtype: int64
例如上面的groupId,matchId这类型数据,虽然也是类别型数据。但是它们的数据量特别多,从上面的分组聚合数据可以看出,一共有47964个ID,每个ID对应90~100条数据,如果你使用one-hot编码,无异于自杀。
但是如果不处理的话,模型训练时会占用较多内存空间。
在这儿我们将groupId
,matchId
列转换为数值化的类别编码,这是分类特征预处理的核心步骤:并且不影响我们正常使用。
# 转换groupId
data['groupId'] = data['groupId'].astype('category') # 转换为分类类型
data["groupId_cat"] = data["groupId"].cat.codes # 生成数值编码列
# 转换matchId
data['matchId'] = data['matchId'].astype('category')
data['matchId_cat'] = data['matchId'].cat.codes
# 查看新产生列
data[['groupId_cat', 'matchId_cat']].head()
groupId_cat | matchId_cat | |
---|---|---|
0 | 613591 | 30085 |
1 | 827580 | 32751 |
2 | 843271 | 3143 |
3 | 1340070 | 45260 |
4 | 1757334 | 20531 |
🎯 逐步解析:
1. 转换为分类类型 (astype('category')
)
data['groupId'] = data['groupId'].astype('category')
data["groupId_cat"] = data["groupId"].cat.codes
- 作用:基于分类类型创建对应的整数编码列
- 编码规则:
- 每个唯一
groupId
分配一个唯一整数 - 编码从0开始连续分配(0, 1, 2,...)
- 例如:
- 每个唯一
'Alpha' → 0
'Beta' → 1
'Gamma' → 2
📊 原始数据 vs 转换后数据示例:
原始 groupId | groupId (分类类型) | groupId_cat (编码) |
---|---|---|
"Alpha" | "Alpha" | 0 |
"Beta" | "Beta" | 1 |
"Alpha" | "Alpha" | 0 |
"Gamma" | "Gamma" | 2 |
✅ 关键意义与优势:
-
内存优化
- 分类类型平均减少内存占用60-90%(用整数索引代替原始值)
- 尤其当groupID是长字符串时效果显著
-
提速利器
- 分组操作提速3-10倍(如
groupby
,pivot_table
等) - 机器学习训练速度显著提升(算法更擅长处理数值数据)
- 分组操作提速3-10倍(如
-
数据分析友好
# 统计分析更高效
data.groupby('groupId_cat')['kills'].mean()
-
模型兼容性
- 机器学习模型要求数值输入(SVM、神经网络等)
- 避免字符串导致的模型错误或崩溃
-
保持数据关联
- 原始列保留(用于展示)
- 数值编码列独立存储(用于计算)
⚠️ 注意事项:
-
顺序依赖
编码顺序按首字母排序(sorted(groupId.unique())
)
如需特定顺序需提前声明:
data['groupId'] = pd.Categorical(
data['groupId'],
categories=['custom','order','here']
)
-
新增组处理
新增未定义的groupId
会被识别为缺失值(NaN)
可用add_categories()
动态扩展 -
可逆性
原始值可通过映射还原:
# 查看编码映射表
mapping = dict(enumerate(data['groupId'].cat.categories))
# {0: 'Alpha', 1: 'Beta', 2: 'Gamma'}
🌟 应用场景推荐:
游戏数据分析
# 计算每组的击杀均值(毫秒级完成)
data.groupby('groupId_cat')['kills'].mean()
机器学习特征工程
# 直接作为模型输入特征
from sklearn.ensemble import RandomForestClassifier
model = RandomForestClassifier()
model.fit(data[['groupId_cat']], data['win'])
高效数据存储
# 内存占用对比
print(f"原始列: {data['groupId'].nbytes/1e6:.2f} MB")
print(f"编码列: {data['groupId_cat'].nbytes/1e6:.2f} MB")
📊 内存优化案例:当有100万行5000个唯一groupID时
-
字符串列:约100MB
-
分类编码:约1MB(节省99%内存)