risk-network 0.0.6b3__py3-none-any.whl → 0.0.6b4__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 CHANGED
@@ -7,4 +7,4 @@ RISK: RISK Infers Spatial Kinships
7
7
 
8
8
  from risk.risk import RISK
9
9
 
10
- __version__ = "0.0.6-beta.3"
10
+ __version__ = "0.0.6-beta.4"
risk/annotations/io.py CHANGED
@@ -153,6 +153,35 @@ 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], network: nx.Graph) -> Dict[str, Any]:
157
+ """Load annotations from a provided dictionary and convert them to a dictionary annotation.
158
+
159
+ Args:
160
+ content (dict): The annotations dictionary to load.
161
+ network (NetworkX graph): The network to which the annotations are related.
162
+
163
+ Returns:
164
+ dict: A dictionary containing ordered nodes, ordered annotations, and the annotations matrix.
165
+ """
166
+ # Ensure the input content is a dictionary
167
+ if not isinstance(content, dict):
168
+ raise TypeError(
169
+ f"Expected 'content' to be a dictionary, but got {type(content).__name__} instead."
170
+ )
171
+
172
+ filetype = "Dictionary"
173
+ # Log the loading of the annotations from the dictionary
174
+ params.log_annotations(filepath="In-memory dictionary", filetype=filetype)
175
+ _log_loading(filetype, "In-memory dictionary")
176
+
177
+ # Load the annotations into the provided network
178
+ annotations_dict = load_annotations(network, content)
179
+ # Ensure the output is a dictionary
180
+ if not isinstance(annotations_dict, dict):
181
+ raise ValueError("Expected output to be a dictionary")
182
+
183
+ return annotations_dict
184
+
156
185
 
