publiplots 0.1.0__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.
- publiplots/__init__.py +110 -0
- publiplots/advanced/__init__.py +8 -0
- publiplots/advanced/venn.py +374 -0
- publiplots/base/__init__.py +8 -0
- publiplots/base/bar.py +498 -0
- publiplots/base/scatter.py +498 -0
- publiplots/config.py +30 -0
- publiplots/fonts/Arial_Black.ttf +0 -0
- publiplots/fonts/Arial_Bold.ttf +0 -0
- publiplots/fonts/Arial_Bold_Italic.ttf +0 -0
- publiplots/fonts/Arial_Italic.ttf +0 -0
- publiplots/themes/__init__.py +84 -0
- publiplots/themes/colors.py +496 -0
- publiplots/themes/hatches.py +490 -0
- publiplots/themes/markers.py +255 -0
- publiplots/themes/styles.py +331 -0
- publiplots/utils/__init__.py +99 -0
- publiplots/utils/axes.py +453 -0
- publiplots/utils/fonts.py +50 -0
- publiplots/utils/io.py +213 -0
- publiplots/utils/legend.py +582 -0
- publiplots/utils/validation.py +465 -0
- publiplots-0.1.0.dist-info/METADATA +166 -0
- publiplots-0.1.0.dist-info/RECORD +26 -0
- publiplots-0.1.0.dist-info/WHEEL +4 -0
- publiplots-0.1.0.dist-info/licenses/LICENSE +21 -0
publiplots/__init__.py
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"""
|
|
2
|
+
PubliPlots: Publication-ready plotting with a clean, modular API.
|
|
3
|
+
|
|
4
|
+
PubliPlots provides a seaborn-like interface for creating beautiful,
|
|
5
|
+
publication-ready visualizations with sensible defaults and extensive
|
|
6
|
+
customization options.
|
|
7
|
+
|
|
8
|
+
Basic usage:
|
|
9
|
+
>>> import publiplots as pp
|
|
10
|
+
>>> pp.set_publication_style()
|
|
11
|
+
>>> fig, ax = pp.barplot(data=df, x='category', y='value')
|
|
12
|
+
>>> pp.savefig(fig, 'output.png')
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
__version__ = "0.1.0"
|
|
16
|
+
__author__ = "Jorge Botas"
|
|
17
|
+
|
|
18
|
+
# Core plotting functions (base)
|
|
19
|
+
from publiplots.base.bar import barplot
|
|
20
|
+
from publiplots.base.scatter import scatterplot
|
|
21
|
+
|
|
22
|
+
# Advanced plotting functions
|
|
23
|
+
from publiplots.advanced.venn import venn
|
|
24
|
+
|
|
25
|
+
# Utilities
|
|
26
|
+
from publiplots.utils.io import savefig, save_multiple, close_all
|
|
27
|
+
from publiplots.utils.axes import (
|
|
28
|
+
adjust_spines,
|
|
29
|
+
add_grid,
|
|
30
|
+
set_axis_labels,
|
|
31
|
+
add_reference_line,
|
|
32
|
+
)
|
|
33
|
+
from publiplots.utils.legend import (
|
|
34
|
+
HandlerCircle,
|
|
35
|
+
HandlerRectangle,
|
|
36
|
+
get_legend_handler_map,
|
|
37
|
+
create_legend_handles,
|
|
38
|
+
LegendBuilder,
|
|
39
|
+
create_legend_builder,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
# Theming
|
|
43
|
+
from publiplots.themes.colors import get_palette, list_palettes, show_palette, resolve_palette, DEFAULT_COLOR
|
|
44
|
+
from publiplots.themes.styles import (
|
|
45
|
+
set_publication_style,
|
|
46
|
+
set_minimal_style,
|
|
47
|
+
set_poster_style,
|
|
48
|
+
reset_style,
|
|
49
|
+
)
|
|
50
|
+
from publiplots.themes.markers import (
|
|
51
|
+
get_marker_cycle,
|
|
52
|
+
get_hatch_cycle,
|
|
53
|
+
STANDARD_MARKERS,
|
|
54
|
+
HATCH_PATTERNS,
|
|
55
|
+
)
|
|
56
|
+
from publiplots.themes.hatches import (
|
|
57
|
+
set_hatch_mode,
|
|
58
|
+
get_hatch_mode,
|
|
59
|
+
get_hatch_patterns,
|
|
60
|
+
list_hatch_patterns,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
__all__ = [
|
|
64
|
+
"__version__",
|
|
65
|
+
"__author__",
|
|
66
|
+
# Base plots
|
|
67
|
+
"barplot",
|
|
68
|
+
"scatterplot",
|
|
69
|
+
# Advanced plots
|
|
70
|
+
"venn",
|
|
71
|
+
# I/O utilities
|
|
72
|
+
"savefig",
|
|
73
|
+
"save_multiple",
|
|
74
|
+
"close_all",
|
|
75
|
+
# Axes utilities
|
|
76
|
+
"adjust_spines",
|
|
77
|
+
"add_grid",
|
|
78
|
+
"set_axis_labels",
|
|
79
|
+
"add_reference_line",
|
|
80
|
+
# Legend utilities
|
|
81
|
+
"HandlerCircle",
|
|
82
|
+
"HandlerRectangle",
|
|
83
|
+
"get_legend_handler_map",
|
|
84
|
+
"create_legend_handles",
|
|
85
|
+
"LegendBuilder",
|
|
86
|
+
"create_legend_builder",
|
|
87
|
+
# Color/palette functions
|
|
88
|
+
"get_palette",
|
|
89
|
+
"list_palettes",
|
|
90
|
+
"show_palette",
|
|
91
|
+
"resolve_palette",
|
|
92
|
+
# Color constants
|
|
93
|
+
"DEFAULT_COLOR",
|
|
94
|
+
# Style functions
|
|
95
|
+
"set_publication_style",
|
|
96
|
+
"set_minimal_style",
|
|
97
|
+
"set_poster_style",
|
|
98
|
+
"reset_style",
|
|
99
|
+
# Marker functions
|
|
100
|
+
"get_marker_cycle",
|
|
101
|
+
"get_hatch_cycle",
|
|
102
|
+
# Hatch functions
|
|
103
|
+
"set_hatch_mode",
|
|
104
|
+
"get_hatch_mode",
|
|
105
|
+
"get_hatch_patterns",
|
|
106
|
+
"list_hatch_patterns",
|
|
107
|
+
# Constants
|
|
108
|
+
"STANDARD_MARKERS",
|
|
109
|
+
"HATCH_PATTERNS",
|
|
110
|
+
]
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Venn diagram visualizations for publiplots.
|
|
3
|
+
|
|
4
|
+
This module provides functions for creating 2-way and 3-way Venn diagrams
|
|
5
|
+
with optional statistical analysis.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Optional, Dict, List, Union, Tuple
|
|
9
|
+
import matplotlib.pyplot as plt
|
|
10
|
+
from matplotlib.axes import Axes
|
|
11
|
+
from matplotlib_venn import venn2, venn2_circles, venn3, venn3_circles
|
|
12
|
+
from matplotlib_venn.layout.venn2 import DefaultLayoutAlgorithm as Venn2LayoutAlgorithm
|
|
13
|
+
from matplotlib_venn.layout.venn3 import DefaultLayoutAlgorithm as Venn3LayoutAlgorithm
|
|
14
|
+
from scipy.stats import hypergeom
|
|
15
|
+
import numpy as np
|
|
16
|
+
import seaborn as sns
|
|
17
|
+
|
|
18
|
+
from publiplots.config import DEFAULT_ALPHA, DEFAULT_FIGSIZE
|
|
19
|
+
from publiplots.themes.colors import get_palette
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def venn(
|
|
23
|
+
sets: Union[List[set], Dict[str, set]],
|
|
24
|
+
labels: Optional[List[str]] = None,
|
|
25
|
+
colors: Optional[List[str]] = None,
|
|
26
|
+
universe_size: Optional[int] = None,
|
|
27
|
+
weighted: bool = False,
|
|
28
|
+
include_size_in_label: bool = True,
|
|
29
|
+
alpha: float = DEFAULT_ALPHA,
|
|
30
|
+
figsize: Tuple[float, float] = DEFAULT_FIGSIZE,
|
|
31
|
+
ax: Optional[Axes] = None,
|
|
32
|
+
) -> Tuple[plt.Figure, Axes, Dict]:
|
|
33
|
+
"""
|
|
34
|
+
Create a Venn diagram for 2 or 3 sets with optional overlap statistics.
|
|
35
|
+
|
|
36
|
+
Parameters
|
|
37
|
+
----------
|
|
38
|
+
sets : list of sets or dict
|
|
39
|
+
Either a list of 2-3 sets, or a dictionary mapping labels to sets.
|
|
40
|
+
Example: [set1, set2] or {'Group A': set1, 'Group B': set2}
|
|
41
|
+
labels : list, optional
|
|
42
|
+
Labels for each set. If sets is a dict, labels are taken from keys.
|
|
43
|
+
Default: ['Set A', 'Set B', 'Set C']
|
|
44
|
+
colors : list, optional
|
|
45
|
+
Colors for each set. If None, uses pastel_categorical palette.
|
|
46
|
+
universe_size : int, optional
|
|
47
|
+
Total number of elements in the universe for statistical tests.
|
|
48
|
+
If None, no statistical analysis is performed.
|
|
49
|
+
weighted : bool, default=False
|
|
50
|
+
If False, uses unweighted layout where all regions have equal area.
|
|
51
|
+
If True, region sizes are proportional to set sizes.
|
|
52
|
+
include_size_in_label : bool, default=True
|
|
53
|
+
If True, appends set size to labels.
|
|
54
|
+
alpha : float, default=0.3
|
|
55
|
+
Transparency of set regions (0-1).
|
|
56
|
+
figsize : tuple, default=(10, 6)
|
|
57
|
+
Figure size (width, height).
|
|
58
|
+
ax : Axes, optional
|
|
59
|
+
Matplotlib axes object. If None, creates new figure.
|
|
60
|
+
|
|
61
|
+
Returns
|
|
62
|
+
-------
|
|
63
|
+
fig : Figure
|
|
64
|
+
Matplotlib figure object.
|
|
65
|
+
ax : Axes
|
|
66
|
+
Matplotlib axes object.
|
|
67
|
+
stats : dict
|
|
68
|
+
Dictionary containing overlap statistics and p-values if universe_size
|
|
69
|
+
is provided.
|
|
70
|
+
|
|
71
|
+
Examples
|
|
72
|
+
--------
|
|
73
|
+
Simple 2-way Venn diagram:
|
|
74
|
+
>>> set1 = {1, 2, 3, 4, 5}
|
|
75
|
+
>>> set2 = {4, 5, 6, 7, 8}
|
|
76
|
+
>>> fig, ax, stats = pp.venn([set1, set2],
|
|
77
|
+
... labels=['Group A', 'Group B'])
|
|
78
|
+
|
|
79
|
+
3-way Venn with custom colors:
|
|
80
|
+
>>> sets_dict = {'A': set1, 'B': set2, 'C': set3}
|
|
81
|
+
>>> colors = pp.get_palette('pastel_categorical', n_colors=3)
|
|
82
|
+
>>> fig, ax, stats = pp.venn(sets_dict, colors=colors)
|
|
83
|
+
|
|
84
|
+
With statistical testing:
|
|
85
|
+
>>> fig, ax, stats = pp.venn([set1, set2], universe_size=1000)
|
|
86
|
+
>>> print(f"P-value: {stats['p_value']:.4f}")
|
|
87
|
+
|
|
88
|
+
Notes
|
|
89
|
+
-----
|
|
90
|
+
- For 2-way Venn diagrams, uses hypergeometric test for overlap significance
|
|
91
|
+
- For 3-way Venn diagrams, tests significance of triple intersection
|
|
92
|
+
- Statistical tests require universe_size to be specified
|
|
93
|
+
"""
|
|
94
|
+
# Parse input sets
|
|
95
|
+
if isinstance(sets, dict):
|
|
96
|
+
labels = list(sets.keys())
|
|
97
|
+
sets = [set(s) for s in sets.values()]
|
|
98
|
+
else:
|
|
99
|
+
sets = [set(s) for s in sets]
|
|
100
|
+
if labels is None:
|
|
101
|
+
labels = [f"Set {chr(65+i)}" for i in range(len(sets))]
|
|
102
|
+
|
|
103
|
+
# Validate number of sets
|
|
104
|
+
if len(sets) not in [2, 3]:
|
|
105
|
+
raise ValueError("Venn diagram supports only 2 or 3 sets")
|
|
106
|
+
|
|
107
|
+
# Get colors
|
|
108
|
+
if colors is None:
|
|
109
|
+
colors = get_palette('pastel_categorical', n_colors=len(sets))
|
|
110
|
+
|
|
111
|
+
# Set up seaborn style
|
|
112
|
+
sns.set_theme("paper", style="white", font="Arial", font_scale=2)
|
|
113
|
+
|
|
114
|
+
# Create figure if not provided
|
|
115
|
+
if ax is None:
|
|
116
|
+
fig, ax = plt.subplots(figsize=figsize)
|
|
117
|
+
else:
|
|
118
|
+
fig = ax.get_figure()
|
|
119
|
+
|
|
120
|
+
# Decide on layout algorithm
|
|
121
|
+
if not weighted:
|
|
122
|
+
if len(sets) == 3:
|
|
123
|
+
layout_algorithm = Venn3LayoutAlgorithm(fixed_subset_sizes=(1,) * 7)
|
|
124
|
+
else:
|
|
125
|
+
layout_algorithm = Venn2LayoutAlgorithm(fixed_subset_sizes=(1, 1, 1))
|
|
126
|
+
else:
|
|
127
|
+
layout_algorithm = None
|
|
128
|
+
|
|
129
|
+
# Create Venn diagram based on number of sets
|
|
130
|
+
if len(sets) == 2:
|
|
131
|
+
stats = _create_venn2(
|
|
132
|
+
sets, labels, colors, universe_size, include_size_in_label,
|
|
133
|
+
alpha, layout_algorithm, weighted, ax
|
|
134
|
+
)
|
|
135
|
+
else:
|
|
136
|
+
stats = _create_venn3(
|
|
137
|
+
sets, labels, colors, universe_size, include_size_in_label,
|
|
138
|
+
alpha, layout_algorithm, weighted, ax
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
plt.tight_layout()
|
|
142
|
+
return fig, ax, stats
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def _create_venn2(
|
|
146
|
+
sets: List[set],
|
|
147
|
+
labels: List[str],
|
|
148
|
+
colors: List[str],
|
|
149
|
+
universe_size: Optional[int],
|
|
150
|
+
include_size_in_label: bool,
|
|
151
|
+
alpha: float,
|
|
152
|
+
layout_algorithm,
|
|
153
|
+
weighted: bool,
|
|
154
|
+
ax: Axes
|
|
155
|
+
) -> Dict:
|
|
156
|
+
"""Create 2-way Venn diagram."""
|
|
157
|
+
A, B = sets
|
|
158
|
+
labelA, labelB = labels[:2]
|
|
159
|
+
colorA, colorB = colors[:2]
|
|
160
|
+
|
|
161
|
+
if include_size_in_label:
|
|
162
|
+
labelA, labelB = f"{labelA} ({len(A)})", f"{labelB} ({len(B)})"
|
|
163
|
+
|
|
164
|
+
size_A = len(A)
|
|
165
|
+
size_B = len(B)
|
|
166
|
+
overlap = len(A.intersection(B))
|
|
167
|
+
|
|
168
|
+
# Prepare data for venn2
|
|
169
|
+
subsets = (size_A - overlap, size_B - overlap, overlap)
|
|
170
|
+
|
|
171
|
+
# Create Venn diagram
|
|
172
|
+
v = venn2(
|
|
173
|
+
subsets=subsets,
|
|
174
|
+
set_labels=(labelA, labelB),
|
|
175
|
+
set_colors=(colorA, colorB),
|
|
176
|
+
layout_algorithm=layout_algorithm,
|
|
177
|
+
ax=ax
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
# Increase transparency
|
|
181
|
+
for patch in v.patches:
|
|
182
|
+
if patch is not None:
|
|
183
|
+
patch.set_alpha(alpha)
|
|
184
|
+
|
|
185
|
+
# Add circles for clarity
|
|
186
|
+
circles = venn2_circles(
|
|
187
|
+
subsets=subsets if weighted else [1, 1, 1],
|
|
188
|
+
linestyle="solid",
|
|
189
|
+
linewidth=2,
|
|
190
|
+
color="black",
|
|
191
|
+
layout_algorithm=layout_algorithm,
|
|
192
|
+
ax=ax
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
# Override circle edge colors
|
|
196
|
+
for i, color in enumerate([colorA, colorB]):
|
|
197
|
+
if circles[i] is not None:
|
|
198
|
+
circles[i].set_edgecolor(color)
|
|
199
|
+
|
|
200
|
+
# Statistical test if universe_size provided
|
|
201
|
+
if universe_size is None:
|
|
202
|
+
return {
|
|
203
|
+
"set_sizes": [size_A, size_B],
|
|
204
|
+
"overlap": overlap,
|
|
205
|
+
"expected_overlap": None,
|
|
206
|
+
"fold_enrichment": None,
|
|
207
|
+
"log2_fold_enrichment": None,
|
|
208
|
+
"p_value": None,
|
|
209
|
+
"significant": None
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
# Hypergeometric test for overlap
|
|
213
|
+
p_value = hypergeom.sf(overlap - 1, universe_size, size_A, size_B)
|
|
214
|
+
expected_overlap = (size_A * size_B) / universe_size
|
|
215
|
+
fold_enrichment = overlap / expected_overlap if expected_overlap > 0 else float("inf")
|
|
216
|
+
log2_fold_enrichment = np.log2(fold_enrichment) if fold_enrichment > 0 else float("inf")
|
|
217
|
+
|
|
218
|
+
# Show stats
|
|
219
|
+
ax.text(
|
|
220
|
+
0.5, -0.12,
|
|
221
|
+
f"P-value: {p_value:.2e}",
|
|
222
|
+
horizontalalignment="center",
|
|
223
|
+
transform=ax.transAxes
|
|
224
|
+
)
|
|
225
|
+
ax.text(
|
|
226
|
+
0.5, -0.17,
|
|
227
|
+
f"Expected overlap: {expected_overlap:.2f}, "
|
|
228
|
+
f"Fold enrichment: {fold_enrichment:.2f}x (log2: {log2_fold_enrichment:.2f})",
|
|
229
|
+
horizontalalignment="center",
|
|
230
|
+
transform=ax.transAxes,
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
return {
|
|
234
|
+
"set_sizes": [size_A, size_B],
|
|
235
|
+
"overlap": overlap,
|
|
236
|
+
"expected_overlap": expected_overlap,
|
|
237
|
+
"fold_enrichment": fold_enrichment,
|
|
238
|
+
"log2_fold_enrichment": log2_fold_enrichment,
|
|
239
|
+
"p_value": p_value,
|
|
240
|
+
"significant": p_value < 0.05
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def _create_venn3(
|
|
245
|
+
sets: List[set],
|
|
246
|
+
labels: List[str],
|
|
247
|
+
colors: List[str],
|
|
248
|
+
universe_size: Optional[int],
|
|
249
|
+
include_size_in_label: bool,
|
|
250
|
+
alpha: float,
|
|
251
|
+
layout_algorithm,
|
|
252
|
+
weighted: bool,
|
|
253
|
+
ax: Axes
|
|
254
|
+
) -> Dict:
|
|
255
|
+
"""Create 3-way Venn diagram."""
|
|
256
|
+
A, B, C = sets
|
|
257
|
+
labelA, labelB, labelC = labels[:3]
|
|
258
|
+
colorA, colorB, colorC = colors[:3]
|
|
259
|
+
|
|
260
|
+
if include_size_in_label:
|
|
261
|
+
labelA = f"{labelA} ({len(A)})"
|
|
262
|
+
labelB = f"{labelB} ({len(B)})"
|
|
263
|
+
labelC = f"{labelC} ({len(C)})"
|
|
264
|
+
|
|
265
|
+
# Compute all subset sizes for 3-set Venn
|
|
266
|
+
onlyA = len(A - B - C)
|
|
267
|
+
onlyB = len(B - A - C)
|
|
268
|
+
onlyC = len(C - A - B)
|
|
269
|
+
AB_only = len((A & B) - C)
|
|
270
|
+
AC_only = len((A & C) - B)
|
|
271
|
+
BC_only = len((B & C) - A)
|
|
272
|
+
ABC = len(A & B & C)
|
|
273
|
+
|
|
274
|
+
size_A = len(A)
|
|
275
|
+
size_B = len(B)
|
|
276
|
+
size_C = len(C)
|
|
277
|
+
|
|
278
|
+
# Prepare data for venn3
|
|
279
|
+
subsets = (onlyA, onlyB, AB_only, onlyC, AC_only, BC_only, ABC)
|
|
280
|
+
|
|
281
|
+
# Create Venn diagram
|
|
282
|
+
v = venn3(
|
|
283
|
+
subsets=subsets,
|
|
284
|
+
set_labels=(labelA, labelB, labelC),
|
|
285
|
+
set_colors=(colorA, colorB, colorC),
|
|
286
|
+
layout_algorithm=layout_algorithm,
|
|
287
|
+
ax=ax
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
# Increase transparency
|
|
291
|
+
for patch in v.patches:
|
|
292
|
+
if patch is not None:
|
|
293
|
+
patch.set_alpha(alpha)
|
|
294
|
+
|
|
295
|
+
# Add circles
|
|
296
|
+
circles = venn3_circles(
|
|
297
|
+
subsets=subsets if weighted else [1]*7,
|
|
298
|
+
linestyle="solid",
|
|
299
|
+
linewidth=2,
|
|
300
|
+
color="black",
|
|
301
|
+
layout_algorithm=layout_algorithm,
|
|
302
|
+
ax=ax
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
# Override circle edge colors
|
|
306
|
+
for i, color in enumerate([colorA, colorB, colorC]):
|
|
307
|
+
if circles[i] is not None:
|
|
308
|
+
circles[i].set_edgecolor(color)
|
|
309
|
+
|
|
310
|
+
if universe_size is None:
|
|
311
|
+
return {
|
|
312
|
+
"set_sizes": [size_A, size_B, size_C],
|
|
313
|
+
"unique_counts": {
|
|
314
|
+
f"{labelA} only": onlyA,
|
|
315
|
+
f"{labelB} only": onlyB,
|
|
316
|
+
f"{labelC} only": onlyC
|
|
317
|
+
},
|
|
318
|
+
"pairwise_overlaps": {
|
|
319
|
+
f"{labelA}&{labelB} only": AB_only,
|
|
320
|
+
f"{labelA}&{labelC} only": AC_only,
|
|
321
|
+
f"{labelB}&{labelC} only": BC_only
|
|
322
|
+
},
|
|
323
|
+
"triple_overlap": ABC,
|
|
324
|
+
"expected_triple_overlap": None,
|
|
325
|
+
"fold_enrichment": None,
|
|
326
|
+
"log2_fold_enrichment": None,
|
|
327
|
+
"p_value": None,
|
|
328
|
+
"significant": None
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
# Hypergeometric test on triple intersection
|
|
332
|
+
BC = B & C
|
|
333
|
+
size_BC = len(BC)
|
|
334
|
+
p_value = hypergeom.sf(ABC - 1, universe_size, size_A, size_BC)
|
|
335
|
+
|
|
336
|
+
# Expected triple intersection
|
|
337
|
+
expected_abc = (size_A * size_B * size_C) / (universe_size**2)
|
|
338
|
+
fold_enrichment = (ABC / expected_abc) if expected_abc > 0 else float("inf")
|
|
339
|
+
log2_fold_enrichment = np.log2(fold_enrichment) if fold_enrichment > 0 else float("inf")
|
|
340
|
+
|
|
341
|
+
# Show p-value and enrichment
|
|
342
|
+
ax.text(
|
|
343
|
+
0.5, -0.10,
|
|
344
|
+
f"P-value (for triple intersection): {p_value:.2e}",
|
|
345
|
+
horizontalalignment="center",
|
|
346
|
+
transform=ax.transAxes
|
|
347
|
+
)
|
|
348
|
+
ax.text(
|
|
349
|
+
0.5, -0.15,
|
|
350
|
+
f"Expected triple intersection: {expected_abc:.2f}, "
|
|
351
|
+
f"Fold enrichment: {fold_enrichment:.2f}x (log2: {log2_fold_enrichment:.2f})",
|
|
352
|
+
horizontalalignment="center",
|
|
353
|
+
transform=ax.transAxes,
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
return {
|
|
357
|
+
"set_sizes": [size_A, size_B, size_C],
|
|
358
|
+
"unique_counts": {
|
|
359
|
+
f"{labelA} only": onlyA,
|
|
360
|
+
f"{labelB} only": onlyB,
|
|
361
|
+
f"{labelC} only": onlyC
|
|
362
|
+
},
|
|
363
|
+
"pairwise_overlaps": {
|
|
364
|
+
f"{labelA}&{labelB} only": AB_only,
|
|
365
|
+
f"{labelA}&{labelC} only": AC_only,
|
|
366
|
+
f"{labelB}&{labelC} only": BC_only
|
|
367
|
+
},
|
|
368
|
+
"triple_overlap": ABC,
|
|
369
|
+
"expected_triple_overlap": expected_abc,
|
|
370
|
+
"fold_enrichment": fold_enrichment,
|
|
371
|
+
"log2_fold_enrichment": log2_fold_enrichment,
|
|
372
|
+
"p_value": p_value,
|
|
373
|
+
"significant": p_value < 0.05
|
|
374
|
+
}
|