risk-network 0.0.9b46__py3-none-any.whl → 0.0.11__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- risk/__init__.py +1 -1
- risk/annotations/annotations.py +17 -52
- risk/annotations/nltk_setup.py +85 -0
- risk/neighborhoods/domains.py +9 -12
- risk/risk.py +2 -3
- risk/stats/permutation/test_functions.py +1 -0
- {risk_network-0.0.9b46.dist-info → risk_network-0.0.11.dist-info}/METADATA +29 -48
- {risk_network-0.0.9b46.dist-info → risk_network-0.0.11.dist-info}/RECORD +11 -10
- {risk_network-0.0.9b46.dist-info → risk_network-0.0.11.dist-info}/LICENSE +0 -0
- {risk_network-0.0.9b46.dist-info → risk_network-0.0.11.dist-info}/WHEEL +0 -0
- {risk_network-0.0.9b46.dist-info → risk_network-0.0.11.dist-info}/top_level.txt +0 -0
    
        risk/__init__.py
    CHANGED
    
    
    
        risk/annotations/annotations.py
    CHANGED
    
    | @@ -3,71 +3,36 @@ risk/annotations/annotations | |
| 3 3 | 
             
            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
         | 
| 4 4 | 
             
            """
         | 
| 5 5 |  | 
| 6 | 
            -
            import os
         | 
| 7 6 | 
             
            import re
         | 
| 8 | 
            -
            import zipfile
         | 
| 9 7 | 
             
            from collections import Counter
         | 
| 10 8 | 
             
            from itertools import compress
         | 
| 11 9 | 
             
            from typing import Any, Dict, List, Set
         | 
| 12 10 |  | 
| 13 11 | 
             
            import networkx as nx
         | 
| 14 | 
            -
            import nltk
         | 
| 15 12 | 
             
            import numpy as np
         | 
| 16 13 | 
             
            import pandas as pd
         | 
| 17 | 
            -
            from nltk.corpus import stopwords
         | 
| 18 | 
            -
            from nltk.stem import WordNetLemmatizer
         | 
| 19 14 | 
             
            from nltk.tokenize import word_tokenize
         | 
| 15 | 
            +
            from scipy.sparse import coo_matrix
         | 
| 20 16 |  | 
| 17 | 
            +
            from risk.annotations.nltk_setup import setup_nltk_resources
         | 
| 21 18 | 
             
            from risk.log import logger
         | 
| 22 | 
            -
            from scipy.sparse import coo_matrix
         | 
| 23 19 |  | 
| 24 20 |  | 
| 25 | 
            -
            def  | 
| 26 | 
            -
                """ | 
| 27 | 
            -
                 | 
| 28 | 
            -
             | 
| 29 | 
            -
                #  | 
| 30 | 
            -
                 | 
| 31 | 
            -
             | 
| 32 | 
            -
             | 
| 33 | 
            -
                 | 
| 34 | 
            -
             | 
| 35 | 
            -
             | 
| 36 | 
            -
             | 
| 37 | 
            -
             | 
| 38 | 
            -
             | 
| 39 | 
            -
             | 
| 40 | 
            -
                    nltk.data.find(resource_path)
         | 
| 41 | 
            -
                    return
         | 
| 42 | 
            -
                except LookupError:
         | 
| 43 | 
            -
                    print(f"Resource '{resource}' still not found after download. Checking for a ZIP file...")
         | 
| 44 | 
            -
             | 
| 45 | 
            -
                # Look for a ZIP file in all known NLTK data directories.
         | 
| 46 | 
            -
                for data_path in nltk.data.path:
         | 
| 47 | 
            -
                    zip_path = os.path.join(data_path, "corpora", f"{resource}.zip")
         | 
| 48 | 
            -
                    if os.path.isfile(zip_path):
         | 
| 49 | 
            -
                        print(f"Found ZIP file for '{resource}' at: {zip_path}")
         | 
| 50 | 
            -
                        target_dir = os.path.join(data_path, "corpora")
         | 
| 51 | 
            -
                        with zipfile.ZipFile(zip_path, "r") as z:
         | 
| 52 | 
            -
                            z.extractall(path=target_dir)
         | 
| 53 | 
            -
                        print(f"Unzipped '{resource}' successfully.")
         | 
| 54 | 
            -
                        break  # Stop after unzipping the first found ZIP.
         | 
| 55 | 
            -
             | 
| 56 | 
            -
                # Final check: Try to load the resource one last time.
         | 
| 57 | 
            -
                try:
         | 
