Python 我如何寻找作为孩子包含自己代码的父母?

Python 我如何寻找作为孩子包含自己代码的父母?,python,excel,tree,hierarchy,Python,Excel,Tree,Hierarchy,我有一个树状数据,由父代码构成,其中包含子代码,子代码本身可能充当父代码,这取决于它们是否标记为“SA”。此数据显示在Excel表格中,如下所示: |树级(A)|代码(B)|规范(C)|通信代码(D)|父代码(J)| |----------------|----------|----------|----------------|-----------------| |1 | A12 | 1 | SA |马赫| |2 | B41 | 2 | SA | A12| |3 | A523 | 1 | B

我有一个树状数据,由父代码构成,其中包含子代码,子代码本身可能充当父代码,这取决于它们是否标记为“SA”。此数据显示在Excel表格中,如下所示:

|树级(A)|代码(B)|规范(C)|通信代码(D)|父代码(J)|
|----------------|----------|----------|----------------|-----------------|
|1 | A12 | 1 | SA |马赫|
|2 | B41 | 2 | SA | A12|
|3 | A523 | 1 | BP | B41|
|2 | G32 | 4 | BP | A12|
|2 | D3F5 | 1 | SA | A12|
|3 | A12 | 4 | SA | D3F5|
|3 | A12 | 1 | SA | D3F5|
这里有一个问题:位于顶层树级别(1)的A12包含一个子级(D3F5),该子级本身包含另一个与D3F5自己的父级相同的父级。正如您可以想象的那样,这(虽然在数据中没有表示,因为它是交付给我的)创建了一个无休止的循环,其中树级别3的A12一次又一次地展开整个结构

请注意,两个“A12”子级中的一个没有问题,因为这与树级别1的A12父级有不同的规范

我有一个检查这种情况的函数,但是它非常慢,因为它使用嵌套循环遍历行,并且总行数可以是1000。最终目标是向用户显示错误发生的最深层次。在本例中,这将是代码
A12
,在树级
3
上具有规范
1

def nested_parent(sht):
    """
    Checks if a parent SA contains itself as a child.
    :return: nested_parents: Dictionary of found 'nested parents'. None if none found
    """
    nested_parents = {}
    found = False

    lrow = sht.Cells(sht.Rows.Count, 1).End(3).Row
    parent_treelevel = 1

    # Get deepest tree level, as this no longer contains children
    last_treelevel = int(max([i[0] for i in sht.Range(sht.Cells(2, 1), sht.Cells(lrow, 1)).Value]))

    # Loop through parent rows
    print('Checking for nested parents...')
    for i in range(2, lrow):
        if sht.Cells(i, "D").Value == "SA":
            parent_code, parent_treelevel = f'{sht.Cells(i, "B").Value}_{sht.Cells(i, "C")}', sht.Cells(i, "A").Value

            # Add new key with list containing parent's tree level for parent code
            if parent_code not in nested_parents:
                nested_parents[parent_code] = [int(parent_treelevel)]

            # Loop child rows
            for j in range(i + 1, lrow + 1):
                child_code, child_treelevel = f'{sht.Cells(j, "B").Value}_{sht.Cells(j, "C")}', sht.Cells(i, "A").Value

                if child_code == parent_code and child_treelevel > parent_treelevel:
                    found = True
                    nested_parents[parent_code].append(int(child_treelevel))

        if parent_treelevel == last_treelevel:
            # End function if deepst tree level is reached
            print("done")
            if found:
                # Delete keys that contain no information
                delkeys = []
                for key in reversed(nested_parents):
                    if len(nested_parents[key]) == 1:
                        delkeys.append(key)
                for key in delkeys:
                    del nested_parents[key]
                return nested_parents
            else:
                return
此函数可以按如下方式调用,其中
wb\u name
是包含数据的工作簿的名称:

from win32com.client import GetObject
wb_name = "NAME"
sht = GetObject(None, "Excel.Application").Workbooks(wb_name).Worksheets(1)


def err(msg):
    """
    stops the code from executing after printing an error message
    """
    print("Unexpected error occured:", msg)
    exit()


infloop = nested_parent(sht)
if infloop is not None:
    dict_str = ''.join([f'Code: {key}, Tree levels: {infloop[key]}\n' for key in infloop])
    err(f"Warning: one or more parent codes contain their own code as a child:\n{dict_str}")

我希望加快这段代码的速度,因为我脚本的其余部分速度相当快,而且它的速度正受到此函数的严重阻碍。

我希望此响应将有助于展示分层数据结构的威力。我所做的是将数据重写为json字符串,然后编写代码遍历层次结构并生成报告。您仍然需要将excel转换为json。主要的一点是,json的每一级都有相同的键,并且子级中的每个子级都有与其父字典相同的键,因此可以使用递归函数遍历该结构。我按代码或级别对总计进行了示例