157
186
  def _load_matrix_file(
158
187
  filepath: str,
@@ -35,26 +35,31 @@ def define_domains(
35
35
  Returns:
36
36
  pd.DataFrame: DataFrame with the primary domain for each node.
37
37
  """
38
- # Perform hierarchical clustering on the binary enrichment matrix
39
- m = significant_neighborhoods_enrichment[:, top_annotations["top attributes"]].T
40
- best_linkage, best_metric, best_threshold = _optimize_silhouette_across_linkage_and_metrics(
41
- m, linkage_criterion, linkage_method, linkage_metric
42
- )
43
- try:
44
- Z = linkage(m, method=best_linkage, metric=best_metric)
45
- except ValueError as e:
46
- raise ValueError("No significant annotations found.") from e
38
+ # Check if there's more than one column in significant_neighborhoods_enrichment
39
+ if significant_neighborhoods_enrichment.shape[1] == 1:
40
+ print("Single annotation detected. Skipping clustering.")
41
+ top_annotations["domain"] = 1 # Assign a default domain or handle appropriately
42
+ else:
43
+ # Perform hierarchical clustering on the binary enrichment matrix
44
+ m = significant_neighborhoods_enrichment[:, top_annotations["top attributes"]].T
45
+ best_linkage, best_metric, best_threshold = _optimize_silhouette_across_linkage_and_metrics(
46
+ m, linkage_criterion, linkage_method, linkage_metric
47
+ )
48
+ try:
49
+ Z = linkage(m, method=best_linkage, metric=best_metric)
50
+ except ValueError as e:
51
+ raise ValueError("No significant annotations found.") from e
47
52
 
48
- print(
49
- f"Linkage criterion: '{linkage_criterion}'\nLinkage method: '{best_linkage}'\nLinkage metric: '{best_metric}'"
50
- )
51
- print(f"Optimal linkage threshold: {round(best_threshold, 3)}")
53
+ print(
54
+ f"Linkage criterion: '{linkage_criterion}'\nLinkage method: '{best_linkage}'\nLinkage metric: '{best_metric}'"
55
+ )
56
+ print(f"Optimal linkage threshold: {round(best_threshold, 3)}")
52
57
 
53
- max_d_optimal = np.max(Z[:, 2]) * best_threshold
54
- domains = fcluster(Z, max_d_optimal, criterion=linkage_criterion)
55
- # Assign domains to the annotations matrix
56
- top_annotations["domain"] = 0
57
- top_annotations.loc[top_annotations["top attributes"], "domain"] = domains
58
+ max_d_optimal = np.max(Z[:, 2]) * best_threshold
59
+ domains = fcluster(Z, max_d_optimal, criterion=linkage_criterion)
60
+ # Assign domains to the annotations matrix
61
+ top_annotations["domain"] = 0
62
+ top_annotations.loc[top_annotations["top attributes"], "domain"] = domains
58
63
 
59
64
  # Create DataFrames to store domain information
60
65
  node_to_enrichment = pd.DataFrame(
@@ -63,6 +68,7 @@ def define_domains(
63
68
  )
64
69
  node_to_domain = node_to_enrichment.groupby(level="domain", axis=1).sum()
65
70
 
71
+ # Find the maximum enrichment score for each node
66
72
  t_max = node_to_domain.loc[:, 1:].max(axis=1)
67
73
  t_idxmax = node_to_domain.loc[:, 1:].idxmax(axis=1)
68
74
  t_idxmax[t_max == 0] = 0
risk/network/graph.py CHANGED
@@ -42,12 +42,16 @@ class NetworkGraph:
42
42
  node_enrichment_sums (np.ndarray): Array containing the enrichment sums for the nodes.
43
43
  """
44
44
  self.top_annotations = top_annotations
45
- self.domain_to_nodes = self._create_domain_to_nodes_map(domains)
45
+ self.domain_to_nodes_map = self._create_domain_to_nodes_map(domains)
46
46
  self.domains = domains
47
47
  self.trimmed_domain_to_term = self._create_domain_to_term_map(trimmed_domains)
48
48
  self.trimmed_domains = trimmed_domains
49
- self.node_label_to_id_map = node_label_to_id_map
50
49
  self.node_enrichment_sums = node_enrichment_sums
50
+ self.node_id_to_label_map = {v: k for k, v in node_label_to_id_map.items()}
51
+ self.node_label_to_enrichment_map = dict(
52
+ zip(node_label_to_id_map.keys(), node_enrichment_sums)
53
+ )
54
+ self.node_label_to_id_map = node_label_to_id_map
51
55
  # NOTE: self.network and self.node_coordinates are declared in _initialize_network
52
56
  self.network = None
53
57
  self.node_coordinates = None
@@ -63,12 +67,12 @@ class NetworkGraph:
63
67
  dict: A dictionary where keys are domain IDs and values are lists of nodes belonging to each domain.
64
68
  """
65
69
  cleaned_domains_matrix = domains.reset_index()[["index", "primary domain"]]
66
- node_to_domains = cleaned_domains_matrix.set_index("index")["primary domain"].to_dict()
67
- domain_to_nodes = defaultdict(list)
68
- for k, v in node_to_domains.items():
69
- domain_to_nodes[v].append(k)
70
+ node_to_domains_map = cleaned_domains_matrix.set_index("index")["primary domain"].to_dict()
71
+ domain_to_nodes_map = defaultdict(list)
72
+ for k, v in node_to_domains_map.items():
73
+ domain_to_nodes_map[v].append(k)
70
74
 
71
- return domain_to_nodes
75
+ return domain_to_nodes_map
72
76
 
73
77
  def _create_domain_to_term_map(self, trimmed_domains: pd.DataFrame) -> Dict[str, Any]:
74
78
  """Create a mapping from domain IDs to their corresponding terms.
@@ -154,7 +158,7 @@ class NetworkGraph:
154
158
  # Initialize composite colors array with shape (number of nodes, 4) for RGBA
155
159
  composite_colors = np.zeros((num_nodes, 4))
156
160
  # Assign colors to nodes based on domain_colors
157
- for domain_idx, nodes in self.domain_to_nodes.items():
161
+ for domain_idx, nodes in self.domain_to_nodes_map.items():
158
162
  color = domain_colors[domain_idx]
159
163
  for node in nodes:
160
164
  composite_colors[node] = color
@@ -185,7 +189,7 @@ class NetworkGraph:
185
189
  domain_colors = _get_colors(
186
190
  num_colors_to_generate=len(domains), cmap=cmap, color=color, random_seed=random_seed
187
191
  )
188
- return dict(zip(self.domain_to_nodes.keys(), domain_colors))
192
+ return dict(zip(self.domain_to_nodes_map.keys(), domain_colors))
189
193
 
190
194
 
191
195
  def _transform_colors(
risk/network/plot.py CHANGED
@@ -279,6 +279,7 @@ class NetworkPlotter:
279
279
  nodes: List,
280
280
  node_size: Union[int, np.ndarray] = 50,
281
281
  node_shape: str = "o",
282
+ node_edgewidth: float = 1.0, # Added node_edgewidth parameter
282
283
  edge_width: float = 1.0,
283
284
  node_color: Union[str, List, Tuple, np.ndarray] = "white",
284
285
  node_edgecolor: Union[str, List, Tuple, np.ndarray] = "black",
@@ -292,6 +293,7 @@ class NetworkPlotter:
292
293
  nodes (list): List of node labels to include in the subnetwork.
293
294
  node_size (int or np.ndarray, optional): Size of the nodes. Can be a single integer or an array of sizes. Defaults to 50.
294
295
  node_shape (str, optional): Shape of the nodes. Defaults to "o".
296
+ node_edgewidth (float, optional): Width of the node edges. Defaults to 1.0.
295
297
  edge_width (float, optional): Width of the edges. Defaults to 1.0.
296
298
  node_color (str, list, tuple, or np.ndarray, optional): Color of the nodes. Can be a single color or an array of colors. Defaults to "white".
297
299
  node_edgecolor (str, list, tuple, or np.ndarray, optional): Color of the node edges. Defaults to "black".
@@ -302,7 +304,6 @@ class NetworkPlotter:
302
304
  Raises:
303
305
  ValueError: If no valid nodes are found in the network graph.
304
306
  """
305
- # Don't log subnetwork parameters as they are specific to individual annotations
306
307
  # Filter to get node IDs and their coordinates
307
308
  node_ids = [
308
309
  self.graph.node_label_to_id_map.get(node)
@@ -312,10 +313,18 @@ class NetworkPlotter:
312
313
  if not node_ids:
313
314
  raise ValueError("No nodes found in the network graph.")
314
315
 
316
+ # If node_color is an array, match its length to the found node_ids
317
+ if not isinstance(node_color, str):
318
+ node_color = [
319
+ node_color[nodes.index(node)]
320
+ for node in nodes
321
+ if node in self.graph.node_label_to_id_map
322
+ ]
323
+
315
324
  # Convert colors to RGBA using the _to_rgba helper function
316
- node_color = _to_rgba(node_color, node_alpha)
317
- node_edgecolor = _to_rgba(node_edgecolor, 1.0) # Node edges usually fully opaque
318
- edge_color = _to_rgba(edge_color, edge_alpha)
325
+ node_color = _to_rgba(node_color, node_alpha, num_repeats=len(node_ids))
326
+ node_edgecolor = _to_rgba(node_edgecolor, 1.0, num_repeats=len(node_ids))
327
+ edge_color = _to_rgba(edge_color, edge_alpha, num_repeats=len(self.graph.network.edges))
319
328
  # Get the coordinates of the filtered nodes
320
329
  node_coordinates = {node_id: self.graph.node_coordinates[node_id] for node_id in node_ids}
321
330
 
@@ -328,6 +337,7 @@ class NetworkPlotter:
328
337
  node_shape=node_shape,
329
338
  node_color=node_color,
330
339
  edgecolors=node_edgecolor,
340
+ linewidths=node_edgewidth,
331
341
  ax=self.ax,
332
342
  )
333
343
  # Draw the edges between the specified nodes in the subnetwork
@@ -373,11 +383,11 @@ class NetworkPlotter:
373
383
  )
374
384
 
375
385
  # Ensure color is converted to RGBA with repetition matching the number of domains
376
- color = _to_rgba(color, alpha, num_repeats=len(self.graph.domain_to_nodes))
386
+ color = _to_rgba(color, alpha, num_repeats=len(self.graph.domain_to_nodes_map))
377
387
  # Extract node coordinates from the network graph
378
388
  node_coordinates = self.graph.node_coordinates
379
389
  # Draw contours for each domain in the network
380
- for idx, (_, nodes) in enumerate(self.graph.domain_to_nodes.items()):
390
+ for idx, (_, nodes) in enumerate(self.graph.domain_to_nodes_map.items()):
381
391
  if len(nodes) > 1:
382
392
  self._draw_kde_contour(
383
393
  self.ax,
@@ -580,9 +590,9 @@ class NetworkPlotter:
580
590
  )
581
591
 
582
592
  # Convert colors to RGBA using the _to_rgba helper function, applying alpha separately for font and arrow
583
- fontcolor = _to_rgba(fontcolor, fontalpha, num_repeats=len(self.graph.domain_to_nodes))
593
+ fontcolor = _to_rgba(fontcolor, fontalpha, num_repeats=len(self.graph.domain_to_nodes_map))
584
594
  arrow_color = _to_rgba(
585
- arrow_color, arrow_alpha, num_repeats=len(self.graph.domain_to_nodes)
595
+ arrow_color, arrow_alpha, num_repeats=len(self.graph.domain_to_nodes_map)
586
596
  )
587
597
 
588
598
  # Normalize words_to_omit to lowercase
@@ -591,7 +601,7 @@ class NetworkPlotter:
591
601
 
592
602
  # Calculate the center and radius of the network
593
603
  domain_centroids = {}
594
- for domain, nodes in self.graph.domain_to_nodes.items():
604
+ for domain, nodes in self.graph.domain_to_nodes_map.items():
595
605
  if nodes: # Skip if the domain has no nodes
596
606
  domain_centroids[domain] = self._calculate_domain_centroid(nodes)
597
607
 
@@ -811,9 +821,9 @@ class NetworkPlotter:
811
821
  Returns:
812
822
  np.ndarray: Array of node sizes, with enriched nodes larger than non-enriched ones.
813
823
  """
814
- # Merge all enriched nodes from the domain_to_nodes dictionary
824
+ # Merge all enriched nodes from the domain_to_nodes_map dictionary
815
825
  enriched_nodes = set()
816
- for _, nodes in self.graph.domain_to_nodes.items():
826
+ for _, nodes in self.graph.domain_to_nodes_map.items():
817
827
  enriched_nodes.update(nodes)
818
828
 
819
829
  # Initialize all node sizes to the non-enriched size
@@ -928,7 +938,7 @@ class NetworkPlotter:
928
938
  random_seed=random_seed,
929
939
  )
930
940
  annotated_colors = []
931
- for _, nodes in self.graph.domain_to_nodes.items():
941
+ for _, nodes in self.graph.domain_to_nodes_map.items():
932
942
  if len(nodes) > 1:
933
943
  # For multi-node domains, choose the brightest color based on RGB sum
934
944
  domain_colors = np.array([node_colors[node] for node in nodes])
@@ -969,33 +979,51 @@ def _to_rgba(
969
979
  alpha: float = 1.0,
970
980
  num_repeats: Union[int, None] = None,
971
981
  ) -> np.ndarray:
972
- """Convert a color or array of colors to RGBA format, applying or updating the alpha as needed.
982
+ """Convert a color or array of colors to RGBA format, applying alpha only if the color is RGB.
973
983
 
974
984
  Args:
975
985
  color (Union[str, list, tuple, np.ndarray]): The color(s) to convert. Can be a string, list, tuple, or np.ndarray.
976
- alpha (float, optional): Alpha value (transparency) to apply. Defaults to 1.0.
986
+ alpha (float, optional): Alpha value (transparency) to apply if the color is in RGB format. Defaults to 1.0.
977
987
  num_repeats (int or None, optional): If provided, the color will be repeated this many times. Defaults to None.
978
988
 
979
989
  Returns:
980
990
  np.ndarray: The RGBA color or array of RGBA colors.
981
991
  """
982
- # Handle single color case
992
+ # Handle single color case (string, RGB, or RGBA)
983
993
  if isinstance(color, str) or (
984
994
  isinstance(color, (list, tuple, np.ndarray)) and len(color) in [3, 4]
985
995
  ):
986
- rgba_color = np.array(mcolors.to_rgba(color, alpha))
987
- # Repeat the color if repeat argument is provided
996
+ rgba_color = np.array(mcolors.to_rgba(color))
997
+ # Only set alpha if the input is an RGB color or a string (not RGBA)
998
+ if len(rgba_color) == 4 and (
999
+ len(color) == 3 or isinstance(color, str)
1000
+ ): # If it's RGB or a string, set the alpha
1001
+ rgba_color[3] = alpha
1002
+
1003
+ # Repeat the color if num_repeats argument is provided
988
1004
  if num_repeats is not None:
989
1005
  return np.array([rgba_color] * num_repeats)
990
1006
 
991
1007
  return rgba_color
992
1008
 
993
- # Handle array of colors case
994
- elif isinstance(color, (list, tuple, np.ndarray)) and isinstance(
995
- color[0], (list, tuple, np.ndarray)
996
- ):
997
- rgba_colors = [mcolors.to_rgba(c, alpha) if len(c) == 3 else np.array(c) for c in color]
998
- # Repeat the colors if repeat argument is provided
1009
+ # Handle array of colors case (including strings, RGB, and RGBA)
1010
+ elif isinstance(color, (list, tuple, np.ndarray)):
1011
+ rgba_colors = []
1012
+ for c in color:
1013
+ # Ensure each element is either a valid string or a list/tuple of length 3 (RGB) or 4 (RGBA)
1014
+ if isinstance(c, str) or (
1015
+ isinstance(c, (list, tuple, np.ndarray)) and len(c) in [3, 4]
1016
+ ):
1017
+ rgba_c = np.array(mcolors.to_rgba(c))
1018
+ # Apply alpha only to RGB colors (not RGBA) and strings
1019
+ if len(rgba_c) == 4 and (len(c) == 3 or isinstance(c, str)):
1020
+ rgba_c[3] = alpha
1021
+
1022
+ rgba_colors.append(rgba_c)
1023
+ else:
1024
+ raise ValueError(f"Invalid color: {c}. Must be a valid RGB/RGBA or string color.")
1025
+
1026
+ # Repeat the colors if num_repeats argument is provided
999
1027
  if num_repeats is not None and len(rgba_colors) == 1:
1000
1028
  return np.array([rgba_colors[0]] * num_repeats)
1001
1029
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: risk-network
3
- Version: 0.0.6b3
3
+ Version: 0.0.6b4
4
4
  Summary: A Python package for biological network analysis
5
5
  Author: Ira Horecka
6
6
  Author-email: Ira Horecka <ira89@icloud.com>
@@ -1,21 +1,21 @@
1
- risk/__init__.py,sha256=WFP7SnF4dOZHRbKW9s80p106uY5Pdgn1CpT6cVK-g5k,112
1
+ risk/__init__.py,sha256=AGEvxxDrf1pHNa6sursCylT1nkxBgi_16zOuGB4SYbk,112
2
2
  risk/constants.py,sha256=XInRaH78Slnw_sWgAsBFbUHkyA0h0jL0DKGuQNbOvjM,550
3
3
  risk/risk.py,sha256=PONl5tzN5DSVUf4MgczfOvzGV-5JoAOLTQ6YWl10mZ8,20697
4
4
  risk/annotations/__init__.py,sha256=vUpVvMRE5if01Ic8QY6M2Ae3EFGJHdugEe9PdEkAW4Y,138
5
5
  risk/annotations/annotations.py,sha256=DRUTdGzMdqo62NWSapBUksbvPr9CrzD76qtOcxeNKmo,10554
6
- risk/annotations/io.py,sha256=qPrYgOn9-znCorHO_CWrkMW8N3s0-8ze65SvvQ5fuxo,8168
6
+ risk/annotations/io.py,sha256=lo7NKqOVkeeBp58JBxWJHtA0xjL5Yoxqe9Ox0daKlZk,9457
7
7
  risk/log/__init__.py,sha256=xuLImfxFlKpnVhzi_gDYlr2_c9cLkrw2c_3iEsXb1as,107
8
8
  risk/log/console.py,sha256=im9DRExwf6wHlcn9fewoDcKIpo3vPcorZIaNAl-0csY,355
9
9
  risk/log/params.py,sha256=Tbb-sovFTptGBqPDKafUA8KOpby4zFObutAT_Iti1hE,6302
10
10
  risk/neighborhoods/__init__.py,sha256=tKKEg4lsbqFukpgYlUGxU_v_9FOqK7V0uvM9T2QzoL0,206
11
11
  risk/neighborhoods/community.py,sha256=7ebo1Q5KokSQISnxZIh2SQxsKXdXm8aVkp-h_DiQ3K0,6818
12
- risk/neighborhoods/domains.py,sha256=HwuChmZH0RGD9eQOvk2-ezQDJRUHHn93vhVgHb-kX6I,10192
12
+ risk/neighborhoods/domains.py,sha256=5V--Nj-TrSdubhD_2PI57ffcn_PMSEgpX_iY5OjT6R8,10626
13
13
  risk/neighborhoods/neighborhoods.py,sha256=sHmjFFl2U5qV9YbQCRbpbI36j7dS7IFfFwwRb1_-AuM,13945
14
14
  risk/network/__init__.py,sha256=iEPeJdZfqp0toxtbElryB8jbz9_t_k4QQ3iDvKE8C_0,126
15
15
  risk/network/geometry.py,sha256=H1yGVVqgbfpzBzJwEheDLfvGLSA284jGQQTn612L4Vc,6759
16
- risk/network/graph.py,sha256=DhfNw2cBD238Kz0X1OSr5VLTW9h1n4cXvqUObxB4CAs,12892
16
+ risk/network/graph.py,sha256=scPFQIJjioup1FjQLyxNrAB17RmskY9MmvoFHrMlqNI,13135
17
17
  risk/network/io.py,sha256=gG50kOknO-D3HkW1HsbHMkTMvjUtn3l4W4Jwd-rXNr8,21202
18
- risk/network/plot.py,sha256=f-H6bTmoyu41e-93PTv8PqkUmGAxpRsrjnOcoExoKAc,53965
18
+ risk/network/plot.py,sha256=GjNZizcsJf6zjkEkvZv9LA456YmKbC2MO51AIzV5QOw,55342
19
19
  risk/stats/__init__.py,sha256=e-BE_Dr_jgiK6hKM-T-tlG4yvHnId8e5qjnM0pdwNVc,230
20
20
  risk/stats/fisher_exact.py,sha256=-bPwzu76-ob0HzrTV20mXUTot7v-MLuqFaAoab-QxPg,4966
21
21
  risk/stats/hypergeom.py,sha256=lrIFdhCWRjvM4apYw1MlOKqT_IY5OjtCwrjdtJdt6Tg,4954
@@ -23,8 +23,8 @@ risk/stats/stats.py,sha256=kvShov-94W6ffgDUTb522vB9hDJQSyTsYif_UIaFfSM,7059
23
23
  risk/stats/permutation/__init__.py,sha256=neJp7FENC-zg_CGOXqv-iIvz1r5XUKI9Ruxhmq7kDOI,105
24
24
  risk/stats/permutation/permutation.py,sha256=qLWdwxEY6nmkYPxpM8HLDcd2mbqYv9Qr7CKtJvhLqIM,9220
25
25
  risk/stats/permutation/test_functions.py,sha256=HuDIM-V1jkkfE1rlaIqrWWBSKZt3dQ1f-YEDjWpnLSE,2343
26
- risk_network-0.0.6b3.dist-info/LICENSE,sha256=jOtLnuWt7d5Hsx6XXB2QxzrSe2sWWh3NgMfFRetluQM,35147
27
- risk_network-0.0.6b3.dist-info/METADATA,sha256=WsFcvEtkZYU7Q3vyY49LRBayt3jCmGm8JQuIisjRqVU,43142
28
- risk_network-0.0.6b3.dist-info/WHEEL,sha256=cVxcB9AmuTcXqmwrtPhNK88dr7IR_b6qagTj0UvIEbY,91
29
- risk_network-0.0.6b3.dist-info/top_level.txt,sha256=NX7C2PFKTvC1JhVKv14DFlFAIFnKc6Lpsu1ZfxvQwVw,5
30
- risk_network-0.0.6b3.dist-info/RECORD,,
26
+ risk_network-0.0.6b4.dist-info/LICENSE,sha256=jOtLnuWt7d5Hsx6XXB2QxzrSe2sWWh3NgMfFRetluQM,35147
27
+ risk_network-0.0.6b4.dist-info/METADATA,sha256=MxZC6-0dLvPwBCCA5nL2NnnamuhaRA5uSmFK0o13DPU,43142
28
+ risk_network-0.0.6b4.dist-info/WHEEL,sha256=cVxcB9AmuTcXqmwrtPhNK88dr7IR_b6qagTj0UvIEbY,91
29
+ risk_network-0.0.6b4.dist-info/top_level.txt,sha256=NX7C2PFKTvC1JhVKv14DFlFAIFnKc6Lpsu1ZfxvQwVw,5
30
+ risk_network-0.0.6b4.dist-info/RECORD,,