| 58 | 
            -
                    nltk.data.find(resource_path)
         | 
| 59 | 
            -
                    print(f"Resource '{resource}' is now available.")
         | 
| 60 | 
            -
                except LookupError:
         | 
| 61 | 
            -
                    raise LookupError(f"Resource '{resource}' could not be found, downloaded, or unzipped.")
         | 
| 62 | 
            -
             | 
| 63 | 
            -
             | 
| 64 | 
            -
            # Ensure the NLTK stopwords and WordNet resources are available
         | 
| 65 | 
            -
            ensure_nltk_resource("stopwords")
         | 
| 66 | 
            -
            ensure_nltk_resource("wordnet")
         | 
| 67 | 
            -
            # Use NLTK's stopwords - load all languages
         | 
| 68 | 
            -
            STOP_WORDS = set(word for lang in stopwords.fileids() for word in stopwords.words(lang))
         | 
| 69 | 
            -
            # Initialize the WordNet lemmatizer, which is used for normalizing words
         | 
| 70 | 
            -
            LEMMATIZER = WordNetLemmatizer()
         | 
| 21 | 
            +
            def initialize_nltk():
         | 
| 22 | 
            +
                """Initialize all required NLTK components."""
         | 
| 23 | 
            +
                setup_nltk_resources()
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                # After resources are available, initialize the components
         | 
| 26 | 
            +
                from nltk.corpus import stopwords
         | 
| 27 | 
            +
                from nltk.stem import WordNetLemmatizer
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                global STOP_WORDS, LEMMATIZER
         | 
| 30 | 
            +
                STOP_WORDS = set(stopwords.words("english"))
         | 
| 31 | 
            +
                LEMMATIZER = WordNetLemmatizer()
         | 
| 32 | 
            +
             | 
| 33 | 
            +
             | 
| 34 | 
            +
            # Initialize NLTK components
         | 
| 35 | 
            +
            initialize_nltk()
         | 
