wormcat3 0.1.11__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.
- wormcat3/__init__.py +7 -0
- wormcat3/annotations_manger.py +210 -0
- wormcat3/bubble_chart.py +240 -0
- wormcat3/constants.py +33 -0
- wormcat3/extdata/ORF_only_v2_nov-11-2021.csv +19898 -0
- wormcat3/extdata/ahringer_v2_nov-11-2021.csv +16211 -0
- wormcat3/extdata/orfeome_v2_nov-11-2021.csv +11525 -0
- wormcat3/extdata/whole_genome_v2_nov-11-2021.csv +31390 -0
- wormcat3/file_util.py +163 -0
- wormcat3/gsea_analyzer.py +287 -0
- wormcat3/statistical_analysis.py +193 -0
- wormcat3/sunburst.py +97 -0
- wormcat3/sunburst_template.py +182 -0
- wormcat3/wormcat.py +284 -0
- wormcat3/wormcat_error.py +73 -0
- wormcat3/wormcat_excel.py +425 -0
- wormcat3-0.1.11.dist-info/METADATA +43 -0
- wormcat3-0.1.11.dist-info/RECORD +20 -0
- wormcat3-0.1.11.dist-info/WHEEL +5 -0
- wormcat3-0.1.11.dist-info/top_level.txt +1 -0
wormcat3/__init__.py
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
import os
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from wormcat3 import file_util
|
|
5
|
+
import wormcat3.constants as cs
|
|
6
|
+
from wormcat3.wormcat_error import WormcatError, ErrorCode
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class AnnotationsManager:
|
|
10
|
+
""" Manages gene annotations and preprocessing. """
|
|
11
|
+
|
|
12
|
+
def __init__(self, annotation_file=cs.DEFAULT_ANNOTATION_FILE_NAME):
|
|
13
|
+
"""Initialize with the path to the annotation file."""
|
|
14
|
+
if file_util.is_file_path(annotation_file):
|
|
15
|
+
self.annotation_file_path = annotation_file
|
|
16
|
+
else:
|
|
17
|
+
self.annotation_file_path = file_util.find_file_path(annotation_file)
|
|
18
|
+
if not self.annotation_file_path:
|
|
19
|
+
raise WormcatError(f"Annotation file not found: {annotation_file}", ErrorCode.FILE_NOT_FOUND.to_dict())
|
|
20
|
+
|
|
21
|
+
self.annotations_df = self._load_annotations()
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _load_annotations(self):
|
|
26
|
+
""" Load annotations from file. """
|
|
27
|
+
try:
|
|
28
|
+
df = pd.read_csv(self.annotation_file_path) # type: ignore
|
|
29
|
+
df.columns = df.columns.str.replace(' ', '.')
|
|
30
|
+
if df.empty:
|
|
31
|
+
raise WormcatError(f"Annotation file '{self.annotation_file_path}' is empty.", ErrorCode.FILE_LOAD_FAILED.to_dict())
|
|
32
|
+
return df
|
|
33
|
+
except Exception as e:
|
|
34
|
+
raise WormcatError(f"Failed to load annotation file: {e}",
|
|
35
|
+
ErrorCode.FILE_LOAD_FAILED.to_dict(),
|
|
36
|
+
origin="AnnotationsManager._load_annotations")
|
|
37
|
+
|
|
38
|
+
@staticmethod
|
|
39
|
+
def available_annotation_files():
|
|
40
|
+
"""
|
|
41
|
+
Return a list of .csv file names located in:
|
|
42
|
+
- the 'extdata' directory (next to this script), and
|
|
43
|
+
- the directory defined by the WORMCAT_DATA_PATH environment variable (if set).
|
|
44
|
+
"""
|
|
45
|
+
file_path = Path(__file__).resolve()
|
|
46
|
+
extdata_path = file_path.parent / "extdata"
|
|
47
|
+
|
|
48
|
+
search_dirs = [extdata_path]
|
|
49
|
+
|
|
50
|
+
env_path = os.environ.get("WORMCAT_DATA_PATH")
|
|
51
|
+
if env_path:
|
|
52
|
+
env_path = Path(env_path)
|
|
53
|
+
if env_path.exists() and env_path.is_dir():
|
|
54
|
+
search_dirs.append(env_path)
|
|
55
|
+
|
|
56
|
+
found_files = set()
|
|
57
|
+
|
|
58
|
+
for directory in search_dirs:
|
|
59
|
+
if directory.exists():
|
|
60
|
+
for f in directory.glob("*.csv"):
|
|
61
|
+
if f.is_file():
|
|
62
|
+
found_files.add(f.name)
|
|
63
|
+
|
|
64
|
+
return sorted(found_files)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def annotation_name(self):
|
|
68
|
+
# Get the base name of the file (with extension, no directory)
|
|
69
|
+
if isinstance(self.annotation_file_path, (str, os.PathLike)) and self.annotation_file_path:
|
|
70
|
+
return os.path.basename(self.annotation_file_path)
|
|
71
|
+
else:
|
|
72
|
+
return "[NO_ANNOTATION_NAME]"
|
|
73
|
+
|
|
74
|
+
def get_gene_id_type(self, gene_set):
|
|
75
|
+
# Skip the first row (possible header), then filter valid gene entries
|
|
76
|
+
valid_genes = [
|
|
77
|
+
g for g in gene_set[1:] # Skip the first row
|
|
78
|
+
if isinstance(g, str) and len(g.strip()) > 1
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
if len(valid_genes) < 2:
|
|
82
|
+
raise WormcatError(
|
|
83
|
+
"Not enough valid gene entries to determine gene type.",
|
|
84
|
+
ErrorCode.INVALID_VALUE.to_dict()
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
# Check if both start with 'WBGene' or not
|
|
88
|
+
if valid_genes[0].startswith("WBGene") and valid_genes[1].startswith("WBGene"):
|
|
89
|
+
return cs.GENE_TYPE_WORMBASE_ID
|
|
90
|
+
elif not valid_genes[0].startswith("WBGene") and not valid_genes[1].startswith("WBGene"):
|
|
91
|
+
return cs.GENE_TYPE_SEQUENCE_ID
|
|
92
|
+
else:
|
|
93
|
+
raise WormcatError(
|
|
94
|
+
"Invalid gene data: One gene starts with 'WBGene', but the other does not.",
|
|
95
|
+
ErrorCode.INVALID_VALUE.to_dict()
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
@staticmethod
|
|
99
|
+
def dedup_list(input_list):
|
|
100
|
+
""" Deduplicate a list while preserving order. """
|
|
101
|
+
|
|
102
|
+
seen = set()
|
|
103
|
+
deduped_list = []
|
|
104
|
+
for item in input_list:
|
|
105
|
+
if item not in seen:
|
|
106
|
+
deduped_list.append(item)
|
|
107
|
+
seen.add(item)
|
|
108
|
+
return deduped_list
|
|
109
|
+
|
|
110
|
+
def add_annotations(self, gene_set_list, gene_type):
|
|
111
|
+
""" Add annotations to the gene set. """
|
|
112
|
+
|
|
113
|
+
gene_set_df = pd.DataFrame(gene_set_list, columns=[gene_type])
|
|
114
|
+
|
|
115
|
+
# Verify if 'gene_type' is a column in the DataFrame
|
|
116
|
+
if gene_type not in self.annotations_df.columns:
|
|
117
|
+
raise WormcatError(f"Column '{gene_type}' not found in the DataFrame.", ErrorCode.INVALID_VALUE.to_dict())
|
|
118
|
+
|
|
119
|
+
return pd.merge(gene_set_df, self.annotations_df, on=gene_type, how='left')
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def segment_genes_by_annotation_match(self, gene_set_list, gene_type):
|
|
123
|
+
""" Split genes into those with and without annotations. """
|
|
124
|
+
|
|
125
|
+
gene_set_df = pd.DataFrame(gene_set_list, columns=[gene_type])
|
|
126
|
+
gene_set_df = gene_set_df.fillna('') # It is rare but possible that we get NaN in the input
|
|
127
|
+
|
|
128
|
+
# Check if gene_type is in both dataframes
|
|
129
|
+
if gene_type not in gene_set_df.columns:
|
|
130
|
+
raise WormcatError(f"'{gene_type}' MUST be a column in the Gene Set DataFrame.", ErrorCode.MISSING_FIELD.to_dict())
|
|
131
|
+
if gene_type not in self.annotations_df.columns:
|
|
132
|
+
raise WormcatError(f"Incorrect '{gene_type}' name. Column not found in {self.annotation_name()} file.", ErrorCode.MISSING_FIELD.to_dict())
|
|
133
|
+
|
|
134
|
+
# Perform the left merge
|
|
135
|
+
merged_df = pd.merge(gene_set_df, self.annotations_df, on=gene_type, how='left')
|
|
136
|
+
|
|
137
|
+
# Split based on presence of annotation (assuming at least one non-key column in annotations_df)
|
|
138
|
+
annotation_columns = [col for col in self.annotations_df.columns if col != gene_type]
|
|
139
|
+
|
|
140
|
+
genes_matched_df = merged_df.dropna(subset=annotation_columns)
|
|
141
|
+
genes_not_matched_df = merged_df[merged_df[annotation_columns].isnull().all(axis=1)]
|
|
142
|
+
genes_not_matched_df = genes_not_matched_df[[gene_type]]
|
|
143
|
+
|
|
144
|
+
return genes_matched_df, genes_not_matched_df
|
|
145
|
+
|
|
146
|
+
def create_gmt_for_annotations(self, output_dir_path, id_col_nm="Wormbase.ID", output_file_nm_prefix="wormcat"):
|
|
147
|
+
""" Create GMT formatted files for all categories. """
|
|
148
|
+
for category in [1,2,3]:
|
|
149
|
+
gmt_format = self.category_to_gmt_format(category, id_col_nm)
|
|
150
|
+
output_file_path = f"{output_dir_path}/{output_file_nm_prefix}_cat_{category}.gmt"
|
|
151
|
+
self._save_gmt_to_file(gmt_format, output_file_path)
|
|
152
|
+
|
|
153
|
+
def category_to_gmt_format(self, category, id_col_nm="Wormbase.ID"):
|
|
154
|
+
""" Convert an annotation dataframe category to GMT format. """
|
|
155
|
+
category_col = f"Category.{category}"
|
|
156
|
+
id_col = "Function.ID"
|
|
157
|
+
|
|
158
|
+
category_df = self.annotations_df[[id_col_nm, category_col]]
|
|
159
|
+
category_df = category_df.rename(columns={category_col: id_col})
|
|
160
|
+
|
|
161
|
+
# Validate that required columns exist
|
|
162
|
+
required_cols = [id_col, id_col_nm]
|
|
163
|
+
missing_cols = [col for col in required_cols if col not in category_df.columns]
|
|
164
|
+
if missing_cols:
|
|
165
|
+
raise WormcatError(f"Incorrect column names: {', '.join(missing_cols)} {self.annotation_name()} file does not have these columns.",
|
|
166
|
+
ErrorCode.MISSING_FIELD.to_dict())
|
|
167
|
+
|
|
168
|
+
if category_df[id_col].isna().any():
|
|
169
|
+
raise WormcatError(f"{self.annotation_name()} dataframe Column '{id_col}' contains NaN values",
|
|
170
|
+
ErrorCode.INVALID_VALUE.to_dict())
|
|
171
|
+
|
|
172
|
+
# Use ID column as description if desc_col is None
|
|
173
|
+
grouped = category_df.groupby([id_col])[id_col_nm].apply(list).reset_index()
|
|
174
|
+
|
|
175
|
+
if len(grouped) == 0:
|
|
176
|
+
raise WormcatError("No gene sets found after grouping", ErrorCode.INVALID_VALUE.to_dict())
|
|
177
|
+
|
|
178
|
+
# Create GMT formatted dictionary
|
|
179
|
+
gmt_format = {}
|
|
180
|
+
for _, row in grouped.iterrows():
|
|
181
|
+
# Filter out any None or NaN values from gene list
|
|
182
|
+
gene_list = [str(gene) for gene in row[id_col_nm] if pd.notna(gene)]
|
|
183
|
+
|
|
184
|
+
# Only include if there are genes in the set
|
|
185
|
+
if gene_list:
|
|
186
|
+
gmt_format[row[id_col]] = gene_list
|
|
187
|
+
|
|
188
|
+
return gmt_format
|
|
189
|
+
|
|
190
|
+
def _save_gmt_to_file(self, gmt_format, output_file_path='wormcat.gmt'):
|
|
191
|
+
""" Write GMT formatted dictionary to disk. """
|
|
192
|
+
# Ensure the output directory exists
|
|
193
|
+
output_dir_path = os.path.dirname(output_file_path)
|
|
194
|
+
file_util.validate_directory_path(output_dir_path, not_empty_check = False)
|
|
195
|
+
|
|
196
|
+
# Write to GMT file
|
|
197
|
+
with open(output_file_path, 'w') as file:
|
|
198
|
+
for gene_id, gene_list in gmt_format.items():
|
|
199
|
+
description = gene_id
|
|
200
|
+
line = f"{gene_id}\t{description}\t" + '\t'.join(gene_list)
|
|
201
|
+
file.write(line + '\n')
|
|
202
|
+
|
|
203
|
+
if not os.path.exists(output_file_path):
|
|
204
|
+
print(f"[Warning] Output file {output_file_path} was not created.")
|
|
205
|
+
elif os.path.getsize(output_file_path) == 0:
|
|
206
|
+
print(f"[Warning] Output file {output_file_path} is empty.")
|
|
207
|
+
else:
|
|
208
|
+
print(f"Successfully created GMT file: {output_file_path}")
|
|
209
|
+
|
|
210
|
+
return output_file_path
|
wormcat3/bubble_chart.py
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
# cSpell:ignore ggplot ggtitle ylim darkred orangered dimgray
|
|
2
|
+
|
|
3
|
+
import matplotlib
|
|
4
|
+
matplotlib.use('Agg')
|
|
5
|
+
|
|
6
|
+
import pandas as pd
|
|
7
|
+
import numpy as np
|
|
8
|
+
from plotnine import (
|
|
9
|
+
ggplot, aes, geom_point, ggtitle, labs, ylim, coord_flip,
|
|
10
|
+
scale_color_manual, scale_size_manual,
|
|
11
|
+
theme, element_blank, element_rect, element_text
|
|
12
|
+
)
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
import wormcat3.constants as cs
|
|
15
|
+
import warnings
|
|
16
|
+
from wormcat3.wormcat_error import WormcatError, ErrorCode
|
|
17
|
+
|
|
18
|
+
# Suppress UserWarnings and DeprecationWarnings from plotnine
|
|
19
|
+
warnings.filterwarnings('ignore', category=UserWarning, module='plotnine')
|
|
20
|
+
warnings.filterwarnings('ignore', category=DeprecationWarning, module='plotnine')
|
|
21
|
+
warnings.filterwarnings('ignore', module="matplotlib.font_manager")
|
|
22
|
+
|
|
23
|
+
def preprocess_bubble_data(data_file_path, add_calibration=False):
|
|
24
|
+
"""
|
|
25
|
+
Preprocess CSV data for bubble plot visualization.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
csv_file_name (str): Path to the CSV file containing the data.
|
|
29
|
+
|
|
30
|
+
Returns:
|
|
31
|
+
pd.DataFrame: Processed DataFrame ready for plotting.
|
|
32
|
+
"""
|
|
33
|
+
try:
|
|
34
|
+
# Read the CSV file
|
|
35
|
+
bubbles_data = pd.read_csv(data_file_path)
|
|
36
|
+
column_names = bubbles_data.columns.tolist()
|
|
37
|
+
correction_method = 'Bonferroni' if 'Bonferroni' in column_names else 'FDR'
|
|
38
|
+
|
|
39
|
+
# Remove rows with missing values
|
|
40
|
+
bubbles_data = bubbles_data.dropna()
|
|
41
|
+
|
|
42
|
+
# # Add calibration points
|
|
43
|
+
if add_calibration:
|
|
44
|
+
calibration_points = pd.DataFrame({
|
|
45
|
+
"Category": ["calibration high", "calibration low"],
|
|
46
|
+
"RGS": [250, 1],
|
|
47
|
+
"AC": [0, 0],
|
|
48
|
+
"PValue": [1.00E-50, 1],
|
|
49
|
+
correction_method: [1.00E-50, 1]
|
|
50
|
+
})
|
|
51
|
+
bubbles_data = pd.concat([bubbles_data, calibration_points], ignore_index=True)
|
|
52
|
+
|
|
53
|
+
# Compute normalized value as a placeholder
|
|
54
|
+
scale_for_visualization=0.001
|
|
55
|
+
bubbles_data['bubbles_z'] = round(scale_for_visualization * (bubbles_data['PValue'] - bubbles_data['PValue'].mean()) / bubbles_data['PValue'].std(), 2)
|
|
56
|
+
|
|
57
|
+
# Create color coding based on Bonferroni-corrected p-values
|
|
58
|
+
conditions = [
|
|
59
|
+
(bubbles_data[correction_method] < 1e-40),
|
|
60
|
+
(bubbles_data[correction_method] < 1e-20),
|
|
61
|
+
(bubbles_data[correction_method] < 1e-10),
|
|
62
|
+
(bubbles_data[correction_method] < 1e-5),
|
|
63
|
+
(bubbles_data[correction_method] < 1e-2),
|
|
64
|
+
(bubbles_data[correction_method] < 5e-2),
|
|
65
|
+
(bubbles_data[correction_method] >= 5e-2)
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
values = ["Col1", "Col2", "Col3", "Col4", "Col5", "Col6", "NS"]
|
|
69
|
+
bubbles_data['p_value_type'] = np.select(conditions, values, default='')
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
# Create size coding based on RGS values
|
|
73
|
+
size_conditions = [
|
|
74
|
+
(bubbles_data['RGS'] > 150),
|
|
75
|
+
(bubbles_data['RGS'] > 100),
|
|
76
|
+
(bubbles_data['RGS'] > 75),
|
|
77
|
+
(bubbles_data['RGS'] > 50),
|
|
78
|
+
(bubbles_data['RGS'] > 25),
|
|
79
|
+
(bubbles_data['RGS'] > 10),
|
|
80
|
+
(bubbles_data['RGS'] > 5),
|
|
81
|
+
(bubbles_data['RGS'] > 2),
|
|
82
|
+
(bubbles_data['RGS'] <= 2)
|
|
83
|
+
]
|
|
84
|
+
|
|
85
|
+
size_values = ["Size1", "Size2", "Size3", "Size4", "Size5", "Size6", "Size7", "Size8", "Size9"]
|
|
86
|
+
bubbles_data['RGS_size'] = np.select(size_conditions, size_values, default='')
|
|
87
|
+
|
|
88
|
+
bubbles_data = order_categories_by_column(
|
|
89
|
+
bubbles_data,
|
|
90
|
+
category_column = 'Category',
|
|
91
|
+
order_by_column = correction_method,
|
|
92
|
+
ascending=False
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
return bubbles_data
|
|
96
|
+
|
|
97
|
+
except FileNotFoundError:
|
|
98
|
+
raise WormcatError(f"Error/Warning in create Bubble Chart: CSV file not found: {data_file_path}", ErrorCode.FILE_NOT_FOUND.to_dict())
|
|
99
|
+
except pd.errors.EmptyDataError:
|
|
100
|
+
raise WormcatError(f"Error/Warning in create Bubble Chart: The CSV file is empty: {data_file_path}", ErrorCode.FILE_LOAD_FAILED.to_dict())
|
|
101
|
+
except pd.errors.ParserError:
|
|
102
|
+
raise WormcatError(f"Error/Warning in create Bubble Chart: Error parsing CSV file: {data_file_path}", ErrorCode.FILE_LOAD_FAILED.to_dict())
|
|
103
|
+
except KeyError as e:
|
|
104
|
+
raise WormcatError(f"Error/Warning in create Bubble Chart: Required column missing in CSV file: {e}", ErrorCode.FILE_LOAD_FAILED.to_dict())
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def order_categories_by_column(df, category_column='Category', order_by_column='PValue', ascending=True):
|
|
108
|
+
"""
|
|
109
|
+
Convert a column to a categorical type ordered by another column.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
df (pd.DataFrame): DataFrame with category and ordering columns.
|
|
113
|
+
category_column (str): Name of the column to be converted to categorical.
|
|
114
|
+
order_by_column (str): Name of the column to order by.
|
|
115
|
+
ascending (bool): Sort order - True for ascending, False for descending.
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
pd.DataFrame: DataFrame with category column converted to categorical type.
|
|
119
|
+
"""
|
|
120
|
+
if df is None or df.empty:
|
|
121
|
+
raise WormcatError("While creating bubble plot found DataFrame empty or None", ErrorCode.INVALID_STATE.to_dict())
|
|
122
|
+
|
|
123
|
+
if category_column not in df.columns:
|
|
124
|
+
raise WormcatError(f"While creating bubble plot required column '{category_column}' not found in DataFrame",ErrorCode.INVALID_STATE.to_dict())
|
|
125
|
+
|
|
126
|
+
if order_by_column not in df.columns:
|
|
127
|
+
raise WormcatError(f"While creating bubble plot required column '{order_by_column}' not found in DataFrame",ErrorCode.INVALID_STATE.to_dict())
|
|
128
|
+
|
|
129
|
+
# Sort the dataframe by the specified column
|
|
130
|
+
sorted_df = df.sort_values(by=order_by_column, ascending=ascending)
|
|
131
|
+
|
|
132
|
+
# Get the categories in the order determined by the sorting column
|
|
133
|
+
category_order = sorted_df[category_column].unique()
|
|
134
|
+
|
|
135
|
+
# Convert category column to categorical type with the specified order
|
|
136
|
+
df[category_column] = pd.Categorical(
|
|
137
|
+
df[category_column],
|
|
138
|
+
categories=category_order,
|
|
139
|
+
ordered=True
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
return df
|
|
143
|
+
|
|
144
|
+
def generate_bubble_plot(df, svg_file_path, plot_title=cs.DEFAULT_TITLE, width=cs.DEFAULT_WIDTH, height=cs.DEFAULT_HEIGHT):
|
|
145
|
+
"""
|
|
146
|
+
Generate and save a bubble plot as an SVG using plotnine.
|
|
147
|
+
|
|
148
|
+
"""
|
|
149
|
+
if df is None or df.empty:
|
|
150
|
+
raise WormcatError("While Generating Bubble Plot found DataFrame empty or None", ErrorCode.INVALID_STATE.to_dict())
|
|
151
|
+
|
|
152
|
+
required_columns = ['Category', 'bubbles_z', 'RGS_size', 'p_value_type']
|
|
153
|
+
missing_columns = [col for col in required_columns if col not in df.columns]
|
|
154
|
+
if missing_columns:
|
|
155
|
+
raise WormcatError(f"While Generating Bubble Plot found required columns missing in DataFrame: {missing_columns}", ErrorCode.MISSING_FIELD.to_dict())
|
|
156
|
+
|
|
157
|
+
# Define color and size mapping dictionaries
|
|
158
|
+
color_mapping = {
|
|
159
|
+
"Col1": "darkred",
|
|
160
|
+
"Col2": "orangered",
|
|
161
|
+
"Col3": "tomato",
|
|
162
|
+
"Col4": "orange",
|
|
163
|
+
"Col5": "gold",
|
|
164
|
+
"Col6": "yellow",
|
|
165
|
+
"NS": "dimgray"
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
size_mapping = {
|
|
169
|
+
"Size1": 10,
|
|
170
|
+
"Size2": 9,
|
|
171
|
+
"Size3": 8,
|
|
172
|
+
"Size4": 7,
|
|
173
|
+
"Size5": 6,
|
|
174
|
+
"Size6": 5,
|
|
175
|
+
"Size7": 2.5,
|
|
176
|
+
"Size8": 1,
|
|
177
|
+
"Size9": 0.1
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
# Manual color and size scales
|
|
181
|
+
color_scale = scale_color_manual(
|
|
182
|
+
name="P value",
|
|
183
|
+
labels=["10-40", "10-20", "10-10", "10-5", "0.001", "0.05", "NS"],
|
|
184
|
+
values=color_mapping,
|
|
185
|
+
limits=list(color_mapping.keys())
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
size_scale = scale_size_manual(
|
|
189
|
+
name="Count",
|
|
190
|
+
labels=["150", "100", "75", "50", "25", "10", "5", "2", "Zero"],
|
|
191
|
+
values=size_mapping,
|
|
192
|
+
limits=list(size_mapping.keys())
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
# Build the plot
|
|
196
|
+
p = (
|
|
197
|
+
ggplot(df, aes(x='Category', y='bubbles_z', size='RGS_size', color='p_value_type')) +
|
|
198
|
+
ggtitle(title=plot_title) +
|
|
199
|
+
geom_point(stat='identity') +
|
|
200
|
+
color_scale +
|
|
201
|
+
size_scale +
|
|
202
|
+
labs(y=' ') +
|
|
203
|
+
ylim(-1.0, 1.0) +
|
|
204
|
+
coord_flip() +
|
|
205
|
+
theme(
|
|
206
|
+
panel_grid=element_blank(),
|
|
207
|
+
panel_background=element_blank(),
|
|
208
|
+
legend_key=element_rect(fill="white", color="white"),
|
|
209
|
+
text=element_text(family="Arial", size=14),
|
|
210
|
+
axis_text_x=element_blank(), # Remove x-axis tick labels
|
|
211
|
+
axis_ticks_x=element_blank(), # Remove x-axis tick marks
|
|
212
|
+
plot_title=element_text(margin={"b": 3, "units": "lines"})
|
|
213
|
+
)
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
try:
|
|
217
|
+
p.save(filename=svg_file_path, format = "svg", width = width, height = height)
|
|
218
|
+
except Exception as e:
|
|
219
|
+
raise WormcatError(f"Failed to save plot: {e}", ErrorCode.INTERNAL_ERROR.to_dict())
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def create_bubble_chart(dir_path: str, data_file_nm: str, plot_title="RGS", add_calibration=False) -> None:
|
|
223
|
+
try:
|
|
224
|
+
data_file_path = Path(dir_path) / data_file_nm
|
|
225
|
+
svg_file_nm = data_file_nm[:-3] + 'svg'
|
|
226
|
+
svg_file_path = Path(dir_path) / svg_file_nm
|
|
227
|
+
|
|
228
|
+
bubbles_data = preprocess_bubble_data(data_file_path, add_calibration = add_calibration)
|
|
229
|
+
|
|
230
|
+
# Scale the height of the bubble chart based on the number of items
|
|
231
|
+
bubbles_data_len = len(bubbles_data)
|
|
232
|
+
height_fun = lambda x: min(0.1 * x + 6.3, 25)
|
|
233
|
+
height = height_fun(bubbles_data_len)
|
|
234
|
+
|
|
235
|
+
generate_bubble_plot(bubbles_data, svg_file_path, plot_title = plot_title, height=height, width=9)
|
|
236
|
+
except Exception as e:
|
|
237
|
+
print(f"Error: {e}")
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
|
wormcat3/constants.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
# Enum for p-value adjustment methods
|
|
4
|
+
class PAdjustMethod(Enum):
|
|
5
|
+
BONFERRONI = 'bonferroni'
|
|
6
|
+
FDR = 'fdr_bh'
|
|
7
|
+
|
|
8
|
+
@classmethod
|
|
9
|
+
def from_str(cls, value: str) -> "PAdjustMethod":
|
|
10
|
+
for method in cls:
|
|
11
|
+
if method.value == value:
|
|
12
|
+
return method
|
|
13
|
+
raise ValueError(f"Invalid PAdjustMethod: {value}")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# Wormcat Configuration
|
|
17
|
+
DEFAULT_WORKING_DIR_PATH = "./wormcat_out"
|
|
18
|
+
DEFAULT_RUN_PREFIX = "run"
|
|
19
|
+
|
|
20
|
+
# Annotations Management Configuration
|
|
21
|
+
DEFAULT_P_ADJUST_THRESHOLD = 0.05
|
|
22
|
+
DEFAULT_ANNOTATION_FILE_NAME = "whole_genome_v2_nov-11-2021.csv"
|
|
23
|
+
|
|
24
|
+
# Gene Set Enrichment Analysis
|
|
25
|
+
DEFAULT_GSEA_RESULTS_DIR = "./gsea_results"
|
|
26
|
+
|
|
27
|
+
GENE_TYPE_WORMBASE_ID = "Wormbase.ID"
|
|
28
|
+
GENE_TYPE_SEQUENCE_ID = "Sequence.ID"
|
|
29
|
+
|
|
30
|
+
# Bubble Chart Configuration
|
|
31
|
+
DEFAULT_TITLE = "RGS"
|
|
32
|
+
DEFAULT_WIDTH = 6
|
|
33
|
+
DEFAULT_HEIGHT = 5.5
|