import json 
json_data = """
{
    "level": 0,
    "code": "Mach",
    "children": [
        {
            "level": 1,
            "code": "A12",
            "children": [
                {
                    "level": 2,
                    "code": "B41",
                    "children": [
                        {
                            "level": 3,
                            "code": "A523",
                            "children": []
                        }
                    ]
                },
                {
                    "level": 2,
                    "code": "G32",
                    "children": []
                },
                {
                    "level": 2,
                    "code": "D3F5",
                    "children": [
                        {
                            "level": 3,
                            "code": "A12",
                            "children": []
                        },
                        {
                            "level": 3,
                            "code": "A12",
                            "children": []
                        }
                    ]
                }
            ]
        }
    ]
}
"""

data = json.loads(json_data)

def crawl_levels(mydict, result={}):
    try:
        result[mydict["level"]].append(mydict["code"])
    except:
        result[mydict["level"]] = [mydict["code"],]

    for i in mydict["children"]:
        result = crawl_levels(i, result=result)
    return result

crawl_levels(data) 
>>>{0: ['Mach'], 1: ['A12'], 2: ['B41', 'G32', 'D3F5'], 3: ['A523', 'A12', 'A12']}

def crawl_codes(mydict, result={}):
    try:
        result[mydict["code"]].append(mydict["level"])
    except:
        result[mydict["code"]] = [mydict["level"],]

    for i in mydict["children"]:
        result = crawl_codes(i, result=result)
    return result

crawl_codes(data) 
>>>{'Mach': [0],
 'A12': [1, 3, 3],
 'B41': [2],
 'A523': [3],
 'G32': [2],
 'D3F5': [2]}
正如@a'r所提到的,您的“树状数据”可以看作是一个有向图,即用箭头(有向边)连接的点(节点)。有一个非常强大的库,名为
networkx
,可以很好地处理图形。在不深入图形理论的情况下,考虑下面的代码示例:

import networkx as nx

edges = [ ('A12', 'Mach'), 
          ('B41', 'A12'),
          ('A523','B41'),
          ('G32', 'A12'),
          ('D3F5','A12'),
          ('A12', 'D3F5'),
          ('A12', 'D3F5') ]

G = nx.DiGraph(edges)
cycles_list = list(nx.simple_cycles(G))
print(cycles_list)
输出:

[['A12', 'D3F5']]
在这里,节点名称是您读取它们时的代码本身,边是子节点和父节点之间的连接。只需获取Excel文件的相应列,即可轻松创建边列表。在这种情况下,确切的方向(父母对孩子,反之亦然)不是很重要,只是保持一致

simple\u cycles
返回一个生成器。在这里你可以找到它上面的标签

更新 一旦你有了循环列表,要找到最深的节点,你需要匹配节点并找到它最深的外观

从a、B和J列中创建节点列表。该列表如下所示:

data = [
   [1, 'A12', 'Mach'],
   [2, 'B41', 'A12'],
   [3, 'A523', 'B41'],
   [2, 'G32', 'A12'],
   [2, 'D3F5', 'A12'],
   [3, 'A12', 'D3F5'],
   [3, 'A12', 'D3F5'] ]

result = {}
for entry in data:
    for el in cycles_list:
        if entry[1:] == el:
            key = tuple(el)
            result[key] = max(result.setdefault(key, 0), entry[0])
print(result)

>>>
{('A12', 'D3F5'): 3}

现在,您将得到一个字典,其中key是有问题的节点,value是可以找到的最深级别

循环在数据集中有效吗?或者你抓住它是为了向用户反馈一个错误?这是一个构造良好的问题,但因为它实际上在工作,所以它可能会更好?如果我正确理解了你的问题,那么像我在问题中解释的那样的无休止的循环不应该存在,因此错误应该存在raised@JvdV谢谢我考虑过这一点,但鉴于这段代码在理论上是如何工作的,但在实际意义上却毫无用处,我决定将其发布在这里,因此我建议先构建一个有向图,然后寻找循环。请参阅:我无法运行此代码,因为
crawl
是一个未解析的引用,已将其固定。我刚刚更改了函数名,忘记了更新函数调用。谢谢Matt。但是,我不确定这个输出将如何帮助我识别我在帖子中阐述的问题。非常感谢你的回答,因为它非常容易使用,并且工作良好。我发现一个缺点是输出的顺序似乎是随机的。这带来了一个问题,因为我必须向用户显示发生错误的结构中最深的节点。我该怎么处理?你说的“最深”是什么意思?为什么只有一个人?我想你需要删除所有的循环。我得到的Excel文件只是从一个我没有写入权限的数据库中输出的。目标是提醒用户此类问题,以便他们可以在数据库中解决这些问题。“deep”是指发生问题的最低树级别。在我的示例中,这是代码A12,规范1位于树级别3。然而,这段代码可能会遇到更深层次的问题,这就是必须标记的内容。请运行我随帖子提供的代码,看看输出是什么样子的。