如何在Pandas中将每行仅含单值的多类别列合并为一列
问题描述
现有如下代码:
import pandas as pd import numpy as np df = pd.DataFrame({"x": ["red", "blue", np.nan, np.nan, np.nan, np.nan, np.nan, ], "y": [np.nan, np.nan, np.nan, 'cold', 'warm', np.nan, np.nan, ], "z": [np.nan, np.nan, np.nan, np.nan, np.nan, 'charm', 'strange'], }).astype("category")
运行后得到DataFrame:
x y z 0 red NaN NaN 1 blue NaN NaN 2 NaN NaN NaN 3 NaN cold NaN 4 NaN warm NaN 5 NaN NaN charm 6 NaN NaN strange
需求:新增一个无序类别列,包含值red、blue、cold、warm、charm、strange,并按对应行填充(每行仅保留非空的那个值)。实际场景中这类列数量较多,不止三列。
已尝试但失败的方法:
- 使用
astype(str)拼接后重新创建类别 - 使用
union_categoricals创建新类别类型,转换各列后依次fillna()
备注:定义df时用.astype(pd.CategoricalDtype(ordered=True))替代.astype("category"),后续解决方案依然适用。
解决方案
方法1:统一类别后提取每行非空值
先合并所有列的类别,创建统一的CategoricalDtype,再转换所有列并提取每行第一个非空值:
from pandas.api.types import union_categoricals # 合并所有列的类别集合 all_categories = union_categoricals([df[col] for col in df.columns]) # 创建统一的类别类型(无序,需有序则设置ordered=True) unified_cat_type = pd.CategoricalDtype(categories=all_categories.categories, ordered=False) # 将所有列转换为统一类别 df_unified = df.astype(unified_cat_type) # 新增目标列,取每行第一个非空值 df['new_col'] = df_unified.bfill(axis=1).iloc[:, 0]
方法2:利用stack()高效聚合(适合大量列场景)
无需逐列转换类别,通过堆叠操作直接提取每行非空值:
# 堆叠所有列,提取非空值并匹配原行索引 stacked_data = df.stack().reset_index(level=1, drop=True) # 将结果合并到原DataFrame,空行自动填充NaN df['new_col'] = stacked_data # 转换为包含所有目标值的类别列 df['new_col'] = df['new_col'].astype( pd.CategoricalDtype(categories=stacked_data.unique(), ordered=False) )
两种方法均兼容有序类别场景,只需在创建CategoricalDtype时将ordered参数设为True即可。
内容的提问来源于stack exchange,提问作者CPBL
相关产品推荐
相关产品推荐

