从数据框中打印分类法作为带缩进的文本

print taxonomy from dataframe as text with indentation

我有一个可以表示为数据框的 subclassOf 层次结构。 subjectsobjects 的子类。我想将其打印为缩进列表,其中缩进表示该术语是上一行术语的子类。我正在使用递归函数,感觉我非常接近:我可以缩进,但我认为我没有找到缩进的正确位置(通过递减 prefix_level)。

如果这个问题没有很好地组织,我们深表歉意。我愿意接受任何解决方案。它不必建立在我在这里展示的内容之上。

import pandas as pd
current_direct_sco = pd.DataFrame(
    {
        "subject": {
            986: "ENVO:01000025",
            989: "ENVO:01000028",
            990: "ENVO:01000029",
            991: "ENVO:01000030",
            1011: "ENVO:01000050",
            1014: "ENVO:01000053",
            1015: "ENVO:01000054",
            1096: "ENVO:01000127",
            1242: "ENVO:01000252",
            1243: "ENVO:01000253",
        },
        "object": {
            986: "ENVO:01000024",
            989: "ENVO:01000024",
            990: "ENVO:01000024",
            991: "ENVO:01000024",
            1011: "ENVO:01000029",
            1014: "ENVO:01000030",
            1015: "ENVO:01000030",
            1096: "ENVO:01000024",
            1242: "ENVO:00000873",
            1243: "ENVO:00000873",
        },
    }
)

print(current_direct_sco)
|      | subject       | object        |
|------|---------------|---------------|
| 986  | ENVO:01000025 | ENVO:01000024 |
| 989  | ENVO:01000028 | ENVO:01000024 |
| 990  | ENVO:01000029 | ENVO:01000024 |
| 991  | ENVO:01000030 | ENVO:01000024 |
| 1011 | ENVO:01000050 | ENVO:01000029 |
| 1014 | ENVO:01000053 | ENVO:01000030 |
| 1015 | ENVO:01000054 | ENVO:01000030 |
| 1096 | ENVO:01000127 | ENVO:01000024 |
| 1242 | ENVO:01000252 | ENVO:00000873 |
| 1243 | ENVO:01000253 | ENVO:00000873 |
import igraph as ig
g = ig.Graph.TupleList(
    current_direct_sco.itertuples(index=False), directed=True, vertex_name_attr="label"
)
# requires pycairo
ig.plot(g)

roots = list(set(current_direct_sco["object"]) - set(current_direct_sco["subject"]))
print(roots)

['ENVO:00000873', 'ENVO:01000024']

def recurse_envo(starting_term):
    global prefix_level
    global prefix_chunk
    global current_prefix
    print(current_prefix + starting_term)
    current_children = list(
        current_direct_sco["subject"].loc[current_direct_sco["object"] == starting_term]
    )
    if len(current_children) > 0:
        prefix_level = prefix_level + 1
        current_prefix = prefix_chunk * prefix_level
        for current_child in current_children:
            recurse_envo(current_child)

for i in roots:
    first_term = i
    prefix_level = 0
    prefix_chunk = "  "
    current_prefix = ""
    recurse_envo(first_term)

这是输出。

ENVO:00000873
  ENVO:01000252
  ENVO:01000253
ENVO:01000024
  ENVO:01000025
  ENVO:01000028
  ENVO:01000029
    ENVO:01000050
    ENVO:01000030
      ENVO:01000053
      ENVO:01000054
      ENVO:01000127

但它应该是这样的(手绘)。例如,ENVO:01000127 不是 ENVO:01000030 的子类:

ENVO:00000873
  ENVO:01000252
  ENVO:01000253
ENVO:01000024
  ENVO:01000025
  ENVO:01000028
  ENVO:01000029
    ENVO:01000050
  ENVO:01000030
    ENVO:01000053
    ENVO:01000054
  ENVO:01000127

你可以使用递归:

data = {'subject': {986: 'ENVO:01000025', 989: 'ENVO:01000028', 990: 'ENVO:01000029', 991: 'ENVO:01000030', 1011: 'ENVO:01000050', 1014: 'ENVO:01000053', 1015: 'ENVO:01000054', 1096: 'ENVO:01000127', 1242: 'ENVO:01000252', 1243: 'ENVO:01000253'}, 'object': {986: 'ENVO:01000024', 989: 'ENVO:01000024', 990: 'ENVO:01000024', 991: 'ENVO:01000024', 1011: 'ENVO:01000029', 1014: 'ENVO:01000030', 1015: 'ENVO:01000030', 1096: 'ENVO:01000024', 1242: 'ENVO:00000873', 1243: 'ENVO:00000873'}}
vals = [[data['subject'][i], data['object'][i]] for i in data['subject']]
def nest(n, c = 0):
   return ((c*"   ")+n)+('' if not (k:=[nest(a, c+1) for a, b in vals if b == n]) 
          else '\n'+("\n".join(k)))

roots = {b for _, b in vals if all(j != b for j, _ in vals)} #can replace this definition with list(set(current_direct_sco["object"]) - set(current_direct_sco["subject"]))
print('\n'.join(nest(b) for b in roots))

输出:

ENVO:01000024
   ENVO:01000025
   ENVO:01000028
   ENVO:01000029
      ENVO:01000050
   ENVO:01000030
      ENVO:01000053
      ENVO:01000054
   ENVO:01000127
ENVO:00000873
   ENVO:01000252
   ENVO:01000253

nest 的更简单实现,使用生成器:

def nest(n, c = 0):
   yield (c*"   ")+n
   for a, b in vals:
       if b == n:
          yield from nest(a, c+1)

...
print('\n'.join(i for b in roots for i in nest(b)))