risk-network 0.0.12b0__py3-none-any.whl → 0.0.12b2__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.
Files changed (41) hide show
  1. risk/__init__.py +1 -1
  2. risk/annotations/__init__.py +10 -0
  3. risk/annotations/annotations.py +354 -0
  4. risk/annotations/io.py +241 -0
  5. risk/annotations/nltk_setup.py +86 -0
  6. risk/log/__init__.py +11 -0
  7. risk/log/console.py +141 -0
  8. risk/log/parameters.py +171 -0
  9. risk/neighborhoods/__init__.py +7 -0
  10. risk/neighborhoods/api.py +442 -0
  11. risk/neighborhoods/community.py +441 -0
  12. risk/neighborhoods/domains.py +360 -0
  13. risk/neighborhoods/neighborhoods.py +514 -0
  14. risk/neighborhoods/stats/__init__.py +13 -0
  15. risk/neighborhoods/stats/permutation/__init__.py +6 -0
  16. risk/neighborhoods/stats/permutation/permutation.py +240 -0
  17. risk/neighborhoods/stats/permutation/test_functions.py +70 -0
  18. risk/neighborhoods/stats/tests.py +275 -0
  19. risk/network/__init__.py +4 -0
  20. risk/network/graph/__init__.py +4 -0
  21. risk/network/graph/api.py +200 -0
  22. risk/network/graph/graph.py +274 -0
  23. risk/network/graph/stats.py +166 -0
  24. risk/network/graph/summary.py +253 -0
  25. risk/network/io.py +693 -0
  26. risk/network/plotter/__init__.py +4 -0
  27. risk/network/plotter/api.py +54 -0
  28. risk/network/plotter/canvas.py +291 -0
  29. risk/network/plotter/contour.py +329 -0
  30. risk/network/plotter/labels.py +935 -0
  31. risk/network/plotter/network.py +294 -0
  32. risk/network/plotter/plotter.py +141 -0
  33. risk/network/plotter/utils/colors.py +419 -0
  34. risk/network/plotter/utils/layout.py +94 -0
  35. risk_network-0.0.12b2.dist-info/METADATA +122 -0
  36. risk_network-0.0.12b2.dist-info/RECORD +40 -0
  37. {risk_network-0.0.12b0.dist-info → risk_network-0.0.12b2.dist-info}/WHEEL +1 -1
  38. risk_network-0.0.12b0.dist-info/METADATA +0 -796
  39. risk_network-0.0.12b0.dist-info/RECORD +0 -7
  40. {risk_network-0.0.12b0.dist-info → risk_network-0.0.12b2.dist-info}/licenses/LICENSE +0 -0
  41. {risk_network-0.0.12b0.dist-info → risk_network-0.0.12b2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,86 @@
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
12
+ from nltk.data import path as nltk_data_path
13
+
14
+ from risk.log import logger
15
+
16
+
17
+ def setup_nltk_resources(required_resources: List[Tuple[str, str]] = None) -> None:
18
+ """Ensures all required NLTK resources are available and properly extracted.
19
+ Uses NLTK's default paths and mechanisms.
20
+
21
+ Args:
22
+ required_resources (List[Tuple[str, str]], optional): List of required resources
23
+ to download and extract. Each tuple should contain the resource path within
24
+ NLTK data and the package name. Defaults to None.
25
+ """
26
+ if required_resources is None:
27
+ required_resources = [
28
+ ("tokenizers/punkt", "punkt"),
29
+ ("tokenizers/punkt_tab", "punkt_tab"),
30
+ ("corpora/stopwords", "stopwords"),
31
+ ("corpora/wordnet", "wordnet"),
32
+ ]
33
+
34
+ # Process each resource
35
+ for resource_path, package_name in required_resources:
36
+ try:
37
+ # First try to find the resource - this is how NLTK checks if it's available
38
+ find(resource_path)
39
+ except LookupError:
40
+ # Resource not found, download it
41
+ logger.info(f"Downloading missing NLTK resource: {package_name}")
42
+ nltk.download(package_name, quiet=True)
43
+
44
+ # Even if find() succeeded, the resource might be a zip that failed to extract
45
+ # Check if we need to manually extract zips
46
+ verify_and_extract_if_needed(resource_path, package_name)
47
+
48
+
49
+ def verify_and_extract_if_needed(resource_path: str, package_name: str) -> None:
50
+ """Verifies if the resource is properly extracted and extracts if needed. Respects
51
+ NLTK's directory structure where the extracted content should be in the same directory
52
+ as the zip file.
53
+
54
+ Args:
55
+ resource_path (str): Path to the resource within NLTK data.
56
+ package_name (str): Name of the NLTK package.
57
+ """
58
+ # Get the directory and base name from the resource path
59
+ path_parts = resource_path.split("/")
60
+ resource_type = path_parts[0] # 'corpora', 'tokenizers', etc.
61
+ resource_name = path_parts[-1] # 'wordnet', 'punkt', etc.
62
+
63
+ # Check all NLTK data directories
64
+ for base in nltk_data_path:
65
+ # For resource paths like 'corpora/wordnet', the zip file is at '~/nltk_data/corpora/wordnet.zip'
66
+ # and the extracted directory should be at '~/nltk_data/corpora/wordnet'
67
+ resource_dir = os.path.join(base, resource_type)
68
+ zip_path = os.path.join(resource_dir, f"{resource_name}.zip")
69
+ folder_path = os.path.join(resource_dir, resource_name)
70
+
71
+ # If zip exists but folder doesn't, extraction is needed
72
+ if os.path.exists(zip_path) and not os.path.exists(folder_path):
73
+ logger.info(f"Found unextracted zip for {package_name}, extracting...")
74
+ try:
75
+ with zipfile.ZipFile(zip_path, "r") as zf:
76
+ # Extract files to the same directory where the zip file is located
77
+ zf.extractall(path=resource_dir)
78
+
79
+ if os.path.exists(folder_path):
80
+ logger.info(f"Successfully extracted {package_name}")
81
+ else:
82
+ logger.warning(
83
+ f"Extraction completed but resource directory not found for {package_name}"
84
+ )
85
+ except Exception as e:
86
+ logger.error(f"Failed to extract {package_name}: {e}")
risk/log/__init__.py ADDED
@@ -0,0 +1,11 @@
1
+ """
2
+ risk/log
3
+ ~~~~~~~~
4
+ """
5
+
6
+ from risk.log.console import log_header, logger, set_global_verbosity
7
+ from risk.log.parameters import Params
8
+
9
+ # Initialize the global parameters logger
10
+ params = Params()
11
+ params.initialize()
risk/log/console.py ADDED
@@ -0,0 +1,141 @@
1
+ """
2
+ risk/log/console
3
+ ~~~~~~~~~~~~~~~~
4
+ """
5
+
6
+ import logging
7
+
8
+
9
+ def in_jupyter():
10
+ """Check if the code is running in a Jupyter notebook environment.
11
+
12
+ Returns:
13
+ bool: True if running in a Jupyter notebook or QtConsole, False otherwise.
14
+ """
15
+ try:
16
+ shell = get_ipython().__class__.__name__
17
+ if shell == "ZMQInteractiveShell": # Jupyter Notebook or QtConsole
18
+ return True
19
+ if shell == "TerminalInteractiveShell": # Terminal running IPython
20
+ return False
21
+
22
+ return False # Other type (?)
23
+ except NameError:
24
+ return False # Not in Jupyter
25
+
26
+
27
+ # Define the MockLogger class to replicate logging behavior with print statements in Jupyter
28
+ class MockLogger:
29
+ """MockLogger: A lightweight logger replacement using print statements in Jupyter.
30
+
31
+ The MockLogger class replicates the behavior of a standard logger using print statements
32
+ to display messages. This is primarily used in a Jupyter environment to show outputs
33
+ directly in the notebook. The class supports logging levels such as `info`, `debug`,
34
+ `warning`, and `error`, while the `verbose` attribute controls whether to display non-error messages.
35
+ """
36
+
37
+ def __init__(self, verbose: bool = True):
38
+ """Initialize the MockLogger with verbosity settings.
39
+
40
+ Args:
41
+ verbose (bool): If True, display all log messages (info, debug, warning).
42
+ If False, only display error messages. Defaults to True.
43
+ """
44
+ self.verbose = verbose
45
+
46
+ def info(self, message: str) -> None:
47
+ """Display an informational message.
48
+
49
+ Args:
50
+ message (str): The informational message to be printed.
51
+ """
52
+ if self.verbose:
53
+ print(message)
54
+
55
+ def debug(self, message: str) -> None:
56
+ """Display a debug message.
57
+
58
+ Args:
59
+ message (str): The debug message to be printed.
60
+ """
61
+ if self.verbose:
62
+ print(message)
63
+
64
+ def warning(self, message: str) -> None:
65
+ """Display a warning message.
66
+
67
+ Args:
68
+ message (str): The warning message to be printed.
69
+ """
70
+ print(message)
71
+
72
+ def error(self, message: str) -> None:
73
+ """Display an error message.
74
+
75
+ Args:
76
+ message (str): The error message to be printed.
77
+ """
78
+ print(message)
79
+
80
+ def setLevel(self, level: int) -> None:
81
+ """Adjust verbosity based on the logging level.
82
+
83
+ Args:
84
+ level (int): Logging level to control message display.
85
+ - logging.DEBUG sets verbose to True (show all messages).
86
+ - logging.WARNING sets verbose to False (show only warning, error, and critical messages).
87
+ """
88
+ if level == logging.DEBUG:
89
+ self.verbose = True # Show all messages
90
+ elif level == logging.WARNING:
91
+ self.verbose = False # Suppress all except warning, error, and critical messages
92
+
93
+
94
+ # Set up logger based on environment
95
+ if not in_jupyter():
96
+ # Set up logger normally for .py files or terminal environments
97
+ logger = logging.getLogger("risk_logger")
98
+ logger.setLevel(logging.DEBUG)
99
+ console_handler = logging.StreamHandler()
100
+ console_handler.setLevel(logging.DEBUG)
101
+ console_handler.setFormatter(logging.Formatter("%(message)s"))
102
+
103
+ if not logger.hasHandlers():
104
+ logger.addHandler(console_handler)
105
+ else:
106
+ # If in Jupyter, use the MockLogger
107
+ logger = MockLogger()
108
+
109
+
110
+ def set_global_verbosity(verbose):
111
+ """Set the global verbosity level for the logger.
112
+
113
+ Args:
114
+ verbose (bool): Whether to display all log messages (True) or only error messages (False).
115
+
116
+ Returns:
117
+ None
118
+ """
119
+ if not isinstance(logger, MockLogger):
120
+ # For the regular logger, adjust logging levels
121
+ if verbose:
122
+ logger.setLevel(logging.DEBUG) # Show all messages
123
+ console_handler.setLevel(logging.DEBUG)
124
+ else:
125
+ logger.setLevel(logging.WARNING) # Show only warning, error, and critical messages
126
+ console_handler.setLevel(logging.WARNING)
127
+ else:
128
+ # For the MockLogger, set verbosity directly
129
+ logger.setLevel(logging.DEBUG if verbose else logging.WARNING)
130
+
131
+
132
+ def log_header(input_string: str) -> None:
133
+ """Log the input string as a header with a line of dashes above and below it.
134
+
135
+ Args:
136
+ input_string (str): The string to be printed as a header.
137
+ """
138
+ border = "-" * len(input_string)
139
+ logger.info(border)
140
+ logger.info(input_string)
141
+ logger.info(border)
risk/log/parameters.py ADDED
@@ -0,0 +1,171 @@
1
+ """
2
+ risk/log/parameters
3
+ ~~~~~~~~~~~~~~~~~~~
4
+ """
5
+
6
+ import csv
7
+ import json
8
+ import warnings
9
+ from datetime import datetime
10
+ from typing import Any, Dict
11
+
12
+ import numpy as np
13
+
14
+ from risk.log.console import log_header, logger
15
+
16
+ # Suppress all warnings - this is to resolve warnings from multiprocessing
17
+ warnings.filterwarnings("ignore")
18
+
19
+
20
+ class Params:
21
+ """Handles the storage and logging of various parameters for network analysis.
22
+
23
+ The Params class provides methods to log parameters related to different components of the analysis,
24
+ such as the network, annotations, neighborhoods, graph, and plotter settings. It also stores
25
+ the current datetime when the parameters were initialized.
26
+ """
27
+
28
+ def __init__(self):
29
+ """Initialize the Params object with default settings and current datetime."""
30
+ self.initialize()
31
+ self.datetime = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
32
+
33
+ def initialize(self) -> None:
34
+ """Initialize the parameter dictionaries for different components."""
35
+ self.network = {}
36
+ self.annotations = {}
37
+ self.neighborhoods = {}
38
+ self.graph = {}
39
+ self.plotter = {}
40
+
41
+ def log_network(self, **kwargs) -> None:
42
+ """Log network-related parameters.
43
+
44
+ Args:
45
+ **kwargs: Network parameters to log.
46
+ """
47
+ self.network = {**self.network, **kwargs}
48
+
49
+ def log_annotations(self, **kwargs) -> None:
50
+ """Log annotation-related parameters.
51
+
52
+ Args:
53
+ **kwargs: Annotation parameters to log.
54
+ """
55
+ self.annotations = {**self.annotations, **kwargs}
56
+
57
+ def log_neighborhoods(self, **kwargs) -> None:
58
+ """Log neighborhood-related parameters.
59
+
60
+ Args:
61
+ **kwargs: Neighborhood parameters to log.
62
+ """
63
+ self.neighborhoods = {**self.neighborhoods, **kwargs}
64
+
65
+ def log_graph(self, **kwargs) -> None:
66
+ """Log graph-related parameters.
67
+
68
+ Args:
69
+ **kwargs: Graph parameters to log.
70
+ """
71
+ self.graph = {**self.graph, **kwargs}
72
+
73
+ def log_plotter(self, **kwargs) -> None:
74
+ """Log plotter-related parameters.
75
+
76
+ Args:
77
+ **kwargs: Plotter parameters to log.
78
+ """
79
+ self.plotter = {**self.plotter, **kwargs}
80
+
81
+ def to_csv(self, filepath: str) -> None:
82
+ """Export the parameters to a CSV file.
83
+
84
+ Args:
85
+ filepath (str): The path where the CSV file will be saved.
86
+ """
87
+ # Load the parameter dictionary
88
+ params = self.load()
89
+ # Open the file in write mode
90
+ with open(filepath, "w", encoding="utf-8", newline="") as csv_file:
91
+ writer = csv.writer(csv_file)
92
+ # Write the header
93
+ writer.writerow(["parent_key", "child_key", "value"])
94
+ # Write the rows
95
+ for parent_key, parent_value in params.items():
96
+ if isinstance(parent_value, dict):
97
+ for child_key, child_value in parent_value.items():
98
+ writer.writerow([parent_key, child_key, child_value])
99
+ else:
100
+ writer.writerow([parent_key, "", parent_value])
101
+
102
+ logger.info(f"Parameters exported to CSV file: {filepath}")
103
+
104
+ def to_json(self, filepath: str) -> None:
105
+ """Export the parameters to a JSON file.
106
+
107
+ Args:
108
+ filepath (str): The path where the JSON file will be saved.
109
+ """
110
+ with open(filepath, "w", encoding="utf-8") as json_file:
111
+ json.dump(self.load(), json_file, indent=4)
112
+
113
+ logger.info(f"Parameters exported to JSON file: {filepath}")
114
+
115
+ def to_txt(self, filepath: str) -> None:
116
+ """Export the parameters to a text file.
117
+
118
+ Args:
119
+ filepath (str): The path where the text file will be saved.
120
+ """
121
+ # Load the parameter dictionary
122
+ params = self.load()
123
+ # Open the file in write mode
124
+ with open(filepath, "w", encoding="utf-8") as txt_file:
125
+ for key, value in params.items():
126
+ # Write the key and its corresponding value
127
+ txt_file.write(f"{key}: {value}\n")
128
+ # Add a blank line after each entry
129
+ txt_file.write("\n")
130
+
131
+ logger.info(f"Parameters exported to text file: {filepath}")
132
+
133
+ def load(self) -> Dict[str, Any]:
134
+ """Load and process various parameters, converting any np.ndarray values to lists.
135
+
136
+ Returns:
137
+ Dict[str, Any]: A dictionary containing the processed parameters.
138
+ """
139
+ log_header("Loading parameters")
140
+ return self._convert_ndarray_to_list(
141
+ {
142
+ "annotations": self.annotations,
143
+ "datetime": self.datetime,
144
+ "graph": self.graph,
145
+ "neighborhoods": self.neighborhoods,
146
+ "network": self.network,
147
+ "plotter": self.plotter,
148
+ }
149
+ )
150
+
151
+ def _convert_ndarray_to_list(self, d: Dict[str, Any]) -> Dict[str, Any]:
152
+ """Recursively convert all np.ndarray values in the dictionary to lists.
153
+
154
+ Args:
155
+ d (Dict[str, Any]): The dictionary to process.
156
+
157
+ Returns:
158
+ Dict[str, Any]: The processed dictionary with np.ndarray values converted to lists.
159
+ """
160
+ if isinstance(d, dict):
161
+ # Recursively process each value in the dictionary
162
+ return {k: self._convert_ndarray_to_list(v) for k, v in d.items()}
163
+ if isinstance(d, list):
164
+ # Recursively process each item in the list
165
+ return [self._convert_ndarray_to_list(v) for v in d]
166
+ if isinstance(d, np.ndarray):
167
+ # Convert numpy arrays to lists
168
+ return d.tolist()
169
+
170
+ # Return the value unchanged if it's not a dict, List, or ndarray
171
+ return d
@@ -0,0 +1,7 @@
1
+ """
2
+ risk/neighborhoods
3
+ ~~~~~~~~~~~~~~~~~~
4
+ """
5
+
6
+ from risk.neighborhoods.domains import define_domains, trim_domains
7
+ from risk.neighborhoods.neighborhoods import process_neighborhoods