risk-network 0.0.9b35__py3-none-any.whl → 0.0.9b38__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/neighborhoods/domains.py +138 -89
- risk/network/graph/api.py +9 -2
- {risk_network-0.0.9b35.dist-info → risk_network-0.0.9b38.dist-info}/METADATA +1 -1
- {risk_network-0.0.9b35.dist-info → risk_network-0.0.9b38.dist-info}/RECORD +8 -8
- {risk_network-0.0.9b35.dist-info → risk_network-0.0.9b38.dist-info}/LICENSE +0 -0
- {risk_network-0.0.9b35.dist-info → risk_network-0.0.9b38.dist-info}/WHEEL +0 -0
- {risk_network-0.0.9b35.dist-info → risk_network-0.0.9b38.dist-info}/top_level.txt +0 -0
risk/__init__.py
CHANGED
risk/neighborhoods/domains.py
CHANGED
@@ -5,12 +5,13 @@ risk/neighborhoods/domains
|
|
5
5
|
|
6
6
|
from contextlib import suppress
|
7
7
|
from itertools import product
|
8
|
-
from typing import Tuple
|
8
|
+
from typing import Tuple, Union
|
9
9
|
|
10
10
|
import numpy as np
|
11
11
|
import pandas as pd
|
12
12
|
from scipy.cluster.hierarchy import linkage, fcluster
|
13
|
-
from
|
13
|
+
from scipy.optimize import minimize_scalar
|
14
|
+
from sklearn.metrics import calinski_harabasz_score, davies_bouldin_score, silhouette_score
|
14
15
|
from tqdm import tqdm
|
15
16
|
|
16
17
|
from risk.annotations import get_weighted_description
|
@@ -18,12 +19,19 @@ from risk.constants import GROUP_LINKAGE_METHODS, GROUP_DISTANCE_METRICS
|
|
18
19
|
from risk.log import logger
|
19
20
|
|
20
21
|
|
22
|
+
class LinkageThresholdError(Exception):
|
23
|
+
"""Exception raised for errors in the linkage threshold optimization process."""
|
24
|
+
|
25
|
+
pass
|
26
|
+
|
27
|
+
|
21
28
|
def define_domains(
|
22
29
|
top_annotations: pd.DataFrame,
|
23
30
|
significant_neighborhoods_significance: np.ndarray,
|
24
31
|
linkage_criterion: str,
|
25
32
|
linkage_method: str,
|
26
33
|
linkage_metric: str,
|
34
|
+
linkage_threshold: Union[str, float],
|
27
35
|
) -> pd.DataFrame:
|
28
36
|
"""Define domains and assign nodes to these domains based on their significance scores and clustering,
|
29
37
|
handling errors by assigning unique domains when clustering fails.
|
@@ -31,12 +39,19 @@ def define_domains(
|
|
31
39
|
Args:
|
32
40
|
top_annotations (pd.DataFrame): DataFrame of top annotations data for the network nodes.
|
33
41
|
significant_neighborhoods_significance (np.ndarray): The binary significance matrix below alpha.
|
34
|
-
linkage_criterion (str): The clustering criterion for defining groups.
|
35
|
-
|
36
|
-
|
42
|
+
linkage_criterion (str): The clustering criterion for defining groups. Use "distance" for distance-based
|
43
|
+
clustering or "maxclust" for a fixed number of clusters. Use "off" to skip clustering.
|
44
|
+
linkage_method (str): The linkage method for clustering. Use "auto" to try multiple methods.
|
45
|
+
linkage_metric (str): The linkage metric for clustering. Use "auto" to try multiple metrics.
|
46
|
+
linkage_threshold (str, float): The linkage threshold for clustering, or one of "silhouette",
|
47
|
+
"calinski_harabasz", or "davies_bouldin" to optimize the threshold.
|
37
48
|
|
38
49
|
Returns:
|
39
50
|
pd.DataFrame: DataFrame with the primary domain for each node.
|
51
|
+
|
52
|
+
Raises:
|
53
|
+
ValueError: If an improper value is passed for linkage_threshold. Acceptable values are "silhouette",
|
54
|
+
"calinski_harabasz", "davies_bouldin", or a float value.
|
40
55
|
"""
|
41
56
|
try:
|
42
57
|
if linkage_criterion == "off":
|
@@ -47,8 +62,10 @@ def define_domains(
|
|
47
62
|
# Safeguard the matrix by replacing NaN, Inf, and -Inf values
|
48
63
|
m = _safeguard_matrix(m)
|
49
64
|
# Optimize silhouette score across different linkage methods and distance metrics
|
50
|
-
best_linkage, best_metric, best_threshold =
|
51
|
-
|
65
|
+
best_linkage, best_metric, best_threshold = (
|
66
|
+
_optimize_linkage_threshold_across_methods_and_metrics(
|
67
|
+
m, linkage_criterion, linkage_method, linkage_metric, linkage_threshold
|
68
|
+
)
|
52
69
|
)
|
53
70
|
# Perform hierarchical clustering
|
54
71
|
Z = linkage(m, method=best_linkage, metric=best_metric)
|
@@ -74,6 +91,9 @@ def define_domains(
|
|
74
91
|
f"Error encountered. Skipping clustering and assigning {n_rows} unique domains."
|
75
92
|
)
|
76
93
|
top_annotations["domain"] = range(1, n_rows + 1) # Assign unique domains
|
94
|
+
except LinkageThresholdError as e:
|
95
|
+
# If a LinkageThresholdError is encountered, raise a ValueError with the original exception
|
96
|
+
raise ValueError(e) from e
|
77
97
|
|
78
98
|
# Create DataFrames to store domain information
|
79
99
|
node_to_significance = pd.DataFrame(
|
@@ -195,125 +215,154 @@ def _safeguard_matrix(matrix: np.ndarray) -> np.ndarray:
|
|
195
215
|
return matrix
|
196
216
|
|
197
217
|
|
198
|
-
def
|
199
|
-
m: np.ndarray,
|
218
|
+
def _optimize_linkage_threshold_across_methods_and_metrics(
|
219
|
+
m: np.ndarray,
|
220
|
+
linkage_criterion: str,
|
221
|
+
linkage_method: str,
|
222
|
+
linkage_metric: str,
|
223
|
+
linkage_threshold: Union[str, float],
|
200
224
|
) -> Tuple[str, str, float]:
|
201
|
-
"""Optimize
|
225
|
+
"""Optimize the linkage method, metric, and threshold for hierarchical clustering. If the threshold is
|
226
|
+
a string, optimize the threshold using the specified metric; otherwise, use the provided threshold.
|
202
227
|
|
203
228
|
Args:
|
204
229
|
m (np.ndarray): Data matrix.
|
205
|
-
linkage_criterion (str):
|
206
|
-
linkage_method (str): Linkage method for clustering.
|
207
|
-
linkage_metric (str):
|
230
|
+
linkage_criterion (str): Criterion for fcluster (typically "distance").
|
231
|
+
linkage_method (str): Linkage method for clustering, or "auto" to try multiple methods.
|
232
|
+
linkage_metric (str): Distance metric for clustering, or "auto" to try multiple metrics.
|
233
|
+
linkage_threshold (str, float): Either a numeric threshold or one of the following keywords:
|
234
|
+
"silhouette", "calinski_harabasz", or "davies_bouldin" to trigger optimization.
|
208
235
|
|
209
236
|
Returns:
|
210
237
|
Tuple[str, str, float]:
|
211
|
-
-
|
212
|
-
-
|
213
|
-
-
|
238
|
+
- The chosen linkage method.
|
239
|
+
- The chosen linkage metric.
|
240
|
+
- The optimized threshold (a float).
|
241
|
+
|
242
|
+
Raises:
|
243
|
+
ValueError: If linkage_threshold is neither one of the supported keywords nor convertible to float.
|
214
244
|
"""
|
215
|
-
|
216
|
-
|
245
|
+
# Supported linkage threshold metrics
|
246
|
+
supported_linkage_thresholds = {"silhouette", "calinski_harabasz", "davies_bouldin"}
|
247
|
+
|
248
|
+
# If linkage_threshold is a string:
|
249
|
+
if isinstance(linkage_threshold, str):
|
250
|
+
if linkage_threshold in supported_linkage_thresholds:
|
251
|
+
opt_metric = linkage_threshold
|
252
|
+
else:
|
253
|
+
try:
|
254
|
+
threshold_float = float(linkage_threshold)
|
255
|
+
except (TypeError, ValueError):
|
256
|
+
raise LinkageThresholdError(
|
257
|
+
f"linkage_threshold must be one of {', '.join(supported_linkage_thresholds)} or a float value."
|
258
|
+
)
|
259
|
+
return linkage_method, linkage_metric, threshold_float
|
260
|
+
else:
|
261
|
+
# If not a string, try to convert it to float.
|
262
|
+
try:
|
263
|
+
threshold_float = float(linkage_threshold)
|
264
|
+
except (TypeError, ValueError):
|
265
|
+
raise LinkageThresholdError(
|
266
|
+
f"linkage_threshold must be one of {', '.join(supported_linkage_thresholds)} or a float value."
|
267
|
+
)
|
268
|
+
return linkage_method, linkage_metric, threshold_float
|
269
|
+
|
270
|
+
# Otherwise, perform optimization using the specified metric (opt_metric).
|
271
|
+
best_overall_method = None
|
272
|
+
best_overall_metric = None
|
273
|
+
best_overall_threshold = None
|
217
274
|
best_overall_score = -np.inf
|
218
|
-
best_overall_threshold = 1
|
219
275
|
|
220
|
-
|
221
|
-
|
222
|
-
|
276
|
+
# Use the provided lists if "auto" is specified.
|
277
|
+
methods = GROUP_LINKAGE_METHODS if linkage_method == "auto" else [linkage_method]
|
278
|
+
metrics = GROUP_DISTANCE_METRICS if linkage_metric == "auto" else [linkage_metric]
|
279
|
+
total_combinations = len(methods) * len(metrics)
|
223
280
|
|
224
|
-
# Evaluating optimal linkage method and metric
|
225
281
|
for method, metric in tqdm(
|
226
|
-
product(
|
282
|
+
product(methods, metrics),
|
227
283
|
desc="Evaluating optimal linkage method and metric",
|
228
284
|
total=total_combinations,
|
229
285
|
bar_format="{l_bar}{bar}| {n_fmt}/{total_fmt} [{elapsed}<{remaining}]",
|
230
286
|
):
|
231
|
-
# Some linkage methods and metrics may not work with certain data
|
232
287
|
with suppress(ValueError):
|
233
288
|
Z = linkage(m, method=method, metric=metric)
|
234
|
-
threshold, score =
|
289
|
+
threshold, score = _find_optimal_linkage_threshold(
|
290
|
+
Z, m, metric, linkage_criterion, opt_metric=opt_metric
|
291
|
+
)
|
235
292
|
if score > best_overall_score:
|
236
293
|
best_overall_score = score
|
237
294
|
best_overall_threshold = threshold
|
238
295
|
best_overall_method = method
|
239
296
|
best_overall_metric = metric
|
240
297
|
|
298
|
+
if best_overall_method is None or best_overall_metric is None or best_overall_threshold is None:
|
299
|
+
raise ValueError("Optimization failed to determine an optimal threshold.")
|
241
300
|
return best_overall_method, best_overall_metric, best_overall_threshold
|
242
301
|
|
243
302
|
|
244
|
-
def
|
303
|
+
def _find_optimal_linkage_threshold(
|
245
304
|
Z: np.ndarray,
|
246
305
|
m: np.ndarray,
|
247
306
|
linkage_metric: str,
|
248
307
|
linkage_criterion: str,
|
249
|
-
|
250
|
-
upper_bound: float = 1.0,
|
251
|
-
resolution: float = 0.001,
|
308
|
+
opt_metric: str = "silhouette",
|
252
309
|
) -> Tuple[float, float]:
|
253
|
-
"""Find the
|
310
|
+
"""Find the optimal linkage threshold coefficient for hierarchical clustering. The function optimizes
|
311
|
+
the threshold value using the specified metric (opt_metric).
|
254
312
|
|
255
313
|
Args:
|
256
|
-
Z (np.ndarray): Linkage matrix.
|
257
|
-
m (np.ndarray): Data matrix.
|
258
|
-
linkage_metric (str):
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
314
|
+
Z (np.ndarray): Linkage matrix generated by a hierarchical clustering algorithm.
|
315
|
+
m (np.ndarray): Data matrix used for clustering.
|
316
|
+
linkage_metric (str): Metric used to calculate distances between data points
|
317
|
+
(e.g., "euclidean" or "cosine").
|
318
|
+
linkage_criterion (str): Criterion to pass to `fcluster`, typically "distance".
|
319
|
+
opt_metric (str, optional): Metric to optimize clustering quality. Options are:
|
320
|
+
"silhouette", "calinski_harabasz", or "davies_bouldin". Defaults to "silhouette".
|
263
321
|
|
264
322
|
Returns:
|
265
323
|
Tuple[float, float]:
|
266
|
-
-
|
267
|
-
-
|
268
|
-
|
269
|
-
|
270
|
-
best_threshold = None
|
324
|
+
- best_threshold (float): The optimal linkage threshold coefficient.
|
325
|
+
- best_metric_value (float): The value of the clustering quality metric achieved
|
326
|
+
at the optimal threshold (higher for "silhouette" and "calinski_harabasz",
|
327
|
+
lower for "davies_bouldin").
|
271
328
|
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
score_upper = -np.inf
|
287
|
-
|
288
|
-
# Determine initial bounds for binary search
|
289
|
-
if score_lower > score_upper:
|
290
|
-
best_score = score_lower
|
291
|
-
best_threshold = lower_bound
|
292
|
-
upper_bound = (lower_bound + upper_bound) / 2
|
293
|
-
else:
|
294
|
-
best_score = score_upper
|
295
|
-
best_threshold = upper_bound
|
296
|
-
lower_bound = (lower_bound + upper_bound) / 2
|
297
|
-
|
298
|
-
# Binary search loop
|
299
|
-
while upper_bound - lower_bound > resolution:
|
300
|
-
mid_threshold = (upper_bound + lower_bound) / 2
|
301
|
-
max_d_mid = np.max(Z[:, 2]) * mid_threshold
|
302
|
-
clusters_mid = fcluster(Z, max_d_mid, criterion=linkage_criterion)
|
329
|
+
Raises:
|
330
|
+
ValueError: If the `opt_metric` argument is not one of the supported metrics.
|
331
|
+
"""
|
332
|
+
# Get the maximum distance in the linkage matrix
|
333
|
+
max_d = np.max(Z[:, 2])
|
334
|
+
resolution = 1e-6
|
335
|
+
|
336
|
+
def compute_objective(coefficient: float) -> float:
|
337
|
+
"""Compute the objective function for optimization."""
|
338
|
+
threshold_val = coefficient * max_d
|
339
|
+
clusters = fcluster(Z, threshold_val, criterion=linkage_criterion)
|
340
|
+
unique_clusters = np.unique(clusters)
|
341
|
+
if len(unique_clusters) <= 1 or len(unique_clusters) == m.shape[0]:
|
342
|
+
return 1e6
|
303
343
|
try:
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
344
|
+
if opt_metric == "silhouette":
|
345
|
+
score = silhouette_score(m, clusters, metric=linkage_metric)
|
346
|
+
return -score # We want to maximize the score.
|
347
|
+
elif opt_metric == "calinski_harabasz":
|
348
|
+
score = calinski_harabasz_score(m, clusters)
|
349
|
+
return -score
|
350
|
+
elif opt_metric == "davies_bouldin":
|
351
|
+
score = davies_bouldin_score(m, clusters)
|
352
|
+
return score
|
353
|
+
else:
|
354
|
+
raise ValueError(f"Unknown optimization metric: {opt_metric}.")
|
355
|
+
except Exception:
|
356
|
+
return 1e6
|
357
|
+
|
358
|
+
# Optimize the threshold using the specified metric
|
359
|
+
res = minimize_scalar(
|
360
|
+
compute_objective, bounds=(0.0, 1.0), method="bounded", options={"xatol": resolution}
|
361
|
+
)
|
362
|
+
|
363
|
+
best_threshold = res.x
|
364
|
+
best_obj = res.fun
|
365
|
+
# For silhouette and calinski_harabasz, the objective was negative.
|
366
|
+
best_metric_value = -best_obj if opt_metric in ["silhouette", "calinski_harabasz"] else best_obj
|
318
367
|
|
319
|
-
return best_threshold, float(
|
368
|
+
return best_threshold, float(best_metric_value)
|
risk/network/graph/api.py
CHANGED
@@ -42,6 +42,7 @@ class GraphAPI:
|
|
42
42
|
linkage_criterion: str = "distance",
|
43
43
|
linkage_method: str = "average",
|
44
44
|
linkage_metric: str = "yule",
|
45
|
+
linkage_threshold: float = 0.2,
|
45
46
|
min_cluster_size: int = 5,
|
46
47
|
max_cluster_size: int = 1000,
|
47
48
|
) -> Graph:
|
@@ -57,8 +58,12 @@ class GraphAPI:
|
|
57
58
|
impute_depth (int, optional): Depth for imputing neighbors. Defaults to 0.
|
58
59
|
prune_threshold (float, optional): Distance threshold for pruning neighbors. Defaults to 0.0.
|
59
60
|
linkage_criterion (str, optional): Clustering criterion for defining domains. Defaults to "distance".
|
60
|
-
linkage_method (str, optional): Clustering method to use. Defaults to "average".
|
61
|
-
|
61
|
+
linkage_method (str, optional): Clustering method to use. Defaults to "average". Choose "auto"
|
62
|
+
to automatically select the best linkage method.
|
63
|
+
linkage_metric (str, optional): Metric to use for calculating distances. Defaults to "yule". Choose "auto"
|
64
|
+
to automatically select the best linkage metric.
|
65
|
+
linkage_threshold (str, float, optional): Threshold for clustering. Choose "silhouette", "calinski_harabasz",
|
66
|
+
or "davies_bouldin" to automatically select the best threshold. Defaults to 0.2.
|
62
67
|
min_cluster_size (int, optional): Minimum size for clusters. Defaults to 5.
|
63
68
|
max_cluster_size (int, optional): Maximum size for clusters. Defaults to 1000.
|
64
69
|
|
@@ -76,6 +81,7 @@ class GraphAPI:
|
|
76
81
|
linkage_criterion=linkage_criterion,
|
77
82
|
linkage_method=linkage_method,
|
78
83
|
linkage_metric=linkage_metric,
|
84
|
+
linkage_threshold=linkage_threshold,
|
79
85
|
min_cluster_size=min_cluster_size,
|
80
86
|
max_cluster_size=max_cluster_size,
|
81
87
|
)
|
@@ -130,6 +136,7 @@ class GraphAPI:
|
|
130
136
|
linkage_criterion=linkage_criterion,
|
131
137
|
linkage_method=linkage_method,
|
132
138
|
linkage_metric=linkage_metric,
|
139
|
+
linkage_threshold=linkage_threshold,
|
133
140
|
)
|
134
141
|
# Trim domains and top annotations based on cluster size constraints
|
135
142
|
domains, trimmed_domains = trim_domains(
|
@@ -1,4 +1,4 @@
|
|
1
|
-
risk/__init__.py,sha256=
|
1
|
+
risk/__init__.py,sha256=S22dtKjPn7IvWeLakeN4IajRLsqHuSbBhjvf6z5kHoo,127
|
2
2
|
risk/constants.py,sha256=XInRaH78Slnw_sWgAsBFbUHkyA0h0jL0DKGuQNbOvjM,550
|
3
3
|
risk/risk.py,sha256=s827_lRknFseOP9O4zW8sP-IcCd2EzrpV_tnVY_tz5s,1104
|
4
4
|
risk/annotations/__init__.py,sha256=parsbcux1U4urpUqh9AdzbDWuLj9HlMidycMPkpSQFo,179
|
@@ -10,13 +10,13 @@ risk/log/parameters.py,sha256=VtwfMzLU1xI4yji3-Ch5vHjH-KdwTfwaEMmi7hFQTs0,5716
|
|
10
10
|
risk/neighborhoods/__init__.py,sha256=Q74HwTH7okI-vaskJPy2bYwb5sNjGASTzJ6m8V8arCU,234
|
11
11
|
risk/neighborhoods/api.py,sha256=ywngw2TQVV27gYlWDXcs8-qnmeepnvb-W9ov6J6VEPM,23341
|
12
12
|
risk/neighborhoods/community.py,sha256=5Q_-VAJC-5SY5EUsB8gIlemeDoAL85uLjyl16pItHiQ,16699
|
13
|
-
risk/neighborhoods/domains.py,sha256=
|
13
|
+
risk/neighborhoods/domains.py,sha256=yaSHymGfRJVuXHIa7BwoKzvIRSg5oLhNoOMg0tsVqV8,15961
|
14
14
|
risk/neighborhoods/neighborhoods.py,sha256=l9FhADB1C-OxM8E9QXOcA4osUDgA1vs4ud-OCGKKybc,21457
|
15
15
|
risk/network/__init__.py,sha256=oVi3FA1XXKD84014Cykq-9bpX4_s0F3aAUfNOU-07Qw,73
|
16
16
|
risk/network/geometry.py,sha256=eVtGHMgBf9fEqQZUFdHWjw-zFYYpfUONoHFSAxoRkug,6219
|
17
17
|
risk/network/io.py,sha256=RCH4nQdgYDXcNwMfpSz7qEmPO0pJ1p9fL0rNQptsQrc,21673
|
18
18
|
risk/network/graph/__init__.py,sha256=ziGJew3yhtqvrb9LUuneDu_LwW2Wa9vd4UuhoL5l1CA,91
|
19
|
-
risk/network/graph/api.py,sha256=
|
19
|
+
risk/network/graph/api.py,sha256=fOyd-5rRnqmtquproP90scehewd0UtOVZS65hCuwasI,8684
|
20
20
|
risk/network/graph/graph.py,sha256=qEWyZvuaGT_vvjhreBdmRPX3gst2wQFaXhFAvikPSqw,12158
|
21
21
|
risk/network/graph/summary.py,sha256=Y_0rL2C1UoQeZQIPVe5LbaCO356Mcc8HisnrXwQsRm8,10289
|
22
22
|
risk/network/plotter/__init__.py,sha256=4gWtQHGzQVNHmEBXi31Zf0tX0y2sTcE66J_yGnn7268,99
|
@@ -34,8 +34,8 @@ risk/stats/stat_tests.py,sha256=tj0ri9w89_1fsjGLuafTWpfBEwZXpSLn7Ej2aAQ5lxk,1177
|
|
34
34
|
risk/stats/permutation/__init__.py,sha256=OLmYLm2uj96hPsSaUs0vUqFYw6Thwch_aHtpL7L0ZFw,127
|
35
35
|
risk/stats/permutation/permutation.py,sha256=BWjgdBpLVcHvmwHy0bmD4aJFccxifNBSrrCBPppyKf4,10569
|
36
36
|
risk/stats/permutation/test_functions.py,sha256=KlECWTz1EZ6EPF_OAgHb0uznaIhopiVYb_AKUKuC4no,3120
|
37
|
-
risk_network-0.0.
|
38
|
-
risk_network-0.0.
|
39
|
-
risk_network-0.0.
|
40
|
-
risk_network-0.0.
|
41
|
-
risk_network-0.0.
|
37
|
+
risk_network-0.0.9b38.dist-info/LICENSE,sha256=jOtLnuWt7d5Hsx6XXB2QxzrSe2sWWh3NgMfFRetluQM,35147
|
38
|
+
risk_network-0.0.9b38.dist-info/METADATA,sha256=mAB2KQoRWeOH13radrcMeW5dalpkPkl4YtjfUpQhJXI,47627
|
39
|
+
risk_network-0.0.9b38.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
40
|
+
risk_network-0.0.9b38.dist-info/top_level.txt,sha256=NX7C2PFKTvC1JhVKv14DFlFAIFnKc6Lpsu1ZfxvQwVw,5
|
41
|
+
risk_network-0.0.9b38.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|