【问题标题】:Parsing graph data file with Python用 Python 解析图形数据文件
【发布时间】:2015-11-29 00:38:53
【问题描述】:

我有一个相对较小的问题,但我无法继续解决它。我有一个包含图表信息的文本文件,结构如下:

  • 第一行包含节点数
  • 空行用于分隔
  • 节点信息如下,每个块之间用空行分隔
  • 块包含节点 id 一行一行,第二行键入,然后是有关边的信息
  • 有两种类型的边,上边和下边,节点类型后面的第一个数字表示“上”边的数量,它们的 ID 紧随其后(如果该数字为 0,则不存在“上”边并且下一个数字表示“向下”边缘)
  • “向下”边缘也是如此,它们的数量和它们的 ID 在下面一行中

所以,有两个节点的样本数据是:

3

1
1
2
2 3
0

2
1
0
2
1 3

3
2
1
1
1
2

因此,节点 1 具有类型 1、两个向上边缘、2 和 3,并且没有向下边缘。 节点 2 具有类型 1、零上边和 2 个下边,即 1 和 3 节点 3 有类型 2,一个上边,1,和 1 个下边,2。

人类可以清楚地读取此信息,但我在编写解析器以获取此信息并将其以可用形式存储时遇到问题。

我已经写了一个示例代码:

f = open('C:\\data', 'r')
lines = f.readlines()
num_of_nodes = lines[0]
nodes = {}
counter = 0
skip_next = False
for line in lines[1:]:
    new = False
    left = False
    right = False
    if line == "\n":
        counter += 1
        nodes[counter] = []
        new = True
        continue
    nodes[counter].append(line.replace("\n", ""))

这让我得到了每个节点的信息拆分。我想要一个像字典这样的东西,它可以保存 ID,每个的上下邻居(如果没有可用的,则为 False)。我想我现在可以再次解析这个节点列表并单独执行每个节点,但我想知道我是否可以修改这个循环,我必须首先很好地做到这一点。

【问题讨论】:

  • 您对clearly readable by human 的定义与我的不同,但我正在考虑解决您的问题
  • 哈哈,好吧,我这辈子肯定读过一些更易读的东西,但我想说的是数据结构是“定义的”,意思是当我看数字的序列时,我可以很容易地在我的脑海中表示,节点ID,它的类型和每一侧的邻居(如果有的话)。这个子句,“如果它有它们”,似乎是我无法在代码中描述的关键部分。
  • 你能考虑给你的问题一个比“使用 Python 解析文本文件”更模糊的标题吗?特定于您尝试读取的数据的内容。
  • 您能提供您要查找的字典的大纲吗?
  • @puredevotion 像这样的节点 = { node_id: {ups: [], downs:[]} 或该方法中的东西。

标签: python parsing text graph


【解决方案1】:

这是你想要的吗?

{1: {'downs': [], 'ups': [2, 3], 'node_type': 1}, 
 2: {'downs': [1, 3], 'ups': [], 'node_type': 1}, 
 3: {'downs': [2], 'ups': [1], 'node_type': 2}}

然后是代码:

def parse_chunk(chunk):
    node_id = int(chunk[0])
    node_type = int(chunk[1])

    nb_up = int(chunk[2])
    if nb_up:
        ups = map(int, chunk[3].split())
        next_pos = 4
    else:
        ups = []
        next_pos = 3

    nb_down = int(chunk[next_pos])
    if nb_down:
        downs = map(int, chunk[next_pos+1].split())
    else:
        downs = []

    return node_id, dict(
        node_type=node_type,
        ups=ups,
        downs=downs
        )

def collect_chunks(lines):
    chunk = []
    for line in lines:
        line = line.strip()
        if line:
            chunk.append(line)
        else:
            yield chunk
            chunk = []
    if chunk:
        yield chunk

def parse(stream):
    nb_nodes = int(stream.next().strip())
    if not nb_nodes:
        return []
    stream.next()
    return dict(parse_chunk(chunk) for chunk in collect_chunks(stream))

def main(*args):
    with open(args[0], "r") as f:
        print parse(f)

if __name__ == "__main__":
    import sys
    main(*sys.argv[1:])

【讨论】:

  • 这太完美了!我只是稍微修改了一下,因为我需要稍微不同的输出进行进一步处理,所以我添加了这个:nodes = {} for node in list_of_nodes: nodes[node['node_id']] = {'type': node['node_type'], 'right': node['right'], 'left': node['left']}
  • 现在完美了!感谢您的快速回答以及您的时间和帮助!
【解决方案2】:

我会按照下面的说明进行操作。我会在文件读取周围添加一个 try-catch,并使用 with-statement 读取您的文件

nodes = {}
counter = 0
with open(node_file, 'r', encoding='utf-8') as file:
     file.readline()                              # skip first line, not a node
     for line in file.readline():
         if line == "\n":
             line = file.readline()               # read next line
             counter = line[0]
             nodes[counter] = {}                  # create a nested dict per node
             line = file.readline() 
             nodes[counter]['type'] = line[0]     # add node type
             line = file.readline()
             if line[0] != '0':
                 line = file.readline()           # there are many ways
                 up_edges = line[0].split()       # you can store edges
                 nodes[counter]['up'] = up_edges  # here a list
                 line = file.readline()
             else: 
                 line = file.readline()
             if line[0] != '0':
                 line = file.readline()
                 down_edges = line[0].split()     # store down-edges as a list  
                 nodes[counter]['down'] = down_edges  
             # end of chunk/node-set, let for-loop read next line
         else:
              print("this should never happen! line: ", line[0])

这会读取每行的文件。我不确定您的数据文件,但这在您的记忆中更容易。如果内存是一个问题,这在读取 HDD 方面会更慢(尽管 SSD 确实有奇迹)

没有测试过代码,但概念很清楚:)

【讨论】:

  • 您的代码不起作用 - 您正在读取字符串但与整数进行比较。
  • 啊,以为它会将数字读取为整数,但这将是一个小的编辑......
  • 是的,不工作。有一些奇怪的东西,比如:'line = file.readline()' 和下一行'counter = line[0]',这会带来一些错误。
  • 好的,那我测试一下:) --> 或者不,因为@bruno 的回答是正确的
  • 是的,看来他已经实现了我的要求,因此无需再将您的宝贵时间浪费在这个问题上。谢谢! :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多