risk-network 0.0.8b18__py3-none-any.whl → 0.0.9b26__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 (50) hide show
  1. risk/__init__.py +2 -2
  2. risk/annotations/__init__.py +2 -2
  3. risk/annotations/annotations.py +133 -72
  4. risk/annotations/io.py +50 -34
  5. risk/log/__init__.py +4 -2
  6. risk/log/{config.py → console.py} +5 -3
  7. risk/log/{params.py → parameters.py} +21 -46
  8. risk/neighborhoods/__init__.py +3 -5
  9. risk/neighborhoods/api.py +446 -0
  10. risk/neighborhoods/community.py +281 -96
  11. risk/neighborhoods/domains.py +92 -38
  12. risk/neighborhoods/neighborhoods.py +210 -149
  13. risk/network/__init__.py +1 -3
  14. risk/network/geometry.py +69 -58
  15. risk/network/graph/__init__.py +6 -0
  16. risk/network/graph/api.py +194 -0
  17. risk/network/graph/network.py +269 -0
  18. risk/network/graph/summary.py +254 -0
  19. risk/network/io.py +58 -48
  20. risk/network/plotter/__init__.py +6 -0
  21. risk/network/plotter/api.py +54 -0
  22. risk/network/{plot → plotter}/canvas.py +80 -26
  23. risk/network/{plot → plotter}/contour.py +43 -34
  24. risk/network/{plot → plotter}/labels.py +123 -113
  25. risk/network/plotter/network.py +424 -0
  26. risk/network/plotter/utils/colors.py +416 -0
  27. risk/network/plotter/utils/layout.py +94 -0
  28. risk/risk.py +11 -469
  29. risk/stats/__init__.py +8 -4
  30. risk/stats/binom.py +51 -0
  31. risk/stats/chi2.py +69 -0
  32. risk/stats/hypergeom.py +28 -18
  33. risk/stats/permutation/__init__.py +1 -1
  34. risk/stats/permutation/permutation.py +45 -39
  35. risk/stats/permutation/test_functions.py +25 -17
  36. risk/stats/poisson.py +17 -11
  37. risk/stats/stats.py +20 -16
  38. risk/stats/zscore.py +68 -0
  39. {risk_network-0.0.8b18.dist-info → risk_network-0.0.9b26.dist-info}/METADATA +9 -5
  40. risk_network-0.0.9b26.dist-info/RECORD +44 -0
  41. {risk_network-0.0.8b18.dist-info → risk_network-0.0.9b26.dist-info}/WHEEL +1 -1
  42. risk/network/graph.py +0 -159
  43. risk/network/plot/__init__.py +0 -6
  44. risk/network/plot/network.py +0 -282
  45. risk/network/plot/plotter.py +0 -137
  46. risk/network/plot/utils/color.py +0 -353
  47. risk/network/plot/utils/layout.py +0 -53
  48. risk_network-0.0.8b18.dist-info/RECORD +0 -37
  49. {risk_network-0.0.8b18.dist-info → risk_network-0.0.9b26.dist-info}/LICENSE +0 -0
  50. {risk_network-0.0.8b18.dist-info → risk_network-0.0.9b26.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,424 @@
1
+ """
2
+ risk/network/plot/network
3
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
4
+ """
5
+
6
+ from typing import Any, Dict, List, Tuple, Union
7
+
8
+ import matplotlib.pyplot as plt
9
+ import networkx as nx
10
+ import numpy as np
11
+
12
+ from risk.log import params
13
+ from risk.network.graph.network import NetworkGraph
14
+ from risk.network.plotter.canvas import Canvas
15
+ from risk.network.plotter.contour import Contour
16
+ from risk.network.plotter.labels import Labels
17
+ from risk.network.plotter.utils.colors import get_domain_colors, to_rgba
18
+ from risk.network.plotter.utils.layout import calculate_bounding_box
19
+
20
+
21
+ class Network:
22
+ """A class for plotting network graphs with customizable options.
23
+
24
+ The Network class provides methods to plot network graphs with flexible node and edge properties.
25
+ """
26
+
27
+ def __init__(self, graph: NetworkGraph, ax: Any = None) -> None:
28
+ """Initialize the NetworkPlotter class.
29
+
30
+ Args:
31
+ graph (NetworkGraph): The network data and attributes to be visualized.
32
+ ax (Any, optional): Axes object to plot the network graph. Defaults to None.
33
+ """
34
+ self.graph = graph
35
+ self.ax = ax
36
+
37
+ def plot_network(
38
+ self,
39
+ node_size: Union[int, np.ndarray] = 50,
40
+ node_shape: str = "o",
41
+ node_edgewidth: float = 1.0,
42
+ edge_width: float = 1.0,
43
+ node_color: Union[str, List, Tuple, np.ndarray] = "white",
44
+ node_edgecolor: Union[str, List, Tuple, np.ndarray] = "black",
45
+ edge_color: Union[str, List, Tuple, np.ndarray] = "black",
46
+ node_alpha: Union[float, None] = 1.0,
47
+ edge_alpha: Union[float, None] = 1.0,
48
+ ) -> None:
49
+ """Plot the network graph with customizable node colors, sizes, edge widths, and node edge widths.
50
+
51
+ Args:
52
+ node_size (int or np.ndarray, optional): Size of the nodes. Can be a single integer or an array of sizes. Defaults to 50.
53
+ node_shape (str, optional): Shape of the nodes. Defaults to "o".
54
+ node_edgewidth (float, optional): Width of the node edges. Defaults to 1.0.
55
+ edge_width (float, optional): Width of the edges. Defaults to 1.0.
56
+ node_color (str, List, Tuple, or np.ndarray, optional): Color of the nodes. Can be a single color or an array of colors.
57
+ Defaults to "white".
58
+ node_edgecolor (str, List, Tuple, or np.ndarray, optional): Color of the node edges. Can be a single color or an array of colors.
59
+ Defaults to "black".
60
+ edge_color (str, List, Tuple, or np.ndarray, optional): Color of the edges. Can be a single color or an array of colors.
61
+ Defaults to "black".
62
+ node_alpha (float, None, optional): Alpha value (transparency) for the nodes. If provided, it overrides any existing alpha
63
+ values found in node_color. Defaults to 1.0. Annotated node_color alphas will override this value.
64
+ edge_alpha (float, None, optional): Alpha value (transparency) for the edges. If provided, it overrides any existing alpha
65
+ values found in edge_color. Defaults to 1.0.
66
+ """
67
+ # Log the plotting parameters
68
+ params.log_plotter(
69
+ network_node_size=(
70
+ "custom" if isinstance(node_size, np.ndarray) else node_size
71
+ ), # np.ndarray usually indicates custom sizes
72
+ network_node_shape=node_shape,
73
+ network_node_edgewidth=node_edgewidth,
74
+ network_edge_width=edge_width,
75
+ network_node_color=(
76
+ "custom" if isinstance(node_color, np.ndarray) else node_color
77
+ ), # np.ndarray usually indicates custom colors
78
+ network_node_edgecolor=node_edgecolor,
79
+ network_edge_color=edge_color,
80
+ network_node_alpha=node_alpha,
81
+ network_edge_alpha=edge_alpha,
82
+ )
83
+
84
+ # Convert colors to RGBA using the to_rgba helper function
85
+ # If node_colors was generated using get_annotated_node_colors, its alpha values will override node_alpha
86
+ node_color_rgba = to_rgba(
87
+ color=node_color, alpha=node_alpha, num_repeats=len(self.graph.network.nodes)
88
+ )
89
+ node_edgecolor_rgba = to_rgba(
90
+ color=node_edgecolor, alpha=1.0, num_repeats=len(self.graph.network.nodes)
91
+ )
92
+ edge_color_rgba = to_rgba(
93
+ color=edge_color, alpha=edge_alpha, num_repeats=len(self.graph.network.edges)
94
+ )
95
+
96
+ # Extract node coordinates from the network graph
97
+ node_coordinates = self.graph.node_coordinates
98
+
99
+ # Draw the nodes of the graph
100
+ nx.draw_networkx_nodes(
101
+ self.graph.network,
102
+ pos=node_coordinates,
103
+ node_size=node_size,
104
+ node_shape=node_shape,
105
+ node_color=node_color_rgba,
106
+ edgecolors=node_edgecolor_rgba,
107
+ linewidths=node_edgewidth,
108
+ ax=self.ax,
109
+ )
110
+ # Draw the edges of the graph
111
+ nx.draw_networkx_edges(
112
+ self.graph.network,
113
+ pos=node_coordinates,
114
+ width=edge_width,
115
+ edge_color=edge_color_rgba,
116
+ ax=self.ax,
117
+ )
118
+
119
+ def plot_subnetwork(
120
+ self,
121
+ nodes: Union[List, Tuple, np.ndarray],
122
+ node_size: Union[int, np.ndarray] = 50,
123
+ node_shape: str = "o",
124
+ node_edgewidth: float = 1.0,
125
+ edge_width: float = 1.0,
126
+ node_color: Union[str, List, Tuple, np.ndarray] = "white",
127
+ node_edgecolor: Union[str, List, Tuple, np.ndarray] = "black",
128
+ edge_color: Union[str, List, Tuple, np.ndarray] = "black",
129
+ node_alpha: Union[float, None] = None,
130
+ edge_alpha: Union[float, None] = None,
131
+ ) -> None:
132
+ """Plot a subnetwork of selected nodes with customizable node and edge attributes.
133
+
134
+ Args:
135
+ nodes (List, Tuple, or np.ndarray): List of node labels to include in the subnetwork. Accepts nested lists.
136
+ node_size (int or np.ndarray, optional): Size of the nodes. Can be a single integer or an array of sizes. Defaults to 50.
137
+ node_shape (str, optional): Shape of the nodes. Defaults to "o".
138
+ node_edgewidth (float, optional): Width of the node edges. Defaults to 1.0.
139
+ edge_width (float, optional): Width of the edges. Defaults to 1.0.
140
+ node_color (str, List, Tuple, or np.ndarray, optional): Color of the nodes. Defaults to "white".
141
+ node_edgecolor (str, List, Tuple, or np.ndarray, optional): Color of the node edges. Defaults to "black".
142
+ edge_color (str, List, Tuple, or np.ndarray, optional): Color of the edges. Defaults to "black".
143
+ node_alpha (float, None, optional): Transparency for the nodes. If provided, it overrides any existing alpha values
144
+ found in node_color. Defaults to 1.0.
145
+ edge_alpha (float, None, optional): Transparency for the edges. If provided, it overrides any existing alpha values
146
+ found in node_color. Defaults to 1.0.
147
+
148
+ Raises:
149
+ ValueError: If no valid nodes are found in the network graph.
150
+ """
151
+ # Flatten nested lists of nodes, if necessary
152
+ if any(isinstance(item, (list, tuple, np.ndarray)) for item in nodes):
153
+ nodes = [node for sublist in nodes for node in sublist]
154
+
155
+ # Filter to get node IDs and their coordinates
156
+ node_ids = [
157
+ self.graph.node_label_to_node_id_map.get(node)
158
+ for node in nodes
159
+ if node in self.graph.node_label_to_node_id_map
160
+ ]
161
+ if not node_ids:
162
+ raise ValueError("No nodes found in the network graph.")
163
+
164
+ # Check if node_color is a single color or a list of colors
165
+ if not isinstance(node_color, (str, Tuple, np.ndarray)):
166
+ node_color = [
167
+ node_color[nodes.index(node)]
168
+ for node in nodes
169
+ if node in self.graph.node_label_to_node_id_map
170
+ ]
171
+
172
+ # Convert colors to RGBA using the to_rgba helper function
173
+ node_color_rgba = to_rgba(color=node_color, alpha=node_alpha, num_repeats=len(node_ids))
174
+ node_edgecolor_rgba = to_rgba(color=node_edgecolor, alpha=1.0, num_repeats=len(node_ids))
175
+ edge_color_rgba = to_rgba(
176
+ color=edge_color, alpha=edge_alpha, num_repeats=len(self.graph.network.edges)
177
+ )
178
+
179
+ # Get the coordinates of the filtered nodes
180
+ node_coordinates = {node_id: self.graph.node_coordinates[node_id] for node_id in node_ids}
181
+
182
+ # Draw the nodes in the subnetwork
183
+ nx.draw_networkx_nodes(
184
+ self.graph.network,
185
+ pos=node_coordinates,
186
+ nodelist=node_ids,
187
+ node_size=node_size,
188
+ node_shape=node_shape,
189
+ node_color=node_color_rgba,
190
+ edgecolors=node_edgecolor_rgba,
191
+ linewidths=node_edgewidth,
192
+ ax=self.ax,
193
+ )
194
+ # Draw the edges between the specified nodes in the subnetwork
195
+ subgraph = self.graph.network.subgraph(node_ids)
196
+ nx.draw_networkx_edges(
197
+ subgraph,
198
+ pos=node_coordinates,
199
+ width=edge_width,
200
+ edge_color=edge_color_rgba,
201
+ ax=self.ax,
202
+ )
203
+
204
+ def get_annotated_node_colors(
205
+ self,
206
+ cmap: str = "gist_rainbow",
207
+ color: Union[str, List, Tuple, np.ndarray, None] = None,
208
+ blend_colors: bool = False,
209
+ blend_gamma: float = 2.2,
210
+ min_scale: float = 0.8,
211
+ max_scale: float = 1.0,
212
+ scale_factor: float = 1.0,
213
+ alpha: Union[float, None] = 1.0,
214
+ nonsignificant_color: Union[str, List, Tuple, np.ndarray] = "white",
215
+ nonsignificant_alpha: Union[float, None] = 1.0,
216
+ ids_to_colors: Union[Dict[int, Any], None] = None,
217
+ random_seed: int = 888,
218
+ ) -> np.ndarray:
219
+ """Adjust the colors of nodes in the network graph based on significance.
220
+
221
+ Args:
222
+ cmap (str, optional): Colormap to use for coloring the nodes. Defaults to "gist_rainbow".
223
+ color (str, List, Tuple, np.ndarray, or None, optional): Color to use for the nodes. Can be a single color or an array of colors.
224
+ If None, the colormap will be used. Defaults to None.
225
+ blend_colors (bool, optional): Whether to blend colors for nodes with multiple domains. Defaults to False.
226
+ blend_gamma (float, optional): Gamma correction factor for perceptual color blending. Defaults to 2.2.
227
+ min_scale (float, optional): Minimum scale for color intensity. Defaults to 0.8.
228
+ max_scale (float, optional): Maximum scale for color intensity. Defaults to 1.0.
229
+ scale_factor (float, optional): Factor for adjusting the color scaling intensity. Defaults to 1.0.
230
+ alpha (float, None, optional): Alpha value for significant nodes. If provided, it overrides any existing alpha values found in `color`.
231
+ Defaults to 1.0.
232
+ nonsignificant_color (str, List, Tuple, or np.ndarray, optional): Color for non-significant nodes. Can be a single color or an array of colors.
233
+ Defaults to "white".
234
+ nonsignificant_alpha (float, None, optional): Alpha value for non-significant nodes. If provided, it overrides any existing alpha values found
235
+ in `nonsignificant_color`. Defaults to 1.0.
236
+ ids_to_colors (Dict[int, Any], None, optional): Mapping of domain IDs to specific colors. Defaults to None.
237
+ random_seed (int, optional): Seed for random number generation. Defaults to 888.
238
+
239
+ Returns:
240
+ np.ndarray: Array of RGBA colors adjusted for significance status.
241
+ """
242
+ # Get the initial domain colors for each node, which are returned as RGBA
243
+ network_colors = get_domain_colors(
244
+ graph=self.graph,
245
+ cmap=cmap,
246
+ color=color,
247
+ blend_colors=blend_colors,
248
+ blend_gamma=blend_gamma,
249
+ min_scale=min_scale,
250
+ max_scale=max_scale,
251
+ scale_factor=scale_factor,
252
+ ids_to_colors=ids_to_colors,
253
+ random_seed=random_seed,
254
+ )
255
+ # Apply the alpha value for significant nodes
256
+ network_colors[:, 3] = alpha # Apply the alpha value to the significant nodes' A channel
257
+ # Convert the non-significant color to RGBA using the to_rgba helper function
258
+ nonsignificant_color_rgba = to_rgba(
259
+ color=nonsignificant_color, alpha=nonsignificant_alpha, num_repeats=1
260
+ ) # num_repeats=1 for a single color
261
+ # Adjust node colors: replace any nodes where all three RGB values are equal and less than or equal to 0.1
262
+ # 0.1 is a predefined threshold for the minimum color intensity
263
+ adjusted_network_colors = np.where(
264
+ (
265
+ np.all(network_colors[:, :3] <= 0.1, axis=1)
266
+ & np.all(network_colors[:, :3] == network_colors[:, 0:1], axis=1)
267
+ )[:, None],
268
+ np.tile(
269
+ np.array(nonsignificant_color_rgba), (network_colors.shape[0], 1)
270
+ ), # Replace with the full RGBA non-significant color
271
+ network_colors, # Keep the original colors where no match is found
272
+ )
273
+ return adjusted_network_colors
274
+
275
+ def get_annotated_node_sizes(
276
+ self, significant_size: int = 50, nonsignificant_size: int = 25
277
+ ) -> np.ndarray:
278
+ """Adjust the sizes of nodes in the network graph based on whether they are significant or not.
279
+
280
+ Args:
281
+ significant_size (int): Size for significant nodes. Defaults to 50.
282
+ nonsignificant_size (int): Size for non-significant nodes. Defaults to 25.
283
+
284
+ Returns:
285
+ np.ndarray: Array of node sizes, with significant nodes larger than non-significant ones.
286
+ """
287
+ # Merge all significant nodes from the domain_id_to_node_ids_map dictionary
288
+ significant_nodes = set()
289
+ for _, node_ids in self.graph.domain_id_to_node_ids_map.items():
290
+ significant_nodes.update(node_ids)
291
+
292
+ # Initialize all node sizes to the non-significant size
293
+ node_sizes = np.full(len(self.graph.network.nodes), nonsignificant_size)
294
+ # Set the size for significant nodes
295
+ for node in significant_nodes:
296
+ if node in self.graph.network.nodes:
297
+ node_sizes[node] = significant_size
298
+
299
+ return node_sizes
300
+
301
+
302
+ class NetworkPlotter(Canvas, Network, Contour, Labels):
303
+ """A class for visualizing network graphs with customizable options.
304
+
305
+ The NetworkPlotter class uses a NetworkGraph object and provides methods to plot the network with
306
+ flexible node and edge properties. It also supports plotting labels, contours, drawing the network's
307
+ perimeter, and adjusting background colors.
308
+ """
309
+
310
+ def __init__(
311
+ self,
312
+ graph: NetworkGraph,
313
+ figsize: Tuple = (10, 10),
314
+ background_color: Union[str, List, Tuple, np.ndarray] = "white",
315
+ background_alpha: Union[float, None] = 1.0,
316
+ pad: float = 0.3,
317
+ ) -> None:
318
+ """Initialize the NetworkPlotter with a NetworkGraph object and plotting parameters.
319
+
320
+ Args:
321
+ graph (NetworkGraph): The network data and attributes to be visualized.
322
+ figsize (Tuple, optional): Size of the figure in inches (width, height). Defaults to (10, 10).
323
+ background_color (str, List, Tuple, np.ndarray, optional): Background color of the plot. Defaults to "white".
324
+ background_alpha (float, None, optional): Transparency level of the background color. If provided, it overrides
325
+ any existing alpha values found in background_color. Defaults to 1.0.
326
+ pad (float, optional): Padding value to adjust the axis limits. Defaults to 0.3.
327
+ """
328
+ self.graph = graph
329
+ # Initialize the plot with the specified parameters
330
+ self.ax = self._initialize_plot(
331
+ graph=graph,
332
+ figsize=figsize,
333
+ background_color=background_color,
334
+ background_alpha=background_alpha,
335
+ pad=pad,
336
+ )
337
+ super().__init__(graph=graph, ax=self.ax)
338
+
339
+ def _initialize_plot(
340
+ self,
341
+ graph: NetworkGraph,
342
+ figsize: Tuple,
343
+ background_color: Union[str, List, Tuple, np.ndarray],
344
+ background_alpha: Union[float, None],
345
+ pad: float,
346
+ ) -> plt.Axes:
347
+ """Set up the plot with figure size and background color.
348
+
349
+ Args:
350
+ graph (NetworkGraph): The network data and attributes to be visualized.
351
+ figsize (Tuple): Size of the figure in inches (width, height).
352
+ background_color (str, List, Tuple, or np.ndarray): Background color of the plot. Can be a single color or an array of colors.
353
+ background_alpha (float, None, optional): Transparency level of the background color. If provided, it overrides any existing
354
+ alpha values found in `background_color`.
355
+ pad (float, optional): Padding value to adjust the axis limits.
356
+
357
+ Returns:
358
+ plt.Axes: The axis object for the plot.
359
+ """
360
+ # Log the plotter settings
361
+ params.log_plotter(
362
+ figsize=figsize,
363
+ background_color=background_color,
364
+ background_alpha=background_alpha,
365
+ pad=pad,
366
+ )
367
+
368
+ # Extract node coordinates from the network graph
369
+ node_coordinates = graph.node_coordinates
370
+ # Calculate the center and radius of the bounding box around the network
371
+ center, radius = calculate_bounding_box(node_coordinates)
372
+
373
+ # Create a new figure and axis for plotting
374
+ fig, ax = plt.subplots(figsize=figsize)
375
+ fig.tight_layout() # Adjust subplot parameters to give specified padding
376
+ # Set axis limits based on the calculated bounding box and radius
377
+ ax.set_xlim([center[0] - radius - pad, center[0] + radius + pad])
378
+ ax.set_ylim([center[1] - radius - pad, center[1] + radius + pad])
379
+ ax.set_aspect("equal") # Ensure the aspect ratio is equal
380
+
381
+ # Set the background color of the plot
382
+ # Convert color to RGBA using the to_rgba helper function
383
+ fig.patch.set_facecolor(
384
+ to_rgba(color=background_color, alpha=background_alpha, num_repeats=1)
385
+ ) # num_repeats=1 for single color
386
+ ax.invert_yaxis() # Invert the y-axis to match typical image coordinates
387
+ # Remove axis spines for a cleaner look
388
+ for spine in ax.spines.values():
389
+ spine.set_visible(False)
390
+
391
+ # Hide axis ticks and labels
392
+ ax.set_xticks([])
393
+ ax.set_yticks([])
394
+ ax.patch.set_visible(False) # Hide the axis background
395
+
396
+ return ax
397
+
398
+ @staticmethod
399
+ def savefig(*args, pad_inches: float = 0.5, dpi: int = 100, **kwargs) -> None:
400
+ """Save the current plot to a file with additional export options.
401
+
402
+ Args:
403
+ *args: Positional arguments passed to `plt.savefig`.
404
+ pad_inches (float, optional): Padding around the figure when saving. Defaults to 0.5.
405
+ dpi (int, optional): Dots per inch (DPI) for the exported image. Defaults to 300.
406
+ **kwargs: Keyword arguments passed to `plt.savefig`, such as filename and format.
407
+ """
408
+ # Ensure user-provided kwargs take precedence
409
+ kwargs.setdefault("dpi", dpi)
410
+ kwargs.setdefault("pad_inches", pad_inches)
411
+ # Ensure the plot is saved with tight bounding box if not specified
412
+ kwargs.setdefault("bbox_inches", "tight")
413
+ # Call plt.savefig with combined arguments
414
+ plt.savefig(*args, **kwargs)
415
+
416
+ @staticmethod
417
+ def show(*args, **kwargs) -> None:
418
+ """Display the current plot.
419
+
420
+ Args:
421
+ *args: Positional arguments passed to `plt.show`.
422
+ **kwargs: Keyword arguments passed to `plt.show`.
423
+ """
424
+ plt.show(*args, **kwargs)