如何在Pandas中仅堆叠DataFrame指定列子集实现行复制?
处理重复列组的DataFrame堆叠转换问题
问题背景
从XML文件生成的数组转换为DataFrame后,前n列为通用信息(每行所有产品共享该内容),后24列包含8组重复的列名组合["Product", "Name", "Price"]。需要将这些重复列组按每3列一组堆叠,得到以下两种格式之一:
- 格式1:通用列仅第一行保留,后续产品行通用列留空
- 格式2:每一行产品都复制对应的通用列信息
初始数据结构
| Location | Description | Status | ..... | Product | Name | Price | Product | Name | Price | ... |
|---|---|---|---|---|---|---|---|---|---|---|
| 1 | "Foo" | 1 | 124 | 4.2 | 122 | 8.8 | ||||
| 2 | "Bar" | 1 | 122 | 8.9 | NaN | NaN |
目标格式示例
格式1
| Location | Description | Status | ..... | Product | Name | Price |
|---|---|---|---|---|---|---|
| 1 | "Foo" | 1 | 124 | 4.2 | ||
| 122 | 8.8 | |||||
| 2 | "Bar" | 1 | 122 | 8.9 | ||
| NaN | NaN |
格式2
| Location | Description | Status | ..... | Product | Name | Price |
|---|---|---|---|---|---|---|
| 1 | "Foo" | 1 | 124 | 4.2 | ||
| 1 | "Foo" | 1 | 122 | 8.8 | ||
| 2 | "Bar" | 1 | 122 | 8.9 | ||
| 2 | "Bar" | 1 | NaN | NaN |
已尝试的方法
步骤1:重命名重复列名
cols=pd.Series(result_list_xml_t[0]) for dup in cols[cols.duplicated()].unique(): cols[cols[cols == dup].index.values.tolist()] = [dup + '.' + str(i) if i != 0 else dup for i in range(sum(cols == dup))]
步骤2:创建多级索引列
columns_xml = pd.MultiIndex.from_arrays([xml_groups,cols])
生成的DataFrame结构:
| base | prod1 | prod2 | ... | |
|---|---|---|---|---|
| Location | Description | Status | ..... | |
| 1 | 1 | "Foo" | 1 | |
| 2 | 2 | "Bar" | 1 |
尝试过df.stack(future_stack=True,level=0)及melt()+set_index()组合,未得到预期结果。
解决方案
方法1:使用pd.wide_to_long(推荐,简洁高效)
该方法专门处理重复列组的宽表转长表需求,无需复杂的手动列处理:
# 替换为实际的通用列数量 n_base_cols = 3 base_cols = df.columns[:n_base_cols].tolist() product_cols = df.columns[n_base_cols:] # 给每组3列添加组号后缀(如_1、_2..._8) new_product_cols = [] for i in range(0, len(product_cols), 3): group = product_cols[i:i+3] new_product_cols.extend([f"{col}_{i//3 + 1}" for col in group]) # 重命名DataFrame列 df.columns = base_cols + new_product_cols # 转换为长表(格式2) result_format2 = pd.wide_to_long( df, stubnames=["Product", "Name", "Price"], i=base_cols, j="product_group", sep="_", suffix=r"\d+" ).reset_index(drop=False) # 转换为格式1(通用列重复行留空) result_format1 = result_format2.copy() result_format1[base_cols] = result_format1[base_cols].mask( result_format1.duplicated(subset=base_cols, keep="first") )
方法2:基于多级索引的stack实现
如果已经创建了多级索引列,可通过以下步骤转换:
# 将通用列设为索引 base_cols = [col for col in df.columns if col[0] == "base"] df_indexed = df.set_index(base_cols) # 堆叠所有prod开头的列组 stacked = df_indexed.stack(level=0).reset_index() # 清理列名 stacked.columns = [col if col != "level_0" else "product_group" for col in stacked.columns] # 格式2直接使用stacked,格式1处理通用列重复值 result_format1 = stacked.copy() result_format1[base_cols] = result_format1[base_cols].mask( result_format1.duplicated(subset=base_cols, keep="first") )
内容的提问来源于stack exchange,提问作者mazwing
相关产品推荐
相关产品推荐

