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