Home > Blockchain >  print taxonomy from dataframe as text with indentation
print taxonomy from dataframe as text with indentation

Time:09-21

I have a subclassOf hierarchy that can be represented as a dataframe. subjects are subclasses of objects. I would like to print it as an indented list, where indentation means that the term is a subclass of the term on the line above. I'm using a recursive function and feel like I'm pretty close: I can indent in, but I don't think I've found the right place to indent out (by decrementing prefix_level).

Apologies if this question isn't organized real well. I'm open to any solution. It doesn't have to build on what I've shown here.

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)

enter image description here

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)

This is the output.

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

But it should look like this (hand-drawn). For example, ENVO:01000127 is not a subclass of 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

CodePudding user response:

You can use recursion:

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))

Output:

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

A simpler implementation of nest, using a generator:

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)))
  • Related