如何使用Pandas将列状层级结构转换为父子列表?
用Pandas将固定列层级数据转换为邻接表的问题
示例层级
Books / | \ Science (null) (null) / | \ Astronomy (null) Pictures / \ | \ Astrophysics Cosmology (null) Astronomy / \ | / | \ (null) (null) Amateurs_Astronomy Galaxies Stars Astronauts
原始数据(data.csv)
id,level_1,level_2,level_3,level_4,level_5 1,Books,Science,Astronomy,Astrophysics, 2,Books,Science,Astronomy,Cosmology, 3,Books,,,,Amateurs_Astronomy 4,Books,,Pictures,Astronomy,Galaxies 5,Books,,Pictures,Astronomy,Stars 6,Books,,Pictures,Astronomy,Astronauts
当前问题
已尝试为节点添加UUID列,但存在两个核心问题:
- 相同节点会生成不同UUID(比如第1、2行的
level_3节点Astronomy应共用同一个UUID) - 不知道如何跳过空值,准确找到每个节点的父节点
期望生成如下格式的邻接表:
this_node,parent_node,this_node_uuid,parent_node_uuid Science,Books,books/science-node-uuid,books-node-uuid Astronomy,Science,books/science/astronomy-node-uuid,books/science/astronomy-node-uuid Astrophysics,Astronomy,books/science/astronomy/astrophysics-node-uuid,books/science/astronomy-node-uuid Amateurs_Astronomy,Books,books/amateurs_astronomy-node-uuid,books-node-uuid
解决方案
核心思路
- 统一节点UUID:用字典存储所有唯一节点,每个节点只生成一次UUID
- 追踪父节点:遍历每行数据时,跳过空值,动态更新当前节点的父节点
- 生成邻接表:从节点字典中提取父子关系,格式化输出
代码实现
import pandas as pd import uuid # 读取数据 df = pd.read_csv('data.csv') # 初始化节点映射字典,存储每个节点的UUID、路径、父节点 node_map = {} root_col = 'level_1' # 处理根节点(level_1) for root_node in df[root_col].dropna().unique(): node_map[root_node] = { 'uuid': str(uuid.uuid4()), 'path': root_node.lower(), 'parent': None } # 遍历每行数据,构建节点映射 for _, row in df.iterrows(): current_parent = row[root_col] # 从level_2到level_5依次处理 for level_num in range(2, 6): col_name = f'level_{level_num}' current_node = row[col_name] # 跳过空值节点 if pd.isna(current_node): continue # 节点未记录时,生成UUID并关联父节点 if current_node not in node_map: parent_info = node_map[current_parent] node_uuid = str(uuid.uuid4()) node_path = f"{parent_info['path']}/{current_node.lower()}" node_map[current_node] = { 'uuid': node_uuid, 'path': node_path, 'parent': current_parent } # 更新当前父节点为当前节点,用于下一层级的子节点 current_parent = current_node # 生成邻接表 adjacency_records = [] for node, info in node_map.items(): parent_node = info['parent'] if parent_node is not None: parent_info = node_map[parent_node] adjacency_records.append({ 'this_node': node, 'parent_node': parent_node, 'this_node_uuid': f"{info['path']}-{info['uuid'][:8]}", # 取UUID前8位简化格式 'parent_node_uuid': f"{parent_info['path']}-{parent_info['uuid'][:8]}" }) # 转换为DataFrame并去重 adjacency_df = pd.DataFrame(adjacency_records).drop_duplicates() print(adjacency_df)
代码说明
- 节点映射字典:确保每个节点只生成一次UUID,避免重复
- 行遍历逻辑:逐行处理时,动态跟踪当前父节点,遇到空值直接跳过,保证父节点定位准确
- UUID格式:采用
路径-短UUID的格式,和示例格式一致,同时保证唯一性 - 去重处理:避免多行数据中重复的父子关系被多次记录
内容的提问来源于stack exchange,提问作者Lionel Hamayon
相关产品推荐
相关产品推荐

