You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将树形结构DataFrame转为指定格式并补充leaf列及优化代码?

树形DataFrame结构转换问题

原始数据

import pandas as pd
df = pd.DataFrame({
    'level': [1,2,2,2,3,3,3],
    'id': [1,11,12,13,111,121,122],
    'parent_id': [pd.NA,1,1,1,11,12,12],
    'type': ['node','node','node','leaf','leaf','leaf','leaf'],
    'text': ['a','b','c','d','e','f','g']
})

期望输出

需要转换为带多级列的DataFrame,最后额外添加一组列收集每条路径对应的leaf节点信息:

1              2                3            leaf           
  id  type text  id  type text    id  type text   id  type text
0  1  node    a  11  node    b   111  leaf    e  111  leaf    e
1  1  node    a  12  node    c   121  leaf    f  121  leaf    f
2  1  node    a  12  node    c   122  leaf    g  122  leaf    g
3  1  node    a  13  leaf    d  <NA>   NaN  NaN   13  leaf    d

当前实现与输出

现有代码如下:

from functools import reduce
def join_fn(x, y):
    i, df1 = x
    j, df2 = y
    return (
        j,
        pd.merge(df1, df2, left_on=f"id_{i}", right_on=f"parent_id_{j}", how="outer"),
    )

dfs = list(df.groupby("level"))
dfs = [
    (i, df.rename(columns={col: col + f"_{i}" for col in df.columns})) for i, df in dfs
]

_, dfr = reduce(join_fn, dfs)
dfr = dfr.filter([col for col in dfr.columns if col.startswith(("id", "text", "type"))])
idx = dfr.columns.str.split("_", expand=True)
dfr.columns = idx.swaplevel()

当前输出结果:

1              2                 3           
  id  type text  id  type text     id  type text
0  1  node    a  11  node    b  111.0  leaf    e
1  1  node    a  12  node    c  121.0  leaf    f
2  1  node    a  12  node    c  122.0  leaf    g
3  1  node    a  13  leaf    d    NaN   NaN  NaN

解决方案

一、添加Leaf节点列

要补充最后三列的leaf节点信息,可按以下步骤实现:

  1. 从原始数据中提取所有type=leaf的记录,单独保存为DataFrame
  2. 对现有结果dfr,逐行定位对应路径的leaf节点ID(即每行最后一个非空的id值)
  3. 将leaf节点ID与提取的leaf数据关联,补充type和text字段
  4. 将这些字段添加为多级列的顶层leaf

完整修改后的代码:

import pandas as pd
from functools import reduce

# 原始数据
df = pd.DataFrame({
    'level': [1,2,2,2,3,3,3],
    'id': [1,11,12,13,111,121,122],
    'parent_id': [pd.NA,1,1,1,11,12,12],
    'type': ['node','node','node','leaf','leaf','leaf','leaf'],
    'text': ['a','b','c','d','e','f','g']
})

# 提取所有leaf节点数据
leaf_df = df[df['type'] == 'leaf'][['id', 'type', 'text']].reset_index(drop=True)

# 合并逻辑函数
def join_fn(x, y):
    i, df1 = x
    j, df2 = y
    return j, pd.merge(df1, df2, left_on=f"id_{i}", right_on=f"parent_id_{j}", how="outer")

# 按level分组并给列添加后缀
dfs = [(i, df.add_suffix(f"_{i}")) for i, df in df.groupby("level")]

# 逐级合并各level的数据
_, dfr = reduce(join_fn, dfs)

# 过滤保留需要的列,设置多级列索引
dfr = dfr.filter(regex='^(id|type|text)_')
idx = dfr.columns.str.split("_", expand=True)
dfr.columns = idx.swaplevel()
dfr = dfr.sort_index(axis=1)

# 提取每行对应的leaf节点ID:从高level到低level取最后一个非空id
leaf_ids = dfr.xs('id', axis=1, level=1).bfill(axis=1).iloc[:, -1].astype(int)

# 关联leaf节点的type和text信息
leaf_data = leaf_df.set_index('id').loc[leaf_ids].reset_index()

# 添加leaf组列到结果中
dfr[('leaf', 'id')] = leaf_data['id']
dfr[('leaf', 'type')] = leaf_data['type']
dfr[('leaf', 'text')] = leaf_data['text']

# 调整列顺序,将leaf组列放到最后
level_cols = [col for col in dfr.columns if col[0] != 'leaf']
leaf_cols = [('leaf', 'id'), ('leaf', 'type'), ('leaf', 'text')]
dfr = dfr[level_cols + leaf_cols]

# 修正id列的数据类型(支持空值的整数类型)
for level in dfr.columns.get_level_values(0).unique():
    if level != 'leaf':
        dfr[(level, 'id')] = dfr[(level, 'id')].fillna(pd.NA).astype('Int64')

# 重置索引并打印结果
dfr = dfr.reset_index(drop=True)
print(dfr.to_string())

运行后输出将完全匹配期望格式。

二、代码优化建议

  1. 列重命名简化:用add_suffix替代手动字典映射,代码更简洁高效:df.add_suffix(f"_{i}")
  2. 列过滤简化:用正则表达式filter(regex='^(id|type|text)_')替代列表推导,可读性更强
  3. leaf ID查找优化:用xs提取所有id列,结合bfill快速获取最后一个非空值,避免逐行循环
  4. 数据类型修正:将id列转为Int64类型(支持空值的整数类型),避免显示为浮点数
  5. 列排序:合并后对列进行排序,保证level顺序逻辑正确
  6. 逻辑拆分:将leaf数据提取和关联逻辑单独拆分,代码结构更清晰

内容的提问来源于stack exchange,提问作者bayes2021

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 23:31:46