Polars中Categorical列转整数出现异常映射?原因与解决办法
Polars Categorical列转数值类型映射异常问题解决
问题背景
你在Polars中通过qcut生成了名为decile的Categorical列,标签为q10、q09...q01(对应逻辑上的10到1分位数)。但直接用pl.col('decile').cast(pl.Int8)转换时,结果变成了0-9的整数,和标签里的数值(10-1)完全不符——这是因为Categorical转数值时,取的是类别在内部的索引(从0开始),而非标签文本里的数字。
你的最小可复现代码:
import numpy as np import polars as pl size = 1e3 df = pl.DataFrame({ "id": np.random.randint(50, size=int(size), dtype=np.uint16), "amount": np.round(np.random.uniform(10, 100000, int(size)).astype(np.float32), 2), "quantity": np.random.randint(1, 7, size=int(size), dtype=np.uint16), }) df = (df .group_by("id") .agg(revenue=pl.sum("amount"), tot_quantity=pl.sum("quantity")) ) df = (df.with_columns( pl.col('revenue') .qcut(10, labels=[f'q{i:02}' for i in range(10, 0, -1)]) .alias("decile") ))
解决方案
方法1:先转字符串再提取数字转换
直接将Categorical转为字符串,提取标签中的数字部分后再转整数:
df = df.with_columns( # 提取标签中的数字,转Int8 pl.col('decile').cast(pl.String).str.extract(r'(\d+)').cast(pl.Int8).alias('decile_num') )
方法2:通过类别映射转换
先获取Categorical的所有类别标签,创建标签到目标数值的映射字典,再批量替换:
# 获取所有类别标签 cat_labels = df['decile'].cat.get_categories() # 构建映射:qxx → xx对应的整数 label_map = {label: int(label.lstrip('q')) for label in cat_labels} df = df.with_columns( pl.col('decile').map_dict(label_map).alias('decile_num') )
方法3:在qcut阶段直接生成数值标签
如果不需要保留字符串格式的分位数标签,可以直接在qcut时指定数值型标签,一步到位:
df = df.with_columns( pl.col('revenue') .qcut(10, labels=list(range(10, 0, -1))) # 直接用10到1的整数作为标签 .alias("decile") )
如果需要同时保留字符串和数值标签,可以生成两列:
df = df.with_columns( decile_str=pl.col('revenue').qcut(10, labels=[f'q{i:02}' for i in range(10, 0, -1)]), decile_num=pl.col('revenue').qcut(10, labels=list(range(10, 0, -1))) )
关键原因说明
Polars的Categorical类型本质是用整数索引映射类别标签,当直接cast为数值类型时,返回的是类别在内部的索引值(从0开始),而非标签文本中的数字。你的decile列类别顺序是['q10', 'q09', ..., 'q01'],所以索引0对应q10,索引9对应q01,直接转换就会得到0-9的结果,和预期不符。
内容的提问来源于stack exchange,提问作者Omar AlSuwaidi
相关产品推荐
相关产品推荐

