pythonflex 0.1.2__py3-none-any.whl → 0.1.3__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.
@@ -0,0 +1,108 @@
1
+ """
2
+ Basic usage example of the pythonFLEX package.
3
+ Demonstrates initialization, data loading, analysis, and plotting.
4
+ """
5
+ #%%
6
+ import pythonflex as flex
7
+
8
+ inputs = {
9
+ "SNF": {
10
+ "path": "C:/Users/yd/Desktop/projects/datasets/fused_similarity_network.csv",
11
+ "sort": "high"
12
+ },
13
+ "miss_SNF": {
14
+ "path": "C:/Users/yd/Desktop/projects/datasets/miss_snf_fused_similarity_network.csv",
15
+ "sort": "high"
16
+ }
17
+ }
18
+
19
+ #%%
20
+
21
+ default_config = {
22
+ "min_genes_in_complex": 0,
23
+ "min_genes_per_complex_analysis": 3,
24
+ "output_folder": "output",
25
+ "gold_standard": "CORUM",
26
+ "color_map": "RdYlBu",
27
+ "jaccard": True,
28
+ "plotting": {
29
+ "save_plot": True,
30
+ "output_type": "PNG",
31
+ },
32
+ "preprocessing": {
33
+ "fill_na": True,
34
+ "normalize": False,
35
+ },
36
+ "corr_function": "numpy",
37
+ "logging": {
38
+ "visible_levels": ["DONE","STARTED"] # "PROGRESS", "STARTED", ,"INFO","WARNING"
39
+ }
40
+ }
41
+
42
+ # Initialize logger, config, and output folder
43
+ flex.initialize(default_config)
44
+
45
+ # Load datasets and gold standard terms
46
+
47
+ data, _ = flex.load_datasets(inputs)
48
+ terms, genes_in_terms = flex.load_gold_standard()
49
+
50
+
51
+ #%%
52
+ # Run analysis
53
+ for name, dataset in data.items():
54
+ df, pr_auc = flex.pra(name, dataset, is_corr=True)
55
+ fpc = flex.pra_percomplex(name, dataset, is_corr=True)
56
+ cc = flex.complex_contributions(name)
57
+
58
+
59
+ #%%
60
+ # Generate plots
61
+ flex.plot_auc_scores()
62
+ flex.plot_precision_recall_curve()
63
+ flex.plot_percomplex_scatter()
64
+ flex.plot_percomplex_scatter_bysize()
65
+ flex.plot_significant_complexes()
66
+ flex.plot_complex_contributions()
67
+
68
+
69
+ #%%
70
+ # Save results to CSV
71
+ flex.save_results_to_csv()
72
+
73
+
74
+
75
+
76
+
77
+
78
+
79
+
80
+
81
+
82
+
83
+
84
+
85
+ # %%
86
+ import os
87
+ import glob
88
+
89
+ inputs = {
90
+ "depmap all": {
91
+ "path": "../../../../datasets/depmap/24Q4/depmap_geneeffect_all_cellines.csv",
92
+ "sort": "high"
93
+ }
94
+ }
95
+
96
+ # Now auto-discover the rest of the CSVs in the folder
97
+ DATA_DIR = "../../../../datasets/depmap/24Q4/subset/"
98
+ for path in glob.glob(os.path.join(DATA_DIR, "*.csv")):
99
+
100
+ # Derive the key name from filename (without extension)
101
+ key = os.path.splitext(os.path.basename(path))[0]
102
+ inputs[key] = {
103
+ "path": path,
104
+ "sort": "high"
105
+ }
106
+
107
+ # inputs now has "depmap all" first, then one entry per CSV in DATA_DIR
108
+ print(inputs)
@@ -0,0 +1,29 @@
1
+
2
+ # %%
3
+ import pandas as pd
4
+
5
+ df = pd.read_csv("../../../../datasets/depmap/24Q4/CRISPRGeneEffect.csv",index_col=0)
6
+ model = pd.read_csv("../../../../datasets/depmap/24Q4/Model.csv",index_col=0)
7
+
8
+ df.columns = df.columns.str.split(" \\(").str[0]
9
+ df = df.T
10
+
11
+ # %%
12
+ # get ModelID of selected disease for example OncotreePrimaryDisease==Melanoma
13
+ melanoma = model[model.OncotreePrimaryDisease=="Melanoma"].index.unique().values
14
+ liver = model[model.OncotreeLineage=="Liver"].index.unique().values
15
+ neuroblastoma = model[model.OncotreePrimaryDisease=="Neuroblastoma"].index.unique().values
16
+
17
+ # %%
18
+ # mel.index is model ids, filter that ids in the columns of df
19
+ mel_df = df.loc[:,df.columns.isin(melanoma)]
20
+ liver_df = df.loc[:,df.columns.isin(liver)]
21
+ neuro_df = df.loc[:,df.columns.isin(neuroblastoma)]
22
+
23
+
24
+ # %%
25
+ mel_df.to_csv("melanoma.csv")
26
+ liver_df.to_csv("liver.csv")
27
+ neuro_df.to_csv("neuroblastoma.csv")
28
+ df.to_csv("depmap_geneeffect_all_cellines.csv")
29
+ # %%
@@ -0,0 +1,56 @@
1
+ import sys
2
+ import emoji
3
+ from loguru import logger as global_logger
4
+ from typing import TYPE_CHECKING
5
+
6
+ pkg_logger = global_logger.bind(name="pyflex")
7
+
8
+
9
+ class CustomLogger:
10
+ def __init__(self, logger):
11
+ self._log = logger
12
+ self.visible_levels = {"DONE", "PROGRESS", "STARTED", "INFO"} # Default: Show all custom levels
13
+ self._define_custom_levels()
14
+ self._configure_output()
15
+
16
+ def _safe_set_level(self, name: str, **kwargs):
17
+ try:
18
+ self._log.level(name, **kwargs)
19
+ except ValueError:
20
+ pass # Level already exists, ignore
21
+
22
+ def _define_custom_levels(self):
23
+ self._safe_set_level("DONE", no=25, color="<green>", icon=emoji.emojize(":flexed_biceps:"))
24
+ self._safe_set_level("PROGRESS", no=22, color="<red>", icon=emoji.emojize(":horse:"))
25
+ self._safe_set_level("STARTED", no=21, color="<blue>", icon=emoji.emojize(":rocket:"))
26
+ self._safe_set_level("INFO", color="<blue>", icon=emoji.emojize(":placard:"))
27
+
28
+ def _configure_output(self):
29
+ self._log.remove()
30
+
31
+ def level_filter(record):
32
+ return record["level"].name in self.visible_levels
33
+
34
+ self._log.add(sys.stderr, format="{time:HH:mm:ss} | {level.icon} {level.name:<8} | {message}", filter=level_filter)
35
+
36
+ def set_visible_levels(self, levels):
37
+ self.visible_levels = set(levels)
38
+ self._configure_output() # Reconfigure the sink with the new filter
39
+
40
+ def started(self, message, *args, **kwargs):
41
+ self._log.log("STARTED", message, *args, **kwargs)
42
+
43
+ def done(self, message, *args, **kwargs):
44
+ self._log.log("DONE", message, *args, **kwargs)
45
+
46
+ def progress(self, message, *args, **kwargs):
47
+ self._log.log("PROGRESS", message, *args, **kwargs)
48
+
49
+ def __getattr__(self, name):
50
+ return getattr(self._log, name)
51
+
52
+ # Type checker compatibility
53
+ if TYPE_CHECKING:
54
+ log: CustomLogger
55
+ else:
56
+ log = CustomLogger(pkg_logger)
pythonflex/plotting.py ADDED
@@ -0,0 +1,510 @@
1
+ # Standard library
2
+ from itertools import combinations
3
+ from pathlib import Path
4
+
5
+ # Third-party libraries
6
+ import numpy as np
7
+ import pandas as pd
8
+ import matplotlib.pyplot as plt
9
+ from matplotlib import patches
10
+ from matplotlib.cm import get_cmap
11
+ from IPython.display import set_matplotlib_formats
12
+
13
+ # Local modules
14
+ from .utils import dload
15
+ from .logging_config import log
16
+
17
+ # Configuration
18
+ set_matplotlib_formats('svg', 'pdf')
19
+
20
+
21
+
22
+
23
+ def plot_precision_recall_curve():
24
+ pra = dload("pra")
25
+ config = dload("config")
26
+ plot_config = config["plotting"]
27
+
28
+ # Create figure using rcParams defaults (figsize and dpi are already set)
29
+ fig, ax = plt.subplots()
30
+ ax.set_xscale("log")
31
+
32
+ # Color map from rcParams (no need to get from config again)
33
+ cmap = get_cmap() # Uses rcParams['image.cmap'] by default
34
+ num_colors = len(pra) if isinstance(pra, dict) else 1
35
+ colors = [cmap(float(i) / max(num_colors - 1, 1)) for i in range(num_colors)]
36
+
37
+ if isinstance(pra, dict):
38
+ for (key, val), color in zip(pra.items(), colors):
39
+ val = val[val.tp > 10]
40
+ ax.plot(val.tp, val.precision, c=color, label=key, linewidth=2, alpha=0.8)
41
+ else:
42
+ pra = pra[pra.tp > 10]
43
+ ax.plot(pra.tp, pra.precision, c="black", label="Precision Recall Curve", linewidth=2, alpha=0.8)
44
+
45
+ # Labels and title (sizes handled by rcParams)
46
+ ax.set(title="Precision-Recall Performance of Datasets",
47
+ xlabel="Number of True Positives (TP)",
48
+ ylabel="Precision")
49
+ ax.legend(loc="upper right", frameon=True)
50
+
51
+ # Fix Y-axis to always go from 0 to 1
52
+ ax.set_ylim(0, 1)
53
+
54
+ # Grid and spines (styles handled by rcParams)
55
+ ax.grid(True) # Style comes from rcParams
56
+ # Spines visibility handled by rcParams ('axes.spines.right' and 'axes.spines.top')
57
+
58
+ # Save handling (output config still needed)
59
+ if plot_config["save_plot"]:
60
+ output_type = plot_config["output_type"]
61
+ output_path = Path(config["output_folder"]) / f"precision_recall_curve.{output_type}"
62
+ fig.savefig(output_path, bbox_inches="tight", format=output_type) # dpi comes from rcParams
63
+
64
+ if plot_config.get("show_plot", True):
65
+ plt.show()
66
+
67
+ plt.close(fig)
68
+
69
+
70
+
71
+ def plot_percomplex_scatter(n_top=10):
72
+ config = dload("config")
73
+ plot_config = config["plotting"]
74
+ rdict = dload("pra_percomplex")
75
+
76
+ # Ensure there are at least two datasets to compare
77
+ if len(rdict) < 2:
78
+ print("Skipping plot: At least two datasets are required for per-complex scatter plot.")
79
+ return
80
+
81
+ column_pairs = list(combinations(rdict.keys(), 2))
82
+ df = pd.DataFrame()
83
+
84
+ # Data loading
85
+ for i, (key, val) in enumerate(rdict.items()):
86
+ val = val.rename(columns={"auc_score": key})
87
+ if i == 0:
88
+ df = val.copy().drop(columns=["Genes", "Length", "used_genes"])
89
+ else:
90
+ df = pd.concat([df, val[key]], axis=1)
91
+
92
+ # Plotting
93
+ for pair in column_pairs:
94
+ extreme_indices_0 = df[pair[0]].sort_values(ascending=False).head(n_top).index
95
+ extreme_indices_1 = df[pair[1]].sort_values(ascending=False).head(n_top).index
96
+
97
+ # Figure created with rcParams defaults
98
+ fig, ax = plt.subplots()
99
+
100
+ # Base scatter plot (keep color overrides)
101
+ sizes = df['n_used_genes'] * 8
102
+ ax.scatter(df[pair[0]], df[pair[1]],
103
+ edgecolors="black",
104
+ marker='o',
105
+ s=sizes,
106
+ linewidth=0.7,
107
+ zorder=1)
108
+
109
+ # Highlight significant points
110
+ significant_indices = extreme_indices_0.union(extreme_indices_1)
111
+ sig_sizes = df.loc[significant_indices, 'n_used_genes'] * 8
112
+ ax.scatter(df.loc[significant_indices, pair[0]],
113
+ df.loc[significant_indices, pair[1]],
114
+ facecolors='black',
115
+ edgecolors='black',
116
+ s=sig_sizes,
117
+ linewidth=0.1,
118
+ zorder=2)
119
+
120
+ all_points = list(zip(df[pair[0]], df[pair[1]]))
121
+ coords = sorted([(df.loc[idx, pair[0]], df.loc[idx, pair[1]], idx)
122
+ for idx in significant_indices], key=lambda c: (-c[1], -c[0]))
123
+
124
+ adjusted_coords = adjust_text_positions(coords, sig_sizes)
125
+
126
+ # Draw vertical lines and right-aligned text
127
+ for x, adj_y, idx in adjusted_coords:
128
+ y = df.loc[idx, pair[1]]
129
+ ax.plot([x, x], [y, adj_y],
130
+ color='black',
131
+ linewidth=0.7,
132
+ alpha=0.3,
133
+ zorder=3)
134
+
135
+ ax.text(x, adj_y + 0.005,
136
+ df.loc[idx, 'Name'][:20] + '.',
137
+ fontsize=6,
138
+ ha='left',
139
+ va='bottom',
140
+ linespacing=1.5,
141
+ zorder=4,
142
+ bbox=dict(facecolor="white", alpha=0.8, edgecolor="none", pad=1.5)
143
+ )
144
+
145
+ # Reference line and labels
146
+ ax.plot([0, 1], [0, 1],
147
+ linestyle='-',
148
+ color='lightgray',
149
+ alpha=0.4,
150
+ zorder=0)
151
+
152
+ # Add padding to axes for better visibility of points near edges
153
+ padding = 0.02 # Small offset (adjust as needed, e.g., 0.05 for more space)
154
+ ax.set_xlim(-padding, 1 + padding)
155
+ ax.set_ylim(-padding, 1 + padding)
156
+
157
+ # Labels use rcParams sizes automatically
158
+ ax.set_xlabel(f"{pair[0]} PR-AUC score")
159
+ ax.set_ylabel(f"{pair[1]} PR-AUC score")
160
+ ax.set_title(f"{pair[0]} vs {pair[1]} - Comparison of complex performance")
161
+
162
+ plt.tight_layout()
163
+
164
+ # Save handling
165
+ if plot_config["save_plot"]:
166
+ output_type = plot_config["output_type"]
167
+ output_path = Path(config["output_folder"]) / f"percomplex_scatter_{pair[0]}_vs_{pair[1]}.{output_type}"
168
+ fig.savefig(output_path, bbox_inches="tight", format=output_type)
169
+
170
+ if plot_config.get("show_plot", True):
171
+ plt.show()
172
+
173
+ plt.close(fig)
174
+
175
+
176
+
177
+
178
+
179
+
180
+
181
+ def adjust_text_positions(coords, sizes, min_distance=0.08, max_y=1.0, scale_factor=1.0):
182
+ adjusted = []
183
+ text_height = 0.04 * scale_factor # Scaled text height
184
+
185
+ for (x, y, idx) in coords:
186
+ base_offset = np.sqrt(sizes.loc[idx]) * 0.04 * scale_factor if idx in sizes else 0.04 * scale_factor
187
+ adj_y = y + base_offset # Move text upwards (scaled)
188
+ safety = 0
189
+
190
+ # Ensure text stays within plot area
191
+ max_possible_y = max_y - text_height
192
+
193
+ while safety < 20:
194
+ conflict = False
195
+ # Check against existing labels
196
+ for (ax, aay, _) in adjusted:
197
+ if abs(x - ax) < 0.01 and abs(adj_y - aay) < min_distance:
198
+ conflict = True
199
+ break
200
+
201
+ if conflict:
202
+ adj_y += (0.03 + (base_offset * 0.1)) # Move further upwards (base_offset already scaled)
203
+ safety += 1
204
+
205
+ # If we're going beyond plot area, stop
206
+ if adj_y > max_possible_y:
207
+ adj_y = max_possible_y
208
+ break
209
+ else:
210
+ # Clamp final position
211
+ adj_y = min(adj_y, max_possible_y)
212
+ adjusted.append((x, adj_y, idx))
213
+ break
214
+
215
+ return adjusted
216
+
217
+
218
+
219
+ def plot_percomplex_scatter_bysize():
220
+ config = dload("config")
221
+ plot_config = config["plotting"]
222
+ rdict = dload("pra_percomplex")
223
+
224
+ for key, per_complex in rdict.items():
225
+ sorted_pc = per_complex.sort_values(by="auc_score", ascending=False, na_position="last")
226
+ top_10, rest = sorted_pc.head(10), sorted_pc.iloc[10:]
227
+
228
+ # Create figure using rcParams defaults
229
+ fig, ax = plt.subplots()
230
+
231
+ # Base scatter plot (simple swap of X/Y data)
232
+ ax.scatter(
233
+ rest.auc_score, rest.n_used_genes, # auc_score on X, n_used_genes on Y
234
+ edgecolors="black",
235
+ linewidth=0.5,
236
+ s=rest.n_used_genes * 10,
237
+ label="Other Complexes"
238
+ )
239
+
240
+ # Top 10 scatter plot (simple swap of X/Y data)
241
+ ax.scatter(
242
+ top_10.auc_score, top_10.n_used_genes, # auc_score on X, n_used_genes on Y
243
+ facecolors='black',
244
+ edgecolors='black',
245
+ linewidth=0.5,
246
+ s=top_10.n_used_genes * 10,
247
+ label="Top 10 AUC Scores"
248
+ )
249
+
250
+
251
+ # Text annotation handling (swapped coords: auc on X, size on Y)
252
+ coords = [(row.auc_score, row.n_used_genes, idx) for idx, row in top_10.iterrows()]
253
+ sizes = top_10.n_used_genes * 10
254
+
255
+ # Dynamically set max_y and scale_factor to make lines visible/long like original
256
+ max_y = sorted_pc.n_used_genes.max() + 50 # Larger buffer (increase to +100 if lines still short)
257
+ scale_factor = max_y / 1.0 # Scale offsets for visibility on new range
258
+ adjusted_coords = adjust_text_positions(
259
+ coords, sizes,
260
+ min_distance=0.08 * scale_factor,
261
+ max_y=max_y,
262
+ scale_factor=scale_factor # New param to make lengths visible
263
+ )
264
+
265
+ for x, adj_y, idx in adjusted_coords:
266
+ y = top_10.loc[idx, "n_used_genes"] # Pull from n_used_genes (now Y)
267
+ ax.plot([x, x], [y, adj_y],
268
+ color='black',
269
+ linewidth=0.7,
270
+ alpha=0.3,
271
+ zorder=3)
272
+
273
+ # Dynamic alignment: left if x < 0.5 (extends right), right if x >= 0.5 (extends left)
274
+ if x < 0.5:
275
+ ha = 'left'
276
+ text_x = x + 0.01 # Small rightward offset to avoid line overlap
277
+ else:
278
+ ha = 'right'
279
+ text_x = x - 0.01 # Small leftward offset to avoid line overlap
280
+
281
+ ax.text(text_x, adj_y + (0.005 * scale_factor),
282
+ top_10.loc[idx, 'Name'][:20] + '.',
283
+ fontsize=6,
284
+ ha=ha,
285
+ va='bottom',
286
+ linespacing=1.5,
287
+ zorder=4,
288
+ bbox=dict(facecolor="white", alpha=0.8, edgecolor="none", pad=1.5))
289
+
290
+ # Axis configuration (integer ticks now on Y)
291
+ ax.yaxis.get_major_locator().set_params(integer=True)
292
+ ax.set_xlabel("PR-AUC score") # Swapped label
293
+ ax.set_ylabel("Number of genes in the complex") # Swapped label
294
+ ax.set_title(f"{key} - Complex performance: PR-AUC score vs complex size")
295
+ ax.grid(False)
296
+
297
+ # Fixed limits (X fixed to 0-1 for AUC, Y with buffer for lines/labels)
298
+ ax.set_xlim(0, 1.0)
299
+ ax.set_ylim(0, max_y)
300
+
301
+ # Adjust subplot margins to give extra space on right for labels (without extending axis; optional)
302
+ plt.subplots_adjust(right=0.8) # Adjust to 0.7 or remove if not needed
303
+
304
+ plt.tight_layout()
305
+
306
+ # Save handling (dpi comes from rcParams)
307
+ if plot_config["save_plot"]:
308
+ output_type = plot_config["output_type"]
309
+ output_path = Path(config["output_folder"]) / f"percomplex_scatter_by_complexsize_{key}.{output_type}"
310
+ fig.savefig(output_path, bbox_inches="tight", format=output_type)
311
+
312
+ if plot_config.get("show_plot", True):
313
+ plt.show()
314
+
315
+ plt.close(fig)
316
+
317
+
318
+
319
+ def plot_complex_contributions(min_pairs=10, min_precision_cutoff=0.5, num_complex_to_show=10, y_lim=None, fig_title=None, fig_labs=['Fraction of TP', 'Precision']):
320
+ config = dload("config")
321
+ plot_config = config["plotting"]
322
+ plot_data_dict = dload("complex_contributions")
323
+ for key, plot_data in plot_data_dict.items():
324
+ s = plot_data.set_index('Name').sum()
325
+ find_last_precision = s[s > min_pairs].index[-1]
326
+ last_prec_value = float(find_last_precision.split('_')[1]) # Parse the float value
327
+
328
+ plot_data = plot_data.drop_duplicates(subset='Name')
329
+ cont_stepwise_anno = plot_data['Name']
330
+ cont_stepwise_mat = plot_data.drop(columns=['Name'])
331
+ tmp_TP = cont_stepwise_mat.sum(axis=0)
332
+ Precision_ind = (tmp_TP >= min_pairs)
333
+ cont_stepwise_mat = cont_stepwise_mat.loc[:, Precision_ind]
334
+ tmp = cont_stepwise_mat.columns
335
+ y = np.array([float(col.split('_')[1]) if isinstance(col, str) and '_' in col else col for col in tmp])
336
+ x = cont_stepwise_mat.sum(axis=0)
337
+ mx, nx = cont_stepwise_mat.shape[0], cont_stepwise_mat.shape[1]
338
+ tmp = np.tile(x, (mx, 1))
339
+ x = cont_stepwise_mat.values / tmp
340
+ x_df = pd.DataFrame(x, index=cont_stepwise_anno, columns=cont_stepwise_mat.columns)
341
+ ind_for_mean = y >= (last_prec_value - min_precision_cutoff)
342
+ if sum(ind_for_mean) == 0:
343
+ log.info("No values above 'min.precision.cutoff'")
344
+ return False
345
+ if sum(ind_for_mean) == 1:
346
+ log.info("Only one value above 'min.precision.cutoff', unable to calculate meaningful contribution structure")
347
+ return False
348
+ # Select top complexes
349
+ a = x_df.loc[:, ind_for_mean].mean(axis=1).sort_values()[-num_complex_to_show:]
350
+ subset = x_df.loc[a.index, :]
351
+ # Use the RdYlBu colormap for the top 10 points
352
+ cmap = plt.get_cmap() # Get default from rcParams
353
+ colors = cmap(np.linspace(0, 1, num_complex_to_show))
354
+ colors = np.vstack(([0.5, 0.5, 0.5, 1.0], colors))
355
+ others = pd.DataFrame(1 - subset.sum(axis=0), columns=['others']).T
356
+ merged = pd.concat([others, subset], ignore_index=False)
357
+ x = merged.to_numpy()
358
+ x1 = np.zeros_like(x)
359
+ x2 = np.zeros_like(x)
360
+ for i in range(x.shape[0]):
361
+ if i == 0:
362
+ x2[i, :] = x[0, :]
363
+ elif i == 1:
364
+ x1[i, :] = x[0, :]
365
+ else:
366
+ x1[i, :] = x[:i, :].sum(axis=0)
367
+ if i > 0:
368
+ x2[i, :] = x[:i + 1, :].sum(axis=0)
369
+
370
+
371
+ # FORCE recalculation of y_lim - ignore any passed parameter
372
+ padding = 0.02 # Small padding to avoid clipping (adjust as needed, e.g., 0.05 for more space)
373
+ lower = max(0, min(y) - padding)
374
+ upper = last_prec_value + padding # Use the actual last precision value instead of 1.0
375
+ y_lim = (lower, upper)
376
+
377
+
378
+ fig, ax = plt.subplots(2, 1, gridspec_kw={'height_ratios': [5, 1]})
379
+ ax[0].set_xlim(0, 1)
380
+ ax[0].set_ylim(*y_lim) # Use dynamic ylim
381
+ ax[0].set_xlabel(fig_labs[0])
382
+ ax[0].set_ylabel(fig_labs[1])
383
+ ax[0].set_title(fig_title if fig_title else f"{key} - Contribution of complexes")
384
+ for i in range(x.shape[0]):
385
+ ax[0].fill_betweenx(y, x1[i, :], x2[i, :], color=colors[i], edgecolor='white')
386
+
387
+ # Legend handling (keep custom settings)
388
+ legend_labels = [f'{label[:20]}.' for label in merged.index]
389
+ patches_list = [patches.Patch(color=colors[i], label=legend_labels[i]) for i in range(len(legend_labels))]
390
+ ax[1].axis('off')
391
+ ax[1].legend(handles=patches_list, loc='center', ncol=3, frameon=False, title="Complexes")
392
+ plt.tight_layout()
393
+
394
+ # Save handling (remove explicit dpi)
395
+ if plot_config["save_plot"]:
396
+ output_type = plot_config["output_type"]
397
+ output_folder = Path(config["output_folder"])
398
+ output_path = output_folder / f"complex_contributions_{key}.{output_type}"
399
+ fig.savefig(output_path, bbox_inches="tight", format=output_type)
400
+
401
+ if plot_config.get("show_plot", True):
402
+ plt.show()
403
+
404
+ plt.close(fig)
405
+
406
+
407
+ def plot_significant_complexes():
408
+ config = dload("config")
409
+ plot_config = config["plotting"]
410
+ pra_percomplex = dload("pra_percomplex")
411
+
412
+ # Define thresholds and prepare data
413
+ thresholds = [0.1, 0.2, 0.3, 0.4, 0.5]
414
+ datasets = list(pra_percomplex.keys())
415
+ num_datasets = len(datasets)
416
+
417
+ # Create a DataFrame to store results
418
+ df = pd.DataFrame(index=thresholds)
419
+ for key, complex_data in pra_percomplex.items():
420
+ df[key] = [complex_data.query(f'auc_score >= {t}').shape[0] for t in thresholds]
421
+
422
+ # Create figure
423
+ fig, ax = plt.subplots()
424
+
425
+ # Use colormap from rcParams
426
+ cmap = plt.get_cmap()
427
+ colors = [cmap(i / (num_datasets + 1)) for i in range(1, num_datasets + 1)]
428
+
429
+ # Plot bars
430
+ bar_width = 0.8 / num_datasets # Dynamic width based on dataset count
431
+ for i, dataset in enumerate(datasets):
432
+ x = np.arange(len(thresholds)) + i * bar_width
433
+ ax.bar(x, df[dataset], width=bar_width, color=colors[i], edgecolor='black', label=dataset)
434
+
435
+ # Customize x-axis labels
436
+ ax.set_xticks(np.arange(len(thresholds)) + (num_datasets - 1) * bar_width / 2)
437
+ ax.set_xticklabels(thresholds, rotation=0, ha='center')
438
+
439
+ # Set title and axis labels (handled by rcParams)
440
+ ax.set_title("Number of significant complexes above PR-AUC thresholds")
441
+ ax.set_xlabel("PR-AUC score thresholds")
442
+ ax.set_ylabel("Number of complexes")
443
+
444
+ # Add grid (already handled by rcParams, but ensured)
445
+ ax.grid(axis='y')
446
+
447
+ # Add legend
448
+ ax.legend(loc='upper right')
449
+
450
+ # Adjust layout
451
+ plt.tight_layout()
452
+
453
+ # Save figure if required
454
+ if plot_config["save_plot"]:
455
+ output_type = plot_config["output_type"]
456
+ output_folder = Path(config["output_folder"])
457
+ output_path = output_folder / f"number_of_significant_complexes.{output_type}"
458
+ plt.savefig(output_path, bbox_inches='tight', format=output_type)
459
+
460
+ if plot_config.get("show_plot", True):
461
+ plt.show()
462
+
463
+ plt.close(fig)
464
+ return df
465
+
466
+
467
+
468
+ def plot_auc_scores():
469
+ config = dload("config")
470
+ plot_config = config["plotting"]
471
+ pra_dict = dload("pr_auc")
472
+
473
+ # Prepare data
474
+ datasets = list(pra_dict.keys())
475
+ auc_scores = list(pra_dict.values())
476
+
477
+ # Create figure and axis
478
+ fig, ax = plt.subplots()
479
+
480
+ # Use colormap from rcParams
481
+ cmap = plt.get_cmap()
482
+ num_datasets = len(datasets)
483
+ colors = [cmap(i / (num_datasets + 1)) for i in range(1, num_datasets + 1)]
484
+
485
+ # Plot bars
486
+ bars = ax.bar(datasets, auc_scores, color=colors, edgecolor="black")
487
+
488
+ # Set y-axis limits dynamically
489
+ ax.set_ylim(0, max(auc_scores) + 0.01)
490
+
491
+ # Set title and labels
492
+ ax.set_title("AUC scores for the datasets")
493
+ ax.set_ylabel("AUC score")
494
+
495
+ # Add grid (already handled by rcParams)
496
+ ax.grid(axis='y')
497
+
498
+ # Save the figure if required
499
+ if plot_config["save_plot"]:
500
+ output_type = plot_config["output_type"]
501
+ output_folder = Path(config["output_folder"])
502
+ output_folder.mkdir(parents=True, exist_ok=True)
503
+ output_path = output_folder / f"prauc_scores.{output_type}"
504
+ plt.savefig(output_path, bbox_inches='tight', format=output_type)
505
+
506
+ if plot_config.get("show_plot", True):
507
+ plt.show()
508
+
509
+ plt.close(fig)
510
+ return pra_dict