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

Python 3中向空嵌套字典添加数据时的异常排查

问题

我是Python新手,在构建/操作网络图的过程中学习字典概念,但遇到了问题。我有如下格式的CSV文件:

RC11,RC12,RC13,RC14,RC15,RC16,RC17,RC18,RC19,RC110
RC21,RC22,RC23,RC24,RC25,RC26,RC27,RC28,RC29,RC210
RC31,RC32,RC33,RC34,RC35,RC36,RC37,RC38,RC39,RC310

我使用networkx包读取该文件创建MultiDiGraph,其中前两列代表边的两个节点,其余列构成存储在属性字典中的各种边属性。我尝试将这些值添加到预先初始化的名为moves的嵌套字典中:

from collections import defaultdict
import pandas as pd
import networkx as nx

# 创建空的嵌套字典
movesTree = lambda: defaultdict(movesTree)
moves = movesTree()

# 用pandas读取CSV文件
columns = list(pd.read_csv(fileURI, sep=',', nrows=0).columns)    # 仅获取表头
routingData = pd.read_csv(fileURI, sep=',').dropna()   # 读取整个文件

# 向已创建的MultiDiGraph routingNetwork添加边
routingNetwork = nx.MultiDiGraph()
for i, elrow in routingData.iterrows():
    routingNetwork.add_edge(elrow[0], elrow[1], attr_dict=elrow[2:].to_dict())

    # 将行中的值添加到字典moves
    # 前6个值构成唯一键
    # 最后5个值对应该键的各种属性(来自表头列表)
    moves[elrow[0]][elrow[1]][elrow[2]][elrow[3]][elrow[4]][elrow[5]] = [(columns[6], elrow[6]), (columns[7], elrow[7]), (columns[8], elrow[8]), (columns[9], elrow[9]), (columns[10], elrow[10])]

print(list(moves))

但print(list(moves))仅输出去重后的节点列表,且缺少终端节点。请问我哪里出错了?

注:实际上,每个节点的moves只需保存该节点的出边数据。在实现该逻辑前,我测试上述语句的语法和输出时遇到了瓶颈。


解决方法

问题原因

你使用的是递归式defaultdict,list(moves)只会返回最外层字典的键(也就是所有起始节点elrow[0]),这就是为什么你只能看到起始节点列表——终端节点是嵌套字典的第二层键,不会被list(moves)直接返回。

另外代码里还有两个明显问题:

  1. 索引越界:你的CSV每行只有10个元素(索引0~9),但代码里引用了columns[10],会触发IndexError。
  2. 列数不匹配:你说“前6个值构成唯一键,最后5个值对应属性”,但6+5=11,和CSV实际的10列矛盾,需要确认CSV的实际列数。

优化方案

针对“每个节点保存出边数据”的需求,没必要用层级过深的嵌套字典,改用起始节点为键、出边信息列表为值的结构更清晰,后续访问和维护也更简单。

修改后的代码示例:

from collections import defaultdict
import pandas as pd
import networkx as nx

# 初始化moves:键是起始节点,值是该节点的出边信息列表
moves = defaultdict(list)

fileURI = "你的CSV文件路径"
routingNetwork = nx.MultiDiGraph()

# 读取CSV并获取表头
routingData = pd.read_csv(fileURI, sep=',').dropna()
columns = routingData.columns.tolist()

# 遍历数据行
for _, elrow in routingData.iterrows():
    start_node = elrow[0]
    end_node = elrow[1]
    
    # 提取前6个值作为唯一键(索引0~5)
    key_values = elrow.iloc[0:6].tolist()
    # 提取属性:根据实际列数调整索引,这里假设是索引6~9(对应最后4列)
    attr_names = columns[6:10]
    attr_values = elrow.iloc[6:10].tolist()
    edge_attrs = dict(zip(attr_names, attr_values))
    
    # 添加边到网络图
    routingNetwork.add_edge(start_node, end_node, **edge_attrs)
    
    # 将出边信息存入moves
    moves[start_node].append({
        "end_node": end_node,
        "unique_key": key_values,
        "attributes": edge_attrs
    })

# 打印所有节点的出边数据
for node, edges in moves.items():
    print(f"节点 {node} 的出边:")
    for edge in edges:
        print(f"  目标节点:{edge['end_node']}")
        print(f"  唯一键:{edge['unique_key']}")
        print(f"  属性:{edge['attributes']}\n")

# 若仅需查看所有起始节点
print("所有起始节点:", list(moves.keys()))

说明

  1. 新的moves结构直接对应“每个节点保存出边数据”的需求,通过moves[起始节点]就能获取该节点的所有出边信息。
  2. 用iloc明确指定索引范围,避免隐式索引的问题,同时解决了之前的索引越界错误。
  3. 若你的CSV实际是11列,只需将属性的索引范围改为columns[6:11]和elrow.iloc[6:11]即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 23:31:05