| 71 36 |  | 
| 72 37 |  | 
| 73 38 | 
             
            def load_annotations(
         | 
| @@ -0,0 +1,85 @@ | |
| 1 | 
            +
            """
         | 
| 2 | 
            +
            risk/annotations/nltk_setup
         | 
| 3 | 
            +
            ~~~~~~~~~~~~~~~~~~~~~~~~~~~
         | 
| 4 | 
            +
            """
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            import os
         | 
| 7 | 
            +
            import zipfile
         | 
| 8 | 
            +
            from typing import List, Tuple
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            import nltk
         | 
| 11 | 
            +
            from nltk.data import find, path as nltk_data_path
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            from risk.log import logger
         | 
| 14 | 
            +
             | 
| 15 | 
            +
             | 
| 16 | 
            +
            def setup_nltk_resources(required_resources: List[Tuple[str, str]] = None) -> None:
         | 
| 17 | 
            +
                """Ensures all required NLTK resources are available and properly extracted.
         | 
| 18 | 
            +
                Uses NLTK's default paths and mechanisms.
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                Args:
         | 
| 21 | 
            +
                    required_resources (List[Tuple[str, str]], optional): List of required resources
         | 
| 22 | 
            +
                        to download and extract. Each tuple should contain the resource path within
         | 
| 23 | 
            +
                        NLTK data and the package name. Defaults to None.
         | 
| 24 | 
            +
                """
         | 
| 25 | 
            +
                if required_resources is None:
         | 
| 26 | 
            +
                    required_resources = [
         | 
| 27 | 
            +
                        ("tokenizers/punkt", "punkt"),
         | 
| 28 | 
            +
                        ("tokenizers/punkt_tab", "punkt_tab"),
         | 
| 29 | 
            +
                        ("corpora/stopwords", "stopwords"),
         | 
| 30 | 
            +
                        ("corpora/wordnet", "wordnet"),
         | 
| 31 | 
            +
                    ]
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                # Process each resource
         | 
| 34 | 
            +
                for resource_path, package_name in required_resources:
         | 
| 35 | 
            +
                    try:
         | 
| 36 | 
            +
                        # First try to find the resource - this is how NLTK checks if it's available
         | 
| 37 | 
            +
                        find(resource_path)
         | 
| 38 | 
            +
                    except LookupError:
         | 
| 39 | 
            +
                        # Resource not found, download it
         | 
| 40 | 
            +
                        logger.info(f"Downloading missing NLTK resource: {package_name}")
         | 
| 41 | 
            +
                        nltk.download(package_name, quiet=True)
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                    # Even if find() succeeded, the resource might be a zip that failed to extract
         | 
| 44 | 
            +
                    # Check if we need to manually extract zips
         | 
| 45 | 
            +
                    verify_and_extract_if_needed(resource_path, package_name)
         | 
| 46 | 
            +
             | 
| 47 | 
            +
             | 
| 48 | 
            +
            def verify_and_extract_if_needed(resource_path: str, package_name: str) -> None:
         | 
| 49 | 
            +
                """Verifies if the resource is properly extracted and extracts if needed. Respects
         | 
| 50 | 
            +
                NLTK's directory structure where the extracted content should be in the same directory
         | 
| 51 | 
            +
                as the zip file.
         | 
| 52 | 
            +
             | 
| 53 | 
            +
                Args:
         | 
| 54 | 
            +
                    resource_path (str): Path to the resource within NLTK data.
         | 
| 55 | 
            +
                    package_name (str): Name of the NLTK package.
         | 
| 56 | 
            +
                """
         | 
| 57 | 
            +
                # Get the directory and base name from the resource path
         | 
| 58 | 
            +
                path_parts = resource_path.split("/")
         | 
| 59 | 
            +
                resource_type = path_parts[0]  # 'corpora', 'tokenizers', etc.
         | 
| 60 | 
            +
                resource_name = path_parts[-1]  # 'wordnet', 'punkt', etc.
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                # Check all NLTK data directories
         | 
| 63 | 
            +
                for base in nltk_data_path:
         | 
| 64 | 
            +
                    # For resource paths like 'corpora/wordnet', the zip file is at '~/nltk_data/corpora/wordnet.zip'
         | 
| 65 | 
            +
                    # and the extracted directory should be at '~/nltk_data/corpora/wordnet'
         | 
| 66 | 
            +
                    resource_dir = os.path.join(base, resource_type)
         | 
| 67 | 
            +
                    zip_path = os.path.join(resource_dir, f"{resource_name}.zip")
         | 
| 68 | 
            +
                    folder_path = os.path.join(resource_dir, resource_name)
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                    # If zip exists but folder doesn't, extraction is needed
         | 
| 71 | 
            +
                    if os.path.exists(zip_path) and not os.path.exists(folder_path):
         | 
| 72 | 
            +
                        logger.info(f"Found unextracted zip for {package_name}, extracting...")
         | 
| 73 | 
            +
                        try:
         | 
| 74 | 
            +
                            with zipfile.ZipFile(zip_path, "r") as zf:
         | 
| 75 | 
            +
                                # Extract files to the same directory where the zip file is located
         | 
| 76 | 
            +
                                zf.extractall(path=resource_dir)
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                            if os.path.exists(folder_path):
         | 
| 79 | 
            +
                                logger.info(f"Successfully extracted {package_name}")
         | 
| 80 | 
            +
                            else:
         | 
| 81 | 
            +
                                logger.warning(
         | 
| 82 | 
            +
                                    f"Extraction completed but resource directory not found for {package_name}"
         | 
| 83 | 
            +
                                )
         | 
| 84 | 
            +
                        except Exception as e:
         | 
| 85 | 
            +
                            logger.error(f"Failed to extract {package_name}: {e}")
         | 
    
        risk/neighborhoods/domains.py
    CHANGED
    
    | @@ -42,7 +42,7 @@ def define_domains( | |
| 42 42 | 
             
                Args:
         | 
| 43 43 | 
             
                    top_annotations (pd.DataFrame): DataFrame of top annotations data for the network nodes.
         | 
| 44 44 | 
             
                    significant_neighborhoods_significance (np.ndarray): The binary significance matrix below alpha.
         | 
| 45 | 
            -
                    linkage_criterion (str): The clustering criterion for defining groups.
         | 
| 45 | 
            +
                    linkage_criterion (str): The clustering criterion for defining groups. Choose "off" to disable clustering.
         | 
| 46 46 | 
             
                    linkage_method (str): The linkage method for clustering. Choose "auto" to optimize.
         | 
| 47 47 | 
             
                    linkage_metric (str): The linkage metric for clustering. Choose "auto" to optimize.
         | 
| 48 48 | 
             
                    linkage_threshold (float, str): The threshold for clustering. Choose "auto" to optimize.
         | 
| @@ -249,23 +249,21 @@ def _optimize_silhouette_across_linkage_and_metrics( | |
| 249 249 | 
             
                    # Some linkage methods and metrics may not work with certain data
         | 
| 250 250 | 
             
                    try:
         | 
| 251 251 | 
             
                        Z = linkage(m, method=method, metric=metric)
         | 
| 252 | 
            -
                    except (ValueError, LinAlgError):
         | 
| 253 | 
            -
                        # If linkage fails, set a default threshold (a float) and a very poor score
         | 
| 254 | 
            -
                        current_threshold = 0.0
         | 
| 255 | 
            -
                        score = -float("inf")
         | 
| 256 | 
            -
                    else:
         | 
| 257 | 
            -
                        # Only optimize silhouette score if the threshold is "auto"
         | 
| 258 252 | 
             
                        if linkage_threshold == "auto":
         | 
| 259 | 
            -
                             | 
| 253 | 
            +
                            try:
         | 
| 254 | 
            +
                                threshold, score = _find_best_silhouette_score(Z, m, metric, linkage_criterion)
         | 
| 255 | 
            +
                            except (ValueError, LinAlgError):
         | 
| 256 | 
            +
                                continue  # Skip to the next combination
         | 
| 260 257 | 
             
                            current_threshold = threshold
         | 
| 261 258 | 
             
                        else:
         | 
| 262 | 
            -
                            # Use the provided threshold without optimization
         | 
| 263 259 | 
             
                            score = silhouette_score(
         | 
| 264 260 | 
             
                                m,
         | 
| 265 261 | 
             
                                fcluster(Z, linkage_threshold * np.max(Z[:, 2]), criterion=linkage_criterion),
         | 
| 266 262 | 
             
                                metric=metric,
         | 
| 267 263 | 
             
                            )
         | 
| 268 264 | 
             
                            current_threshold = linkage_threshold
         | 
| 265 | 
            +
                    except (ValueError, LinAlgError):
         | 
| 266 | 
            +
                        continue  # Skip to the next combination
         | 
| 269 267 |  | 
| 270 268 | 
             
                    if score > best_overall_score:
         | 
| 271 269 | 
             
                        best_overall_score = score
         | 
| @@ -290,7 +288,6 @@ def _find_best_silhouette_score( | |
| 290 288 | 
             
                linkage_criterion: str,
         | 
| 291 289 | 
             
                lower_bound: float = 0.001,
         | 
| 292 290 | 
             
                upper_bound: float = 1.0,
         | 
| 293 | 
            -
                resolution: float = 0.001,
         | 
| 294 291 | 
             
            ) -> Tuple[float, float]:
         | 
| 295 292 | 
             
                """Find the best silhouette score using binary search.
         | 
| 296 293 |  | 
| @@ -301,7 +298,6 @@ def _find_best_silhouette_score( | |
| 301 298 | 
             
                    linkage_criterion (str): Clustering criterion.
         | 
| 302 299 | 
             
                    lower_bound (float, optional): Lower bound for search. Defaults to 0.001.
         | 
| 303 300 | 
             
                    upper_bound (float, optional): Upper bound for search. Defaults to 1.0.
         | 
| 304 | 
            -
                    resolution (float, optional): Desired resolution for the best threshold. Defaults to 0.001.
         | 
| 305 301 |  | 
| 306 302 | 
             
                Returns:
         | 
| 307 303 | 
             
                    Tuple[float, float]:
         | 
| @@ -310,6 +306,7 @@ def _find_best_silhouette_score( | |
| 310 306 | 
             
                """
         | 
| 311 307 | 
             
                best_score = -np.inf
         | 
| 312 308 | 
             
                best_threshold = None
         | 
| 309 | 
            +
                minimum_linkage_threshold = 1e-6
         | 
| 313 310 |  | 
| 314 311 | 
             
                # Test lower bound
         | 
| 315 312 | 
             
                max_d_lower = np.max(Z[:, 2]) * lower_bound
         | 
| @@ -338,7 +335,7 @@ def _find_best_silhouette_score( | |
| 338 335 | 
             
                    lower_bound = (lower_bound + upper_bound) / 2
         | 
| 339 336 |  | 
| 340 337 | 
             
                # Binary search loop
         | 
| 341 | 
            -
                while upper_bound - lower_bound >  | 
| 338 | 
            +
                while upper_bound - lower_bound > minimum_linkage_threshold:
         | 
| 342 339 | 
             
                    mid_threshold = (upper_bound + lower_bound) / 2
         | 
| 343 340 | 
             
                    max_d_mid = np.max(Z[:, 2]) * mid_threshold
         | 
| 344 341 | 
             
                    clusters_mid = fcluster(Z, max_d_mid, criterion=linkage_criterion)
         | 
    
        risk/risk.py
    CHANGED
    
    | @@ -3,14 +3,13 @@ risk/risk | |
| 3 3 | 
             
            ~~~~~~~~~
         | 
| 4 4 | 
             
            """
         | 
| 5 5 |  | 
| 6 | 
            -
            from risk.network import NetworkIO
         | 
| 7 6 | 
             
            from risk.annotations import AnnotationsIO
         | 
| 7 | 
            +
            from risk.log import params, set_global_verbosity
         | 
| 8 8 | 
             
            from risk.neighborhoods import NeighborhoodsAPI
         | 
| 9 | 
            +
            from risk.network import NetworkIO
         | 
| 9 10 | 
             
            from risk.network.graph import GraphAPI
         | 
| 10 11 | 
             
            from risk.network.plotter import PlotterAPI
         | 
| 11 12 |  | 
| 12 | 
            -
            from risk.log import params, set_global_verbosity
         | 
| 13 | 
            -
             | 
| 14 13 |  | 
| 15 14 | 
             
            class RISK(NetworkIO, AnnotationsIO, NeighborhoodsAPI, GraphAPI, PlotterAPI):
         | 
| 16 15 | 
             
                """RISK: A class for network analysis and visualization.
         | 
| @@ -1,6 +1,6 @@ | |
| 1 1 | 
             
            Metadata-Version: 2.2
         | 
| 2 2 | 
             
            Name: risk-network
         | 
| 3 | 
            -
            Version: 0.0. | 
| 3 | 
            +
            Version: 0.0.11
         | 
| 4 4 | 
             
            Summary: A Python package for biological network analysis
         | 
| 5 5 | 
             
            Author: Ira Horecka
         | 
| 6 6 | 
             
            Author-email: Ira Horecka <ira89@icloud.com>
         | 
| @@ -699,7 +699,7 @@ Requires-Dist: leidenalg | |
| 699 699 | 
             
            Requires-Dist: markov_clustering
         | 
| 700 700 | 
             
            Requires-Dist: matplotlib
         | 
| 701 701 | 
             
            Requires-Dist: networkx
         | 
| 702 | 
            -
            Requires-Dist: nltk | 
| 702 | 
            +
            Requires-Dist: nltk
         | 
| 703 703 | 
             
            Requires-Dist: numpy
         | 
| 704 704 | 
             
            Requires-Dist: openpyxl
         | 
| 705 705 | 
             
            Requires-Dist: pandas
         | 
| @@ -726,92 +726,73 @@ Dynamic: requires-python | |
| 726 726 | 
             
            
         | 
| 727 727 | 
             
            [](https://doi.org/10.5281/zenodo.xxxxxxx)
         | 
| 728 728 | 
             
            
         | 
| 729 | 
            -
            
         | 
| 730 730 |  | 
| 731 | 
            -
            **RISK** (Regional Inference of Significant Kinships) is a next-generation tool  | 
| 731 | 
            +
            **RISK** (Regional Inference of Significant Kinships) is a next-generation tool for biological network annotation and visualization. RISK integrates community detection-based clustering, rigorous statistical enrichment analysis, and a modular framework to uncover biologically meaningful relationships and generate high-resolution visualizations. RISK supports diverse data formats and is optimized for large-scale network analysis, making it a valuable resource for researchers in systems biology and beyond.
         | 
| 732 732 |  | 
| 733 733 | 
             
            ## Documentation and Tutorial
         | 
| 734 734 |  | 
| 735 | 
            -
             | 
| 736 | 
            -
            - **Tutorial**: An interactive Jupyter notebook tutorial can be found [here](https://github.com/riskportal/network-tutorial).
         | 
| 737 | 
            -
            We highly recommend new users to consult the documentation and tutorial early on to fully leverage RISK's capabilities.
         | 
| 735 | 
            +
            An interactive Jupyter notebook tutorial can be found [here](https://github.com/riskportal/network-tutorial). We highly recommend new users to consult the documentation and tutorial early on to fully utilize RISK's capabilities.
         | 
| 738 736 |  | 
| 739 737 | 
             
            ## Installation
         | 
| 740 738 |  | 
| 741 | 
            -
            RISK is compatible with Python 3.8  | 
| 739 | 
            +
            RISK is compatible with Python 3.8 or later and runs on all major operating systems. To install the latest version of RISK, run:
         | 
| 742 740 |  | 
| 743 741 | 
             
            ```bash
         | 
| 744 | 
            -
            pip install risk-network
         | 
| 742 | 
            +
            pip install risk-network --upgrade
         | 
| 745 743 | 
             
            ```
         | 
| 746 744 |  | 
| 747 745 | 
             
            ## Features
         | 
| 748 746 |  | 
| 749 | 
            -
            - **Comprehensive Network Analysis**: Analyze biological networks  | 
| 750 | 
            -
            - **Advanced Clustering Algorithms**:  | 
| 751 | 
            -
            - **Flexible Visualization**:  | 
| 752 | 
            -
            - **Efficient Data Handling**:  | 
| 753 | 
            -
            - **Statistical Analysis**:  | 
| 747 | 
            +
            - **Comprehensive Network Analysis**: Analyze biological networks (e.g., protein–protein interaction and genetic interaction networks) as well as non-biological networks.
         | 
| 748 | 
            +
            - **Advanced Clustering Algorithms**: Supports Louvain, Leiden, Markov Clustering, Greedy Modularity, Label Propagation, Spinglass, and Walktrap for identifying structured network regions.
         | 
| 749 | 
            +
            - **Flexible Visualization**: Produce customizable, high-resolution network visualizations with kernel density estimate overlays, adjustable node and edge attributes, and export options in SVG, PNG, and PDF formats.
         | 
| 750 | 
            +
            - **Efficient Data Handling**: Supports multiple input/output formats, including JSON, CSV, TSV, Excel, Cytoscape, and GPickle.
         | 
| 751 | 
            +
            - **Statistical Analysis**: Assess functional enrichment using hypergeometric, permutation, binomial, chi-squared, Poisson, and z-score tests, ensuring statistical adaptability across datasets.
         | 
| 754 752 | 
             
            - **Cross-Domain Applicability**: Suitable for network analysis across biological and non-biological domains, including social and communication networks.
         | 
| 755 753 |  | 
| 756 754 | 
             
            ## Example Usage
         | 
| 757 755 |  | 
| 758 | 
            -
            We applied RISK to a *Saccharomyces cerevisiae* protein–protein interaction network,  | 
| 756 | 
            +
            We applied RISK to a *Saccharomyces cerevisiae* protein–protein interaction network from Michaelis et al. (2023), filtering for proteins with six or more interactions to emphasize core functional relationships. RISK identified compact, statistically enriched clusters corresponding to biological processes such as ribosomal assembly and mitochondrial organization.
         | 
| 759 757 |  | 
| 760 | 
            -
            ](https://i.imgur.com/lJHJrJr.jpeg)
         | 
| 761 759 |  | 
| 762 | 
            -
            RISK  | 
| 760 | 
            +
            This figure highlights RISK’s capability to detect both established and novel functional modules within the yeast interactome.
         | 
| 763 761 |  | 
| 764 762 | 
             
            ## Citation
         | 
| 765 763 |  | 
| 766 | 
            -
            If you use RISK in your research, please cite | 
| 764 | 
            +
            If you use RISK in your research, please cite:
         | 
| 767 765 |  | 
| 768 | 
            -
            **Horecka | 
| 766 | 
            +
            **Horecka et al.**, "RISK: a next-generation tool for biological network annotation and visualization", **Bioinformatics**, 2025. DOI: [10.1234/zenodo.xxxxxxx](https://doi.org/10.1234/zenodo.xxxxxxx)
         | 
| 769 767 |  | 
| 770 768 | 
             
            ## Software Architecture and Implementation
         | 
| 771 769 |  | 
| 772 | 
            -
            RISK features a streamlined, modular architecture designed to meet diverse research needs.  | 
| 770 | 
            +
            RISK features a streamlined, modular architecture designed to meet diverse research needs. It includes dedicated modules for:
         | 
| 773 771 |  | 
| 774 | 
            -
             | 
| 775 | 
            -
             | 
| 776 | 
            -
            - ** | 
| 777 | 
            -
            - **Visualization | 
| 778 | 
            -
             | 
| 779 | 
            -
            ### Clustering Algorithms
         | 
| 780 | 
            -
             | 
| 781 | 
            -
            - **Available Algorithms**:
         | 
| 782 | 
            -
              - Greedy Modularity
         | 
| 783 | 
            -
              - Label Propagation
         | 
| 784 | 
            -
              - Louvain
         | 
| 785 | 
            -
              - Markov Clustering
         | 
| 786 | 
            -
              - Spinglass
         | 
| 787 | 
            -
              - Walktrap
         | 
| 788 | 
            -
            - **Distance Metrics**: Supports both spherical and Euclidean distance metrics.
         | 
| 789 | 
            -
             | 
| 790 | 
            -
            ### Statistical Tests
         | 
| 791 | 
            -
             | 
| 792 | 
            -
            - **Hypergeometric Test**
         | 
| 793 | 
            -
            - **Permutation Test** (single- or multi-process modes)
         | 
| 794 | 
            -
            - **Poisson Test**
         | 
| 772 | 
            +
            - **Data I/O**: Supports JSON, CSV, TSV, Excel, Cytoscape, and GPickle formats.
         | 
| 773 | 
            +
            - **Clustering**: Supports multiple clustering methods, including Louvain, Leiden, Markov Clustering, Greedy Modularity, Label Propagation, Spinglass, and Walktrap. Provides flexible distance metrics tailored to network structure.
         | 
| 774 | 
            +
            - **Statistical Analysis**: Provides a suite of tests for overrepresentation analysis of annotations.
         | 
| 775 | 
            +
            - **Visualization**: Offers customizable, high-resolution output in multiple formats, including SVG, PNG, and PDF.
         | 
| 795 776 |  | 
| 796 777 | 
             
            ## Performance and Efficiency
         | 
| 797 778 |  | 
| 798 | 
            -
             | 
| 779 | 
            +
            Benchmarking results demonstrate that RISK efficiently scales to networks exceeding hundreds of thousands of edges, maintaining low execution times and optimal memory usage across statistical tests.
         | 
| 799 780 |  | 
| 800 781 | 
             
            ## Contributing
         | 
| 801 782 |  | 
| 802 | 
            -
            We welcome contributions from the community | 
| 783 | 
            +
            We welcome contributions from the community:
         | 
| 803 784 |  | 
| 804 | 
            -
            - [Issues Tracker](https://github.com/ | 
| 805 | 
            -
            - [Source Code](https://github.com/ | 
| 785 | 
            +
            - [Issues Tracker](https://github.com/riskportal/network/issues)
         | 
| 786 | 
            +
            - [Source Code](https://github.com/riskportal/network/tree/main/risk)
         | 
| 806 787 |  | 
| 807 788 | 
             
            ## Support
         | 
| 808 789 |  | 
| 809 | 
            -
            If you encounter issues or have suggestions for new features, please use the [Issues Tracker](https://github.com/ | 
| 790 | 
            +
            If you encounter issues or have suggestions for new features, please use the [Issues Tracker](https://github.com/riskportal/network/issues) on GitHub.
         | 
| 810 791 |  | 
| 811 792 | 
             
            ## License
         | 
| 812 793 |  | 
| 813 | 
            -
            RISK is  | 
| 794 | 
            +
            RISK is open source under the [GNU General Public License v3.0](https://www.gnu.org/licenses/gpl-3.0.en.html).
         | 
| 814 795 |  | 
| 815 796 | 
             
            ---
         | 
| 816 797 |  | 
| 817 | 
            -
            **Note**: For detailed documentation and to access the interactive tutorial, please visit the links  | 
| 798 | 
            +
            **Note**: For detailed documentation and to access the interactive tutorial, please visit the links above.
         | 
| @@ -1,15 +1,16 @@ | |
| 1 | 
            -
            risk/__init__.py,sha256= | 
| 2 | 
            -
            risk/risk.py,sha256= | 
| 1 | 
            +
            risk/__init__.py,sha256=FJYPkeBx_fYMZxCzecYrubpT9mJP2L2GpAs-kg7rhQY,120
         | 
| 2 | 
            +
            risk/risk.py,sha256=7Yu_Q3bRS05tMQyAyt3WYqVKphUpBo3DqpyrfjF9yC4,1103
         | 
| 3 3 | 
             
            risk/annotations/__init__.py,sha256=parsbcux1U4urpUqh9AdzbDWuLj9HlMidycMPkpSQFo,179
         | 
| 4 | 
            -
            risk/annotations/annotations.py,sha256= | 
| 4 | 
            +
            risk/annotations/annotations.py,sha256=KtFyCiCnoAkhin3HKDBtkNcz5imjpysrmEfQKUwyqh8,14737
         | 
| 5 5 | 
             
            risk/annotations/io.py,sha256=z1AJySsU-KL_IYuHa7j3nvuczmOHgK3WfaQ4TRunvrA,10499
         | 
| 6 | 
            +
            risk/annotations/nltk_setup.py,sha256=IvuyO3WkrmIg4gz1vsfjxUWBt9Nk-XxkQknPiFRORHE,3533
         | 
| 6 7 | 
             
            risk/log/__init__.py,sha256=7LxDysQu7doi0LAvlY2YbjN6iJH0fNknqy8lSLgeljo,217
         | 
| 7 8 | 
             
            risk/log/console.py,sha256=PgjyEvyhYLUSHXPUKEqOmxsDsfrjPICIgqo_cAHq0N8,4575
         | 
| 8 9 | 
             
            risk/log/parameters.py,sha256=VtwfMzLU1xI4yji3-Ch5vHjH-KdwTfwaEMmi7hFQTs0,5716
         | 
| 9 10 | 
             
            risk/neighborhoods/__init__.py,sha256=Q74HwTH7okI-vaskJPy2bYwb5sNjGASTzJ6m8V8arCU,234
         | 
| 10 11 | 
             
            risk/neighborhoods/api.py,sha256=ywngw2TQVV27gYlWDXcs8-qnmeepnvb-W9ov6J6VEPM,23341
         | 
| 11 12 | 
             
            risk/neighborhoods/community.py,sha256=5Q_-VAJC-5SY5EUsB8gIlemeDoAL85uLjyl16pItHiQ,16699
         | 
| 12 | 
            -
            risk/neighborhoods/domains.py,sha256= | 
| 13 | 
            +
            risk/neighborhoods/domains.py,sha256=INWgpAy4cLsfBb5exkh3MEAIgRreNH6d7UlKjVijHX4,14678
         | 
| 13 14 | 
             
            risk/neighborhoods/neighborhoods.py,sha256=l9FhADB1C-OxM8E9QXOcA4osUDgA1vs4ud-OCGKKybc,21457
         | 
| 14 15 | 
             
            risk/network/__init__.py,sha256=oVi3FA1XXKD84014Cykq-9bpX4_s0F3aAUfNOU-07Qw,73
         | 
| 15 16 | 
             
            risk/network/geometry.py,sha256=c9XweJ0-DImZPU2YAJTJAwVYnW_0OGlWClyvJpeIZE4,5476
         | 
| @@ -32,9 +33,9 @@ risk/stats/significance.py,sha256=6cKv2xBQXWTHZ6HpNWIqlNfKKS5pG_BcCUdMM3r_zw4,73 | |
| 32 33 | 
             
            risk/stats/stat_tests.py,sha256=tj0ri9w89_1fsjGLuafTWpfBEwZXpSLn7Ej2aAQ5lxk,11776
         | 
| 33 34 | 
             
            risk/stats/permutation/__init__.py,sha256=OLmYLm2uj96hPsSaUs0vUqFYw6Thwch_aHtpL7L0ZFw,127
         | 
| 34 35 | 
             
            risk/stats/permutation/permutation.py,sha256=BWjgdBpLVcHvmwHy0bmD4aJFccxifNBSrrCBPppyKf4,10569
         | 
| 35 | 
            -
            risk/stats/permutation/test_functions.py,sha256= | 
| 36 | 
            -
            risk_network-0.0. | 
| 37 | 
            -
            risk_network-0.0. | 
| 38 | 
            -
            risk_network-0.0. | 
| 39 | 
            -
            risk_network-0.0. | 
| 40 | 
            -
            risk_network-0.0. | 
| 36 | 
            +
            risk/stats/permutation/test_functions.py,sha256=0hcv18zqhhh2njWhUb1Yl-5PiFCYd4jX-HaY5hFMz4I,3121
         | 
| 37 | 
            +
            risk_network-0.0.11.dist-info/LICENSE,sha256=jOtLnuWt7d5Hsx6XXB2QxzrSe2sWWh3NgMfFRetluQM,35147
         | 
| 38 | 
            +
            risk_network-0.0.11.dist-info/METADATA,sha256=XmrzSj1VcALUEiN3g0JqxDm5EM8KB1jR_B8Y7oIXQ5Q,46959
         | 
| 39 | 
            +
            risk_network-0.0.11.dist-info/WHEEL,sha256=52BFRY2Up02UkjOa29eZOS2VxUrpPORXg1pkohGGUS8,91
         | 
| 40 | 
            +
            risk_network-0.0.11.dist-info/top_level.txt,sha256=NX7C2PFKTvC1JhVKv14DFlFAIFnKc6Lpsu1ZfxvQwVw,5
         | 
| 41 | 
            +
            risk_network-0.0.11.dist-info/RECORD,,
         | 
| 
            File without changes
         | 
| 
            File without changes
         | 
| 
            File without changes
         |