diff --git a/pyproject.toml b/pyproject.toml index e8b22600..93c868ec 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,6 +40,7 @@ dependencies = [ "rich", "scipy", "sortedcontainers", + "tabulate", ] [project.optional-dependencies] diff --git a/src/nplinker/scoring/link_graph.py b/src/nplinker/scoring/link_graph.py index b707df17..50151997 100644 --- a/src/nplinker/scoring/link_graph.py +++ b/src/nplinker/scoring/link_graph.py @@ -3,6 +3,7 @@ from functools import wraps from typing import Union from networkx import Graph +from tabulate import tabulate from nplinker.genomics import GCF from nplinker.metabolomics import MolecularFamily from nplinker.metabolomics import Spectrum @@ -73,9 +74,20 @@ def __init__(self) -> None: Create a LinkGraph object: >>> lg = LinkGraph() + Display the empty LinkGraph object: + >>> lg + | | Object 1 | Object 2 | Metcalf Score | Rosetta Score | + |----|------------|------------|-----------------|-----------------| + Add a link between a GCF and a Spectrum object: >>> lg.add_link(gcf, spectrum, metcalf=Score("metcalf", 1.0, {"cutoff": 0.5})) + Display all links in LinkGraph object: + >>> lg + | | Object 1 | Object 2 | Metcalf Score | Rosetta Score | + |----|--------------|------------------------|-----------------|-----------------| + | 1 | GCF(id=gcf1) | Spectrum(id=spectrum1) | 1 | - | + Get all links for a given object: >>> lg[gcf] {spectrum: {"metcalf": Score("metcalf", 1.0, {"cutoff": 0.5})}} @@ -94,9 +106,9 @@ def __init__(self) -> None: """ self._g: Graph = Graph() - def __str__(self) -> str: - """Get a short summary of the LinkGraph.""" - return f"{self.__class__.__name__}(#links={len(self.links)}, #objects={len(self)})" + def __repr__(self) -> str: + """Return a string representation of the LinkGraph.""" + return self._get_table_repr() def __len__(self) -> int: """Get the number of objects.""" @@ -272,3 +284,36 @@ def _filter_two_nodes(self, u: Entity, v: Entity, lg: LinkGraph) -> None: link_data = self.get_link_data(u, v) if link_data is not None: lg.add_link(u, v, **link_data) + + def _get_table_repr(self) -> str: + """Generate a table representation of the LinkGraph. + + The table is truncated to 60 links. + """ + headers = ["", "Object 1", "Object 2", "Metcalf Score", "Rosetta Score"] + table_data = [] + display_limit = 60 + + for index, (u, v, data) in enumerate(self.links, start=1): + metcalf_score = data.get("metcalf") + rosetta_score = data.get("rosetta") + + row = [ + index, + str(u if isinstance(u, GCF) else v), + str(v if isinstance(u, GCF) else u), + f"{metcalf_score.value:.2f}" if metcalf_score else "-", + f"{rosetta_score.value:.2f}" if rosetta_score else "-", + ] + table_data.append(row) + + if index == display_limit: + break + + table = tabulate(table_data, headers=headers, tablefmt="github", stralign="right") + + if len(self.links) > display_limit: + truncated_info = f"...\n[ {len(self.links)} links ]" + return f"{table}\n{truncated_info}" + + return table