risk-network 0.0.7b11__py3-none-any.whl → 0.0.8b0__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 +1 -1
- risk/annotations/io.py +12 -12
- risk/log/config.py +108 -17
- risk/neighborhoods/domains.py +2 -2
- risk/neighborhoods/neighborhoods.py +2 -2
- risk/network/io.py +12 -12
- risk/network/plot.py +124 -17
- risk/risk.py +14 -21
- risk/stats/permutation/permutation.py +20 -7
- {risk_network-0.0.7b11.dist-info → risk_network-0.0.8b0.dist-info}/METADATA +1 -1
- risk_network-0.0.8b0.dist-info/RECORD +30 -0
- risk_network-0.0.7b11.dist-info/RECORD +0 -30
- {risk_network-0.0.7b11.dist-info → risk_network-0.0.8b0.dist-info}/LICENSE +0 -0
- {risk_network-0.0.7b11.dist-info → risk_network-0.0.8b0.dist-info}/WHEEL +0 -0
- {risk_network-0.0.7b11.dist-info → risk_network-0.0.8b0.dist-info}/top_level.txt +0 -0
risk/__init__.py
CHANGED
risk/annotations/annotations.py
CHANGED
risk/annotations/io.py
CHANGED
@@ -25,12 +25,12 @@ class AnnotationsIO:
|
|
25
25
|
def __init__(self):
|
26
26
|
pass
|
27
27
|
|
28
|
-
def load_json_annotation(self,
|
28
|
+
def load_json_annotation(self, network: nx.Graph, filepath: str) -> Dict[str, Any]:
|
29
29
|
"""Load annotations from a JSON file and convert them to a DataFrame.
|
30
30
|
|
31
31
|
Args:
|
32
|
-
filepath (str): Path to the JSON annotations file.
|
33
32
|
network (NetworkX graph): The network to which the annotations are related.
|
33
|
+
filepath (str): Path to the JSON annotations file.
|
34
34
|
|
35
35
|
Returns:
|
36
36
|
dict: A dictionary containing ordered nodes, ordered annotations, and the annotations matrix.
|
@@ -49,8 +49,8 @@ class AnnotationsIO:
|
|
49
49
|
|
50
50
|
def load_excel_annotation(
|
51
51
|
self,
|
52
|
-
filepath: str,
|
53
52
|
network: nx.Graph,
|
53
|
+
filepath: str,
|
54
54
|
label_colname: str = "label",
|
55
55
|
nodes_colname: str = "nodes",
|
56
56
|
sheet_name: str = "Sheet1",
|
@@ -59,8 +59,8 @@ class AnnotationsIO:
|
|
59
59
|
"""Load annotations from an Excel file and associate them with the network.
|
60
60
|
|
61
61
|
Args:
|
62
|
-
filepath (str): Path to the Excel annotations file.
|
63
62
|
network (nx.Graph): The NetworkX graph to which the annotations are related.
|
63
|
+
filepath (str): Path to the Excel annotations file.
|
64
64
|
label_colname (str): Name of the column containing the labels (e.g., GO terms).
|
65
65
|
nodes_colname (str): Name of the column containing the nodes associated with each label.
|
66
66
|
sheet_name (str, optional): The name of the Excel sheet to load (default is 'Sheet1').
|
@@ -87,8 +87,8 @@ class AnnotationsIO:
|
|
87
87
|
|
88
88
|
def load_csv_annotation(
|
89
89
|
self,
|
90
|
-
filepath: str,
|
91
90
|
network: nx.Graph,
|
91
|
+
filepath: str,
|
92
92
|
label_colname: str = "label",
|
93
93
|
nodes_colname: str = "nodes",
|
94
94
|
nodes_delimiter: str = ";",
|
@@ -96,8 +96,8 @@ class AnnotationsIO:
|
|
96
96
|
"""Load annotations from a CSV file and associate them with the network.
|
97
97
|
|
98
98
|
Args:
|
99
|
-
filepath (str): Path to the CSV annotations file.
|
100
99
|
network (nx.Graph): The NetworkX graph to which the annotations are related.
|
100
|
+
filepath (str): Path to the CSV annotations file.
|
101
101
|
label_colname (str): Name of the column containing the labels (e.g., GO terms).
|
102
102
|
nodes_colname (str): Name of the column containing the nodes associated with each label.
|
103
103
|
nodes_delimiter (str, optional): Delimiter used to separate multiple nodes within the nodes column (default is ';').
|
@@ -121,8 +121,8 @@ class AnnotationsIO:
|
|
121
121
|
|
122
122
|
def load_tsv_annotation(
|
123
123
|
self,
|
124
|
-
filepath: str,
|
125
124
|
network: nx.Graph,
|
125
|
+
filepath: str,
|
126
126
|
label_colname: str = "label",
|
127
127
|
nodes_colname: str = "nodes",
|
128
128
|
nodes_delimiter: str = ";",
|
@@ -130,8 +130,8 @@ class AnnotationsIO:
|
|
130
130
|
"""Load annotations from a TSV file and associate them with the network.
|
131
131
|
|
132
132
|
Args:
|
133
|
-
filepath (str): Path to the TSV annotations file.
|
134
133
|
network (nx.Graph): The NetworkX graph to which the annotations are related.
|
134
|
+
filepath (str): Path to the TSV annotations file.
|
135
135
|
label_colname (str): Name of the column containing the labels (e.g., GO terms).
|
136
136
|
nodes_colname (str): Name of the column containing the nodes associated with each label.
|
137
137
|
nodes_delimiter (str, optional): Delimiter used to separate multiple nodes within the nodes column (default is ';').
|
@@ -153,12 +153,12 @@ class AnnotationsIO:
|
|
153
153
|
# Load the annotations into the provided network
|
154
154
|
return load_annotations(network, annotations_input)
|
155
155
|
|
156
|
-
def load_dict_annotation(self, content: Dict[str, Any]
|
156
|
+
def load_dict_annotation(self, network: nx.Graph, content: Dict[str, Any]) -> Dict[str, Any]:
|
157
157
|
"""Load annotations from a provided dictionary and convert them to a dictionary annotation.
|
158
158
|
|
159
159
|
Args:
|
160
|
-
content (dict): The annotations dictionary to load.
|
161
160
|
network (NetworkX graph): The network to which the annotations are related.
|
161
|
+
content (dict): The annotations dictionary to load.
|
162
162
|
|
163
163
|
Returns:
|
164
164
|
dict: A dictionary containing ordered nodes, ordered annotations, and the annotations matrix.
|
@@ -219,6 +219,6 @@ def _log_loading(filetype: str, filepath: str = "") -> None:
|
|
219
219
|
filepath (str, optional): The path to the file being loaded.
|
220
220
|
"""
|
221
221
|
log_header("Loading annotations")
|
222
|
-
logger.
|
222
|
+
logger.debug(f"Filetype: {filetype}")
|
223
223
|
if filepath:
|
224
|
-
logger.
|
224
|
+
logger.debug(f"Filepath: {filepath}")
|
risk/log/config.py
CHANGED
@@ -5,18 +5,104 @@ risk/log/config
|
|
5
5
|
|
6
6
|
import logging
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
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
|
+
elif shell == "TerminalInteractiveShell": # Terminal running IPython
|
20
|
+
return False
|
21
|
+
except NameError:
|
22
|
+
return False # Not in Jupyter
|
23
|
+
|
24
|
+
|
25
|
+
# Define the MockLogger class to replicate logging behavior with print statements in Jupyter
|
26
|
+
class MockLogger:
|
27
|
+
"""MockLogger: A lightweight logger replacement using print statements in Jupyter.
|
28
|
+
|
29
|
+
The MockLogger class replicates the behavior of a standard logger using print statements
|
30
|
+
to display messages. This is primarily used in a Jupyter environment to show outputs
|
31
|
+
directly in the notebook. The class supports logging levels such as `info`, `debug`,
|
32
|
+
`warning`, and `error`, while the `verbose` attribute controls whether to display non-error messages.
|
33
|
+
"""
|
34
|
+
|
35
|
+
def __init__(self, verbose: bool = True):
|
36
|
+
"""Initialize the MockLogger with verbosity settings.
|
37
|
+
|
38
|
+
Args:
|
39
|
+
verbose (bool): If True, display all log messages (info, debug, warning).
|
40
|
+
If False, only display error messages. Defaults to True.
|
41
|
+
"""
|
42
|
+
self.verbose = verbose
|
43
|
+
|
44
|
+
def info(self, message: str) -> None:
|
45
|
+
"""Display an informational message.
|
46
|
+
|
47
|
+
Args:
|
48
|
+
message (str): The informational message to be printed.
|
49
|
+
"""
|
50
|
+
if self.verbose:
|
51
|
+
print(message)
|
52
|
+
|
53
|
+
def debug(self, message: str) -> None:
|
54
|
+
"""Display a debug message.
|
55
|
+
|
56
|
+
Args:
|
57
|
+
message (str): The debug message to be printed.
|
58
|
+
"""
|
59
|
+
if self.verbose:
|
60
|
+
print(message)
|
61
|
+
|
62
|
+
def warning(self, message: str) -> None:
|
63
|
+
"""Display a warning message.
|
64
|
+
|
65
|
+
Args:
|
66
|
+
message (str): The warning message to be printed.
|
67
|
+
"""
|
68
|
+
print(message)
|
69
|
+
|
70
|
+
def error(self, message: str) -> None:
|
71
|
+
"""Display an error message.
|
72
|
+
|
73
|
+
Args:
|
74
|
+
message (str): The error message to be printed.
|
75
|
+
"""
|
76
|
+
print(message)
|
77
|
+
|
78
|
+
def setLevel(self, level: int) -> None:
|
79
|
+
"""Adjust verbosity based on the logging level.
|
80
|
+
|
81
|
+
Args:
|
82
|
+
level (int): Logging level to control message display.
|
83
|
+
- logging.DEBUG sets verbose to True (show all messages).
|
84
|
+
- logging.WARNING sets verbose to False (show only warning, error, and critical messages).
|
85
|
+
"""
|
86
|
+
if level == logging.DEBUG:
|
87
|
+
self.verbose = True # Show all messages
|
88
|
+
elif level == logging.WARNING:
|
89
|
+
self.verbose = False # Suppress all except warning, error, and critical messages
|
90
|
+
|
91
|
+
|
92
|
+
# Set up logger based on environment
|
93
|
+
if not in_jupyter():
|
94
|
+
# Set up logger normally for .py files or terminal environments
|
95
|
+
logger = logging.getLogger("risk_logger")
|
96
|
+
logger.setLevel(logging.DEBUG)
|
97
|
+
console_handler = logging.StreamHandler()
|
98
|
+
console_handler.setLevel(logging.DEBUG)
|
99
|
+
console_handler.setFormatter(logging.Formatter("%(message)s"))
|
100
|
+
|
101
|
+
if not logger.hasHandlers():
|
102
|
+
logger.addHandler(console_handler)
|
103
|
+
else:
|
104
|
+
# If in Jupyter, use the MockLogger
|
105
|
+
logger = MockLogger()
|
20
106
|
|
21
107
|
|
22
108
|
def set_global_verbosity(verbose):
|
@@ -28,12 +114,17 @@ def set_global_verbosity(verbose):
|
|
28
114
|
Returns:
|
29
115
|
None
|
30
116
|
"""
|
31
|
-
if
|
32
|
-
|
33
|
-
|
117
|
+
if not isinstance(logger, MockLogger):
|
118
|
+
# For the regular logger, adjust logging levels
|
119
|
+
if verbose:
|
120
|
+
logger.setLevel(logging.DEBUG) # Show all messages
|
121
|
+
console_handler.setLevel(logging.DEBUG)
|
122
|
+
else:
|
123
|
+
logger.setLevel(logging.WARNING) # Show only warning, error, and critical messages
|
124
|
+
console_handler.setLevel(logging.WARNING)
|
34
125
|
else:
|
35
|
-
|
36
|
-
|
126
|
+
# For the MockLogger, set verbosity directly
|
127
|
+
logger.setLevel(logging.DEBUG if verbose else logging.WARNING)
|
37
128
|
|
38
129
|
|
39
130
|
def log_header(input_string: str) -> None:
|
risk/neighborhoods/domains.py
CHANGED
@@ -46,10 +46,10 @@ def define_domains(
|
|
46
46
|
)
|
47
47
|
# Perform hierarchical clustering
|
48
48
|
Z = linkage(m, method=best_linkage, metric=best_metric)
|
49
|
-
logger.
|
49
|
+
logger.warning(
|
50
50
|
f"Linkage criterion: '{linkage_criterion}'\nLinkage method: '{best_linkage}'\nLinkage metric: '{best_metric}'"
|
51
51
|
)
|
52
|
-
logger.
|
52
|
+
logger.debug(f"Optimal linkage threshold: {round(best_threshold, 3)}")
|
53
53
|
# Calculate the optimal threshold for clustering
|
54
54
|
max_d_optimal = np.max(Z[:, 2]) * best_threshold
|
55
55
|
# Assign domains to the annotations matrix
|
@@ -130,7 +130,7 @@ def process_neighborhoods(
|
|
130
130
|
enrichment_matrix = neighborhoods["enrichment_matrix"]
|
131
131
|
binary_enrichment_matrix = neighborhoods["binary_enrichment_matrix"]
|
132
132
|
significant_enrichment_matrix = neighborhoods["significant_enrichment_matrix"]
|
133
|
-
logger.
|
133
|
+
logger.debug(f"Imputation depth: {impute_depth}")
|
134
134
|
if impute_depth:
|
135
135
|
(
|
136
136
|
enrichment_matrix,
|
@@ -143,7 +143,7 @@ def process_neighborhoods(
|
|
143
143
|
max_depth=impute_depth,
|
144
144
|
)
|
145
145
|
|
146
|
-
logger.
|
146
|
+
logger.debug(f"Pruning threshold: {prune_threshold}")
|
147
147
|
if prune_threshold:
|
148
148
|
(
|
149
149
|
enrichment_matrix,
|
risk/network/io.py
CHANGED
@@ -451,10 +451,10 @@ class NetworkIO:
|
|
451
451
|
# Log the number of nodes and edges before and after cleaning
|
452
452
|
num_final_nodes = G.number_of_nodes()
|
453
453
|
num_final_edges = G.number_of_edges()
|
454
|
-
logger.
|
455
|
-
logger.
|
456
|
-
logger.
|
457
|
-
logger.
|
454
|
+
logger.debug(f"Initial node count: {num_initial_nodes}")
|
455
|
+
logger.debug(f"Final node count: {num_final_nodes}")
|
456
|
+
logger.debug(f"Initial edge count: {num_initial_edges}")
|
457
|
+
logger.debug(f"Final edge count: {num_final_edges}")
|
458
458
|
|
459
459
|
def _assign_edge_weights(self, G: nx.Graph) -> None:
|
460
460
|
"""Assign weights to the edges in the graph.
|
@@ -472,7 +472,7 @@ class NetworkIO:
|
|
472
472
|
) # Default to 1.0 if 'weight' not present
|
473
473
|
|
474
474
|
if self.include_edge_weight and missing_weights:
|
475
|
-
logger.
|
475
|
+
logger.debug(f"Total edges missing weights: {missing_weights}")
|
476
476
|
|
477
477
|
def _validate_nodes(self, G: nx.Graph) -> None:
|
478
478
|
"""Validate the graph structure and attributes.
|
@@ -511,13 +511,13 @@ class NetworkIO:
|
|
511
511
|
filepath (str, optional): The path to the file being loaded. Defaults to "".
|
512
512
|
"""
|
513
513
|
log_header("Loading network")
|
514
|
-
logger.
|
514
|
+
logger.debug(f"Filetype: {filetype}")
|
515
515
|
if filepath:
|
516
|
-
logger.
|
517
|
-
logger.
|
516
|
+
logger.debug(f"Filepath: {filepath}")
|
517
|
+
logger.debug(f"Edge weight: {'Included' if self.include_edge_weight else 'Excluded'}")
|
518
518
|
if self.include_edge_weight:
|
519
|
-
logger.
|
520
|
-
logger.
|
521
|
-
logger.
|
519
|
+
logger.debug(f"Weight label: {self.weight_label}")
|
520
|
+
logger.debug(f"Minimum edges per node: {self.min_edges_per_node}")
|
521
|
+
logger.debug(f"Projection: {'Sphere' if self.compute_sphere else 'Plane'}")
|
522
522
|
if self.compute_sphere:
|
523
|
-
logger.
|
523
|
+
logger.debug(f"Surface depth: {self.surface_depth}")
|
risk/network/plot.py
CHANGED
@@ -10,10 +10,11 @@ import matplotlib.pyplot as plt
|
|
10
10
|
import networkx as nx
|
11
11
|
import numpy as np
|
12
12
|
import pandas as pd
|
13
|
+
from scipy import linalg
|
13
14
|
from scipy.ndimage import label
|
14
15
|
from scipy.stats import gaussian_kde
|
15
16
|
|
16
|
-
from risk.log import params
|
17
|
+
from risk.log import params, logger
|
17
18
|
from risk.network.graph import NetworkGraph
|
18
19
|
|
19
20
|
|
@@ -86,6 +87,83 @@ class NetworkPlotter:
|
|
86
87
|
|
87
88
|
return ax
|
88
89
|
|
90
|
+
def plot_title(
|
91
|
+
self,
|
92
|
+
title: Union[str, None] = None,
|
93
|
+
subtitle: Union[str, None] = None,
|
94
|
+
title_fontsize: int = 20,
|
95
|
+
subtitle_fontsize: int = 14,
|
96
|
+
font: str = "Arial",
|
97
|
+
title_color: str = "black",
|
98
|
+
subtitle_color: str = "gray",
|
99
|
+
title_y: float = 0.975,
|
100
|
+
title_space_offset: float = 0.075,
|
101
|
+
subtitle_offset: float = 0.025,
|
102
|
+
) -> None:
|
103
|
+
"""Plot title and subtitle on the network graph with customizable parameters.
|
104
|
+
|
105
|
+
Args:
|
106
|
+
title (str, optional): Title of the plot. Defaults to None.
|
107
|
+
subtitle (str, optional): Subtitle of the plot. Defaults to None.
|
108
|
+
title_fontsize (int, optional): Font size for the title. Defaults to 16.
|
109
|
+
subtitle_fontsize (int, optional): Font size for the subtitle. Defaults to 12.
|
110
|
+
font (str, optional): Font family used for both title and subtitle. Defaults to "Arial".
|
111
|
+
title_color (str, optional): Color of the title text. Defaults to "black".
|
112
|
+
subtitle_color (str, optional): Color of the subtitle text. Defaults to "gray".
|
113
|
+
title_y (float, optional): Y-axis position of the title. Defaults to 0.975.
|
114
|
+
title_space_offset (float, optional): Fraction of figure height to leave for the space above the plot. Defaults to 0.075.
|
115
|
+
subtitle_offset (float, optional): Offset factor to position the subtitle below the title. Defaults to 0.025.
|
116
|
+
"""
|
117
|
+
# Log the title and subtitle parameters
|
118
|
+
params.log_plotter(
|
119
|
+
title=title,
|
120
|
+
subtitle=subtitle,
|
121
|
+
title_fontsize=title_fontsize,
|
122
|
+
subtitle_fontsize=subtitle_fontsize,
|
123
|
+
title_subtitle_font=font,
|
124
|
+
title_color=title_color,
|
125
|
+
subtitle_color=subtitle_color,
|
126
|
+
subtitle_offset=subtitle_offset,
|
127
|
+
title_y=title_y,
|
128
|
+
title_space_offset=title_space_offset,
|
129
|
+
)
|
130
|
+
|
131
|
+
# Get the current figure and axis dimensions
|
132
|
+
fig = self.ax.figure
|
133
|
+
# Use a tight layout to ensure that title and subtitle do not overlap with the original plot
|
134
|
+
fig.tight_layout(
|
135
|
+
rect=[0, 0, 1, 1 - title_space_offset]
|
136
|
+
) # Leave space above the plot for title
|
137
|
+
|
138
|
+
# Plot title if provided
|
139
|
+
if title:
|
140
|
+
# Set the title using figure's suptitle to ensure centering
|
141
|
+
self.ax.figure.suptitle(
|
142
|
+
title,
|
143
|
+
fontsize=title_fontsize,
|
144
|
+
color=title_color,
|
145
|
+
fontname=font,
|
146
|
+
x=0.5, # Center the title horizontally
|
147
|
+
ha="center",
|
148
|
+
va="top",
|
149
|
+
y=title_y,
|
150
|
+
)
|
151
|
+
|
152
|
+
# Plot subtitle if provided
|
153
|
+
if subtitle:
|
154
|
+
# Calculate the subtitle's y position based on title's position and subtitle_offset
|
155
|
+
subtitle_y_position = title_y - subtitle_offset
|
156
|
+
self.ax.figure.text(
|
157
|
+
0.5, # Ensure horizontal centering for subtitle
|
158
|
+
subtitle_y_position,
|
159
|
+
subtitle,
|
160
|
+
ha="center",
|
161
|
+
va="top",
|
162
|
+
fontname=font,
|
163
|
+
fontsize=subtitle_fontsize,
|
164
|
+
color=subtitle_color,
|
165
|
+
)
|
166
|
+
|
89
167
|
def plot_circle_perimeter(
|
90
168
|
self,
|
91
169
|
scale: float = 1.0,
|
@@ -510,26 +588,52 @@ class NetworkPlotter:
|
|
510
588
|
# Extract the positions of the specified nodes
|
511
589
|
points = np.array([pos[n] for n in nodes])
|
512
590
|
if len(points) <= 1:
|
513
|
-
return # Not enough points to form a contour
|
591
|
+
return None # Not enough points to form a contour
|
514
592
|
|
593
|
+
# Check if the KDE forms a single connected component
|
515
594
|
connected = False
|
595
|
+
z = None # Initialize z to None to avoid UnboundLocalError
|
516
596
|
while not connected and bandwidth <= 100.0:
|
517
|
-
|
518
|
-
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
597
|
+
try:
|
598
|
+
# Perform KDE on the points with the given bandwidth
|
599
|
+
kde = gaussian_kde(points.T, bw_method=bandwidth)
|
600
|
+
xmin, ymin = points.min(axis=0) - bandwidth
|
601
|
+
xmax, ymax = points.max(axis=0) + bandwidth
|
602
|
+
x, y = np.mgrid[
|
603
|
+
xmin : xmax : complex(0, grid_size), ymin : ymax : complex(0, grid_size)
|
604
|
+
]
|
605
|
+
z = kde(np.vstack([x.ravel(), y.ravel()])).reshape(x.shape)
|
606
|
+
# Check if the KDE forms a single connected component
|
607
|
+
connected = _is_connected(z)
|
608
|
+
if not connected:
|
609
|
+
bandwidth += 0.05 # Increase bandwidth slightly and retry
|
610
|
+
except linalg.LinAlgError:
|
611
|
+
bandwidth += 0.05 # Increase bandwidth and retry
|
612
|
+
except Exception as e:
|
613
|
+
# Catch any other exceptions and log them
|
614
|
+
logger.error(f"Unexpected error when drawing KDE contour: {e}")
|
615
|
+
return None
|
616
|
+
|
617
|
+
# If z is still None, the KDE computation failed
|
618
|
+
if z is None:
|
619
|
+
logger.error("Failed to compute KDE. Skipping contour plot for these nodes.")
|
620
|
+
return None
|
529
621
|
|
530
622
|
# Define contour levels based on the density
|
531
623
|
min_density, max_density = z.min(), z.max()
|
624
|
+
if min_density == max_density:
|
625
|
+
logger.warning(
|
626
|
+
"Contour levels could not be created due to lack of variation in density."
|
627
|
+
)
|
628
|
+
return None
|
629
|
+
|
630
|
+
# Create contour levels based on the density values
|
532
631
|
contour_levels = np.linspace(min_density, max_density, levels)[1:]
|
632
|
+
if len(contour_levels) < 2 or not np.all(np.diff(contour_levels) > 0):
|
633
|
+
logger.error("Contour levels must be strictly increasing. Skipping contour plot.")
|
634
|
+
return None
|
635
|
+
|
636
|
+
# Set the contour color and linestyle
|
533
637
|
contour_colors = [color for _ in range(levels - 1)]
|
534
638
|
# Plot the filled contours using fill_alpha for transparency
|
535
639
|
if fill_alpha > 0:
|
@@ -554,6 +658,7 @@ class NetworkPlotter:
|
|
554
658
|
linewidths=linewidth,
|
555
659
|
alpha=alpha,
|
556
660
|
)
|
661
|
+
|
557
662
|
# Set linewidth for the contour lines to 0 for levels other than the base level
|
558
663
|
for i in range(1, len(contour_levels)):
|
559
664
|
c.collections[i].set_linewidth(0)
|
@@ -1090,14 +1195,16 @@ class NetworkPlotter:
|
|
1090
1195
|
return np.array(annotated_colors)
|
1091
1196
|
|
1092
1197
|
@staticmethod
|
1093
|
-
def savefig(*args, **kwargs) -> None:
|
1094
|
-
"""Save the current plot to a file.
|
1198
|
+
def savefig(*args, pad_inches: float = 0.5, dpi: int = 100, **kwargs) -> None:
|
1199
|
+
"""Save the current plot to a file with additional export options.
|
1095
1200
|
|
1096
1201
|
Args:
|
1097
1202
|
*args: Positional arguments passed to `plt.savefig`.
|
1203
|
+
pad_inches (float, optional): Padding around the figure when saving. Defaults to 0.5.
|
1204
|
+
dpi (int, optional): Dots per inch (DPI) for the exported image. Defaults to 300.
|
1098
1205
|
**kwargs: Keyword arguments passed to `plt.savefig`, such as filename and format.
|
1099
1206
|
"""
|
1100
|
-
plt.savefig(*args, bbox_inches="tight", **kwargs)
|
1207
|
+
plt.savefig(*args, bbox_inches="tight", pad_inches=pad_inches, dpi=dpi, **kwargs)
|
1101
1208
|
|
1102
1209
|
@staticmethod
|
1103
1210
|
def show(*args, **kwargs) -> None:
|
risk/risk.py
CHANGED
@@ -33,24 +33,17 @@ class RISK(NetworkIO, AnnotationsIO):
|
|
33
33
|
and performing network-based statistical analysis, such as neighborhood significance testing.
|
34
34
|
"""
|
35
35
|
|
36
|
-
def __init__(self,
|
36
|
+
def __init__(self, verbose: bool = True):
|
37
37
|
"""Initialize the RISK class with configuration settings.
|
38
38
|
|
39
39
|
Args:
|
40
40
|
verbose (bool): If False, suppresses all log messages to the console. Defaults to True.
|
41
|
-
*args: Variable length argument list.
|
42
|
-
**kwargs: Arbitrary keyword arguments.
|
43
|
-
|
44
|
-
Note:
|
45
|
-
- All *args and **kwargs are passed to NetworkIO's __init__ method.
|
46
|
-
- AnnotationsIO does not take any arguments and is initialized without them.
|
47
41
|
"""
|
48
42
|
# Set global verbosity for logging
|
49
43
|
set_global_verbosity(verbose)
|
50
44
|
# Initialize and log network parameters
|
51
45
|
params.initialize()
|
52
|
-
|
53
|
-
super().__init__(*args, **kwargs)
|
46
|
+
super().__init__()
|
54
47
|
|
55
48
|
@property
|
56
49
|
def params(self) -> params:
|
@@ -221,10 +214,10 @@ class RISK(NetworkIO, AnnotationsIO):
|
|
221
214
|
)
|
222
215
|
|
223
216
|
# Log and display permutation test settings
|
224
|
-
logger.
|
225
|
-
logger.
|
226
|
-
logger.
|
227
|
-
logger.
|
217
|
+
logger.debug(f"Neighborhood scoring metric: '{score_metric}'")
|
218
|
+
logger.debug(f"Null distribution: '{null_distribution}'")
|
219
|
+
logger.debug(f"Number of permutations: {num_permutations}")
|
220
|
+
logger.debug(f"Maximum workers: {max_workers}")
|
228
221
|
# Run permutation test to compute neighborhood significance
|
229
222
|
neighborhood_significance = compute_permutation_test(
|
230
223
|
neighborhoods=neighborhoods,
|
@@ -290,9 +283,9 @@ class RISK(NetworkIO, AnnotationsIO):
|
|
290
283
|
max_cluster_size=max_cluster_size,
|
291
284
|
)
|
292
285
|
|
293
|
-
logger.
|
294
|
-
logger.
|
295
|
-
logger.
|
286
|
+
logger.debug(f"p-value cutoff: {pval_cutoff}")
|
287
|
+
logger.debug(f"FDR BH cutoff: {fdr_cutoff}")
|
288
|
+
logger.debug(
|
296
289
|
f"Significance tail: '{tail}' ({'enrichment' if tail == 'right' else 'depletion' if tail == 'left' else 'both'})"
|
297
290
|
)
|
298
291
|
# Calculate significant neighborhoods based on the provided parameters
|
@@ -314,8 +307,8 @@ class RISK(NetworkIO, AnnotationsIO):
|
|
314
307
|
)
|
315
308
|
|
316
309
|
log_header("Finding top annotations")
|
317
|
-
logger.
|
318
|
-
logger.
|
310
|
+
logger.debug(f"Min cluster size: {min_cluster_size}")
|
311
|
+
logger.debug(f"Max cluster size: {max_cluster_size}")
|
319
312
|
# Define top annotations based on processed neighborhoods
|
320
313
|
top_annotations = self._define_top_annotations(
|
321
314
|
network=network,
|
@@ -414,9 +407,9 @@ class RISK(NetworkIO, AnnotationsIO):
|
|
414
407
|
else:
|
415
408
|
for_print_distance_metric = distance_metric
|
416
409
|
# Log and display neighborhood settings
|
417
|
-
logger.
|
418
|
-
logger.
|
419
|
-
logger.
|
410
|
+
logger.debug(f"Distance metric: '{for_print_distance_metric}'")
|
411
|
+
logger.debug(f"Edge length threshold: {edge_length_threshold}")
|
412
|
+
logger.debug(f"Random seed: {random_seed}")
|
420
413
|
|
421
414
|
# Compute neighborhoods based on the network and distance metric
|
422
415
|
neighborhoods = get_network_neighborhoods(
|
@@ -133,6 +133,7 @@ def _run_permutation_test(
|
|
133
133
|
observed_neighborhood_scores,
|
134
134
|
neighborhood_score_func,
|
135
135
|
subset_size + (1 if i < remainder else 0),
|
136
|
+
num_permutations,
|
136
137
|
progress_counter,
|
137
138
|
max_workers,
|
138
139
|
rng, # Pass the random number generator to each worker
|
@@ -144,11 +145,9 @@ def _run_permutation_test(
|
|
144
145
|
results = pool.starmap_async(_permutation_process_subset, params_list, chunksize=1)
|
145
146
|
|
146
147
|
# Update progress bar based on progress_counter
|
147
|
-
# NOTE: Waiting for results to be ready while updating progress bar gives a big improvement
|
148
|
-
# in performance, especially for large number of permutations and workers
|
149
148
|
while not results.ready():
|
150
149
|
progress.update(progress_counter.value - progress.n)
|
151
|
-
results.wait(0.
|
150
|
+
results.wait(0.1) # Wait for 100ms
|
152
151
|
# Ensure progress bar reaches 100%
|
153
152
|
progress.update(total_progress - progress.n)
|
154
153
|
|
@@ -167,6 +166,7 @@ def _permutation_process_subset(
|
|
167
166
|
observed_neighborhood_scores: np.ndarray,
|
168
167
|
neighborhood_score_func: Callable,
|
169
168
|
subset_size: int,
|
169
|
+
num_permutations: int,
|
170
170
|
progress_counter: ValueProxy,
|
171
171
|
max_workers: int,
|
172
172
|
rng: np.random.Generator,
|
@@ -180,6 +180,7 @@ def _permutation_process_subset(
|
|
180
180
|
observed_neighborhood_scores (np.ndarray): Observed neighborhood scores.
|
181
181
|
neighborhood_score_func (Callable): Function to calculate neighborhood scores.
|
182
182
|
subset_size (int): Number of permutations to run in this subset.
|
183
|
+
num_permutations (int): Number of total permutations across all subsets.
|
183
184
|
progress_counter (multiprocessing.managers.ValueProxy): Shared counter for tracking progress.
|
184
185
|
max_workers (int): Number of workers for multiprocessing.
|
185
186
|
rng (np.random.Generator): Random number generator object.
|
@@ -190,11 +191,15 @@ def _permutation_process_subset(
|
|
190
191
|
# Initialize local count matrices for this worker
|
191
192
|
local_counts_depletion = np.zeros(observed_neighborhood_scores.shape)
|
192
193
|
local_counts_enrichment = np.zeros(observed_neighborhood_scores.shape)
|
194
|
+
|
193
195
|
# NOTE: Limit the number of threads used by NumPy's BLAS implementation to 1 when more than one worker is used.
|
194
|
-
# This can help prevent oversubscription of CPU resources during multiprocessing, ensuring that each process
|
195
|
-
# doesn't use more than one CPU core.
|
196
196
|
limits = None if max_workers == 1 else 1
|
197
197
|
with threadpool_limits(limits=limits, user_api="blas"):
|
198
|
+
# Initialize a local counter for batched progress updates
|
199
|
+
local_progress = 0
|
200
|
+
# Calculate the modulo value based on total permutations for 1/100th frequency updates
|
201
|
+
modulo_value = max(1, num_permutations // 100)
|
202
|
+
|
198
203
|
for _ in range(subset_size):
|
199
204
|
# Permute the annotation matrix using the RNG
|
200
205
|
annotation_matrix_permut = annotation_matrix[rng.permutation(idxs)]
|
@@ -212,7 +217,15 @@ def _permutation_process_subset(
|
|
212
217
|
local_counts_enrichment,
|
213
218
|
permuted_neighborhood_scores >= observed_neighborhood_scores,
|
214
219
|
)
|
215
|
-
|
216
|
-
|
220
|
+
|
221
|
+
# Update local progress counter
|
222
|
+
local_progress += 1
|
223
|
+
# Update shared progress counter every 1/100th of total permutations
|
224
|
+
if local_progress % modulo_value == 0:
|
225
|
+
progress_counter.value += modulo_value
|
226
|
+
|
227
|
+
# Final progress update for any remaining iterations
|
228
|
+
if local_progress % modulo_value != 0:
|
229
|
+
progress_counter.value += modulo_value
|
217
230
|
|
218
231
|
return local_counts_depletion, local_counts_enrichment
|
@@ -0,0 +1,30 @@
|
|
1
|
+
risk/__init__.py,sha256=aUAtbxK2tEGJflgGKPod20oqObXxlzi7O-LYpsvOQ-Y,112
|
2
|
+
risk/constants.py,sha256=XInRaH78Slnw_sWgAsBFbUHkyA0h0jL0DKGuQNbOvjM,550
|
3
|
+
risk/risk.py,sha256=FaQhDCBZxZSAXJsScH0rSbjjCTNZA5vgf9rJj1GHW44,20924
|
4
|
+
risk/annotations/__init__.py,sha256=vUpVvMRE5if01Ic8QY6M2Ae3EFGJHdugEe9PdEkAW4Y,138
|
5
|
+
risk/annotations/annotations.py,sha256=ySc_N3nXnKx5RnOpFaEkM6zvTbswbrRcfFLzM0KdOck,11391
|
6
|
+
risk/annotations/io.py,sha256=TTXVJQgUGAlKpnGBcx7Dow146IGyozA03nSbl3S7M5M,9475
|
7
|
+
risk/log/__init__.py,sha256=aDUz5LMFQsz0UlsQI2EdXtiBKRLfml1UMeZKC7QQIGU,134
|
8
|
+
risk/log/config.py,sha256=m8pzj-hN4vI_2JdJUfyOoSvzT8_lhoIfBt27sKbnOes,4535
|
9
|
+
risk/log/params.py,sha256=DUmsqPo9hi3rQHFgLTunP14I-vVoyQSFZbx5aSYmVts,6363
|
10
|
+
risk/neighborhoods/__init__.py,sha256=tKKEg4lsbqFukpgYlUGxU_v_9FOqK7V0uvM9T2QzoL0,206
|
11
|
+
risk/neighborhoods/community.py,sha256=stYYBXeZlGLMV-k8ckQeIqThT6v9y-S3hETobAo9590,6817
|
12
|
+
risk/neighborhoods/domains.py,sha256=Ov52EEr-tWqy96y8_0tJ9f1K8FI-8tZQxHR7a59A1k8,10738
|
13
|
+
risk/neighborhoods/neighborhoods.py,sha256=M-wL4xB_BUTlSZg90swygO5NdrZ6hFUFqs6jsiZaqHk,18260
|
14
|
+
risk/network/__init__.py,sha256=iEPeJdZfqp0toxtbElryB8jbz9_t_k4QQ3iDvKE8C_0,126
|
15
|
+
risk/network/geometry.py,sha256=H1yGVVqgbfpzBzJwEheDLfvGLSA284jGQQTn612L4Vc,6759
|
16
|
+
risk/network/graph.py,sha256=_LEoom4EEowGALuJKSXcev9RAAHu2FqIeq3u7mkifW0,16479
|
17
|
+
risk/network/io.py,sha256=ASoKG4vkCC_aHwxlF4502W_SyaaCrRnHsTmRwL00spI,21266
|
18
|
+
risk/network/plot.py,sha256=9GcLKkH3CMEtraYnfdLXNJCi04rBQCjw4T6Q8k5yNOI,67091
|
19
|
+
risk/stats/__init__.py,sha256=WcgoETQ-hS0LQqKRsAMIPtP15xZ-4eul6VUBuUx4Wzc,220
|
20
|
+
risk/stats/hypergeom.py,sha256=o6Qnj31gCAKxr2uQirXrbv7XvdDJGEq69MFW-ubx_hA,2272
|
21
|
+
risk/stats/poisson.py,sha256=8x9hB4DCukq4gNIlIKO-c_jYG1-BTwTX53oLauFyfj8,1793
|
22
|
+
risk/stats/stats.py,sha256=kvShov-94W6ffgDUTb522vB9hDJQSyTsYif_UIaFfSM,7059
|
23
|
+
risk/stats/permutation/__init__.py,sha256=neJp7FENC-zg_CGOXqv-iIvz1r5XUKI9Ruxhmq7kDOI,105
|
24
|
+
risk/stats/permutation/permutation.py,sha256=D84Rcpt6iTQniK0PfQGcw9bLcHbMt9p-ARcurUnIXZQ,10095
|
25
|
+
risk/stats/permutation/test_functions.py,sha256=lftOude6hee0pyR80HlBD32522JkDoN5hrKQ9VEbuoY,2345
|
26
|
+
risk_network-0.0.8b0.dist-info/LICENSE,sha256=jOtLnuWt7d5Hsx6XXB2QxzrSe2sWWh3NgMfFRetluQM,35147
|
27
|
+
risk_network-0.0.8b0.dist-info/METADATA,sha256=eXxrrS0SSUOXYRxJKk_OVH_i-bhVbaAcVR6en-LbTEI,43142
|
28
|
+
risk_network-0.0.8b0.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
29
|
+
risk_network-0.0.8b0.dist-info/top_level.txt,sha256=NX7C2PFKTvC1JhVKv14DFlFAIFnKc6Lpsu1ZfxvQwVw,5
|
30
|
+
risk_network-0.0.8b0.dist-info/RECORD,,
|
@@ -1,30 +0,0 @@
|
|
1
|
-
risk/__init__.py,sha256=LDYtIaVSfLSXLCFAOtMfMWteX-C8tpOnI8OPnqr90bY,113
|
2
|
-
risk/constants.py,sha256=XInRaH78Slnw_sWgAsBFbUHkyA0h0jL0DKGuQNbOvjM,550
|
3
|
-
risk/risk.py,sha256=TYuZbEiEZugwZgR0Kr8kSXiH2GWE-6BxanSEZeSVpFU,21327
|
4
|
-
risk/annotations/__init__.py,sha256=vUpVvMRE5if01Ic8QY6M2Ae3EFGJHdugEe9PdEkAW4Y,138
|
5
|
-
risk/annotations/annotations.py,sha256=3FFyJE9Gp5oRN72_8iVAUnecsmTtx2G2fp5AlCY1oUk,11405
|
6
|
-
risk/annotations/io.py,sha256=ZcONDioLZjLQAbXhhoNHQRsY9nq-aTpyPtHGUSW7ka0,9473
|
7
|
-
risk/log/__init__.py,sha256=aDUz5LMFQsz0UlsQI2EdXtiBKRLfml1UMeZKC7QQIGU,134
|
8
|
-
risk/log/config.py,sha256=hXUewhm35wgc7UYQWmwy9YHDdmDwHP-bAUvi8edlFGM,1360
|
9
|
-
risk/log/params.py,sha256=DUmsqPo9hi3rQHFgLTunP14I-vVoyQSFZbx5aSYmVts,6363
|
10
|
-
risk/neighborhoods/__init__.py,sha256=tKKEg4lsbqFukpgYlUGxU_v_9FOqK7V0uvM9T2QzoL0,206
|
11
|
-
risk/neighborhoods/community.py,sha256=stYYBXeZlGLMV-k8ckQeIqThT6v9y-S3hETobAo9590,6817
|
12
|
-
risk/neighborhoods/domains.py,sha256=_N4E4EJoMNa7M7-6eHRJKzPYREd24kPPKdYQ3jTByxo,10734
|
13
|
-
risk/neighborhoods/neighborhoods.py,sha256=0hxi1jFoXQQX7eg94LfX6qrK_S7E4NGe5fIlOsAOg0Y,18258
|
14
|
-
risk/network/__init__.py,sha256=iEPeJdZfqp0toxtbElryB8jbz9_t_k4QQ3iDvKE8C_0,126
|
15
|
-
risk/network/geometry.py,sha256=H1yGVVqgbfpzBzJwEheDLfvGLSA284jGQQTn612L4Vc,6759
|
16
|
-
risk/network/graph.py,sha256=_LEoom4EEowGALuJKSXcev9RAAHu2FqIeq3u7mkifW0,16479
|
17
|
-
risk/network/io.py,sha256=LmjivifAZURzSaPs-dx3KkNDGmVnfUFJdj5WY0BZJI8,21254
|
18
|
-
risk/network/plot.py,sha256=3OucCoKJwx9M9H4lqAvcQdM9YiCSyIxz21jyqDbpffc,62286
|
19
|
-
risk/stats/__init__.py,sha256=WcgoETQ-hS0LQqKRsAMIPtP15xZ-4eul6VUBuUx4Wzc,220
|
20
|
-
risk/stats/hypergeom.py,sha256=o6Qnj31gCAKxr2uQirXrbv7XvdDJGEq69MFW-ubx_hA,2272
|
21
|
-
risk/stats/poisson.py,sha256=8x9hB4DCukq4gNIlIKO-c_jYG1-BTwTX53oLauFyfj8,1793
|
22
|
-
risk/stats/stats.py,sha256=kvShov-94W6ffgDUTb522vB9hDJQSyTsYif_UIaFfSM,7059
|
23
|
-
risk/stats/permutation/__init__.py,sha256=neJp7FENC-zg_CGOXqv-iIvz1r5XUKI9Ruxhmq7kDOI,105
|
24
|
-
risk/stats/permutation/permutation.py,sha256=kmSZ7bQ-AD0TFiQDgIwfxTeqHa4pjp7fIcOzAqyhUNY,9714
|
25
|
-
risk/stats/permutation/test_functions.py,sha256=lftOude6hee0pyR80HlBD32522JkDoN5hrKQ9VEbuoY,2345
|
26
|
-
risk_network-0.0.7b11.dist-info/LICENSE,sha256=jOtLnuWt7d5Hsx6XXB2QxzrSe2sWWh3NgMfFRetluQM,35147
|
27
|
-
risk_network-0.0.7b11.dist-info/METADATA,sha256=x4uCZ5QCWk-nyaVLFGSsZX1eJs21dbLSo1FpDbyQ83Y,43143
|
28
|
-
risk_network-0.0.7b11.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
29
|
-
risk_network-0.0.7b11.dist-info/top_level.txt,sha256=NX7C2PFKTvC1JhVKv14DFlFAIFnKc6Lpsu1ZfxvQwVw,5
|
30
|
-
risk_network-0.0.7b11.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|