У меня есть древовидные данные, состоящие из родительских кодов, которые содержат дочерние коды, которые могут действовать как родительские, в зависимости от того, помечены ли они как «SA». Эти данные представлены в таблице Excel и выглядят следующим образом:
| Tree Level (A) | Code (B) | Spec (C) | Comm. Code (D) | Parent Code (J) |
|----------------|----------|----------|----------------|-----------------|
| 1 | A12 | 1 | SA | Mach |
| 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 |
Здесь есть одна проблема: A12 на верхнем уровне дерева (1) содержит дочерний элемент (D3F5), который сам содержит еще один родитель, такой же, как собственный родитель D3F5. Как вы можете себе представить, это (хотя и не представлено в данных в том виде, в каком они были доставлены мне) создает бесконечный l oop, где A12 на уровне дерева 3 снова и снова разворачивает всю структуру.
Обратите внимание, что один из двух дочерних элементов A12 не представляет проблемы, поскольку он имеет другую спецификацию относительно родительского элемента A12 на уровне дерева 1.
У меня есть функция, которая проверяет эту ситуацию, но она есть чрезвычайно медленный, так как он использует вложенные циклы для go строк, а общее количество строк может составлять несколько тысяч. Конечная цель - показать пользователю самый глубокий уровень, на котором возникает ошибка. В этом примере это будет код A12
с spe c 1
на уровне дерева 3
:
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_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}")
Я надеюсь ускорить этот код, так как остальная часть моего скрипта выполняется довольно быстро, и его скорость серьезно ограничивается этой функцией.