DataFrame含列表值列转Categorical报错,求内存优化方案
解决含列表的Object列转Categorical报错及内存优化问题
首先,你遇到的TypeError: unhashable type: 'list'是因为Categorical类型要求元素必须是可哈希的——而列表是可变对象,无法被哈希,所以不能直接作为Categorical的类别值。结合你提到的「多数列表值均不同」的情况,转Categorical其实也起不到明显的内存优化效果(因为类别数量太多,和原Object列的内存占用差异不大),下面给你几个更适合的解决方案:
1. 先将列表转为可哈希类型(解决报错,但内存优化有限)
如果只是想解决报错,可以把列表转换成不可变的元组(元组是可哈希的),再转Categorical:
# 将列表转为元组 vs_df['handled_plans_id'] = vs_df['handled_plans_id'].apply(tuple) # 转为Categorical类型 vs_df['handled_plans_id'] = vs_df['handled_plans_id'].astype('category')
不过注意:如果你的列表绝大多数都是唯一的,这种方法的内存优化效果会非常有限,因为Categorical的优势在于重复值多的场景。
2. 使用PyArrow列表类型(高效内存优化,推荐)
对于存储列表的列,用PyArrow的列式存储类型替代原生Object列,能大幅降低内存占用,同时不需要改变数据结构:
import pyarrow as pa from pandas import ArrowDtype # 将列转为PyArrow的列表类型 vs_df['handled_plans_id'] = vs_df['handled_plans_id'].astype(ArrowDtype(pa.list_(pa.int64())))
PyArrow会以更紧凑的二进制格式存储列表数据,比原生Object列(每个列表都是独立的Python对象)节省大量内存,尤其适合大数据集。
3. 序列化列表为Bytes类型
把列表序列化后存储为Bytes对象,也能减少内存占用,代价是存取时需要额外的序列化/反序列化步骤:
import pickle # 序列化列表为bytes vs_df['handled_plans_id'] = vs_df['handled_plans_id'].apply(pickle.dumps) # 读取时反序列化 vs_df['handled_plans_id'] = vs_df['handled_plans_id'].apply(pickle.loads)
这种方法的内存节省效果不如PyArrow,但实现简单,适合对性能要求不高的场景。
4. 拆分列表为多列(适合列表长度固定或差异小的场景)
如果你的列表长度比较固定(比如最多包含2个元素),可以把列表拆成多个数值列,用更高效的数值类型存储:
# 拆分列表为两列,空值填充NaN vs_df[['plan_1', 'plan_2']] = pd.DataFrame(vs_df['handled_plans_id'].tolist(), index=vs_df.index) # 转为int类型(空值用pd.NA) vs_df[['plan_1', 'plan_2']] = vs_df[['plan_1', 'plan_2']].astype(pd.Int64Dtype())
这种方法内存占用最低,但只适合列表长度相对统一的情况。
内容的提问来源于stack exchange,提问作者appletabo
相关产品推荐
相关产品推荐

