|
1 | | -import argparse |
2 | | -import asyncio |
| 1 | +from __future__ import annotations |
| 2 | + |
| 3 | +from pathlib import Path |
3 | 4 |
|
4 | 5 | import numpy as np |
5 | 6 | import pandas as pd |
|
12 | 13 |
|
13 | 14 |
|
14 | 15 | # Load Proteomics |
15 | | -def load_proteomics_data(datafilename, context_name): |
| 16 | +def process_proteomics_data(path: Path) -> pd.DataFrame: |
16 | 17 | """Load proteomics data from a given context and filename.""" |
17 | | - config = Config() |
18 | | - data_path = config.data_dir / "data_matrices" / context_name / datafilename |
19 | | - logger.info(f"Data Matrix Path: {data_path}") |
20 | | - |
21 | | - if data_path.exists(): |
22 | | - proteomics_data = pd.read_csv(data_path, header=0) |
23 | | - else: |
24 | | - logger.error(f"Error: file not found: {data_path}") |
25 | | - |
26 | | - return None |
27 | | - |
28 | 18 | # Preprocess data, drop na, duplicate ';' in symbol, |
29 | | - proteomics_data["gene_symbol"] = proteomics_data["gene_symbol"].astype(str) |
30 | | - proteomics_data.dropna(subset=["gene_symbol"], inplace=True) |
31 | | - pluralnames = proteomics_data[proteomics_data["gene_symbol"].str.contains(";") == True] # noqa: E712 |
32 | | - |
33 | | - for idx, row in pluralnames.iterrows(): |
34 | | - names = row["gene_symbol"].split(";") |
35 | | - rows = [] |
| 19 | + matrix: pd.DataFrame = pd.read_csv(path) |
| 20 | + if "gene_symbol" not in matrix.columns: |
| 21 | + raise ValueError("No gene_symbol column found in proteomics data.") |
36 | 22 |
|
37 | | - for name in names: |
38 | | - rowcopy = row.copy() |
39 | | - rowcopy["gene_symbol"] = name |
40 | | - rows.append(rowcopy) |
41 | | - proteomics_data.drop(index=idx, inplace=True) |
42 | | - proteomics_data = pd.concat([proteomics_data, pd.DataFrame(rows)], ignore_index=True) |
43 | | - |
44 | | - return proteomics_data |
| 23 | + matrix["gene_symbol"] = matrix["gene_symbol"].astype(str) |
| 24 | + matrix.dropna(subset=["gene_symbol"], inplace=True) |
| 25 | + matrix = matrix.assign(gene_symbol=matrix["gene_symbol"].str.split(";")).explode("gene_symbol") |
| 26 | + return matrix |
45 | 27 |
|
46 | 28 |
|
47 | 29 | # read map to convert to entrez |
48 | | -async def load_gene_symbol_map(gene_symbols: list[str]): |
| 30 | +async def load_gene_symbol_map(gene_symbols: list[str], entrez_map: Path | None = None): |
49 | 31 | """Add descirption....""" |
50 | | - config = Config() |
51 | | - filepath = config.data_dir / "proteomics_entrez_map.csv" |
52 | | - if filepath.exists(): |
53 | | - df = pd.read_csv(filepath, index_col="gene_symbol") |
| 32 | + if entrez_map and entrez_map.exists(): |
| 33 | + df = pd.read_csv(entrez_map, index_col="gene_symbol") |
54 | 34 | else: |
55 | 35 | biodbnet = BioDBNet() |
56 | 36 | df = await biodbnet.async_db2db( |
57 | | - values=gene_symbols, input_db=Input.GENE_SYMBOL, output_db=[Output.GENE_ID, Output.ENSEMBL_GENE_ID] |
| 37 | + values=gene_symbols, |
| 38 | + input_db=Input.GENE_SYMBOL, |
| 39 | + output_db=[Output.GENE_ID, Output.ENSEMBL_GENE_ID], |
58 | 40 | ) |
59 | 41 | df.loc[df["gene_id"] == "-", ["gene_id"]] = np.nan |
60 | | - df.to_csv(filepath, index_label="gene_symbol") |
| 42 | + df.to_csv(entrez_map, index_label="gene_symbol") |
61 | 43 |
|
62 | 44 | return df[~df.index.duplicated()] |
63 | 45 |
|
64 | 46 |
|
65 | | -def abundance_to_bool_group(context_name, group_name, abundance_matrix, rep_ratio, hi_rep_ratio, quantile): |
66 | | - """Descrioption....""" |
67 | | - config = Config() |
68 | | - output_dir = config.result_dir / context_name / "proteomics" |
69 | | - output_dir.mkdir(parents=True, exist_ok=True) |
70 | | - |
71 | | - # write group abundances to individual files |
72 | | - abundance_filepath = ( |
73 | | - config.result_dir / context_name / "proteomics" / "".join(["protein_abundance_", group_name, ".csv"]) |
74 | | - ) |
| 47 | +def abundance_to_bool_group( |
| 48 | + context_name, |
| 49 | + abundance_filepath: Path, |
| 50 | + output_gaussian_img_filepath: Path, |
| 51 | + output_z_score_matrix_filepath: Path, |
| 52 | + abundance_matrix: pd.DataFrame, |
| 53 | + replicate_ratio: float, |
| 54 | + high_confidence_replicate_ratio: float, |
| 55 | + quantile: float, |
| 56 | + output_boolean_filepath: Path, |
| 57 | +): |
| 58 | + """Convert proteomic data to boolean expression.""" |
75 | 59 | abundance_matrix.to_csv(abundance_filepath, index_label="entrez_gene_id") |
76 | | - protein_transform_main(abundance_matrix, output_dir, group_name) |
| 60 | + protein_transform_main( |
| 61 | + abundance_df=abundance_matrix, |
| 62 | + output_gaussian_img_filepath=output_gaussian_img_filepath, |
| 63 | + output_z_score_matrix_filepath=output_z_score_matrix_filepath, |
| 64 | + ) |
77 | 65 |
|
78 | 66 | # Logical Calculation |
79 | 67 | abundance_matrix_nozero = abundance_matrix.replace(0, np.nan) |
80 | 68 | thresholds = abundance_matrix_nozero.quantile(quantile, axis=0) |
81 | | - testbool = pd.DataFrame(0, columns=list(abundance_matrix), index=abundance_matrix.index) |
| 69 | + testbool = pd.DataFrame(0, columns=abundance_matrix.columns, index=abundance_matrix.index) |
82 | 70 |
|
83 | | - for col in list(abundance_matrix): |
| 71 | + for col in abundance_matrix.columns: |
84 | 72 | testbool.loc[abundance_matrix[col] > thresholds[col], [col]] = 1 |
85 | 73 |
|
86 | | - abundance_matrix["pos"] = (abundance_matrix > 0).sum(axis=1) / abundance_matrix.count(axis=1) |
87 | 74 | abundance_matrix["expressed"] = 0 |
88 | | - abundance_matrix.loc[(abundance_matrix["pos"] >= rep_ratio), ["expressed"]] = 1 |
89 | 75 | abundance_matrix["high"] = 0 |
90 | | - abundance_matrix.loc[(abundance_matrix["pos"] >= hi_rep_ratio), ["high"]] = 1 |
| 76 | + abundance_matrix["pos"] = abundance_matrix[abundance_matrix > 0].sum(axis=1) / abundance_matrix.count(axis=1) |
| 77 | + abundance_matrix.loc[(abundance_matrix["pos"] >= replicate_ratio), ["expressed"]] = 1 |
| 78 | + abundance_matrix.loc[(abundance_matrix["pos"] >= high_confidence_replicate_ratio), ["high"]] = 1 |
91 | 79 |
|
92 | | - bool_filepath = output_dir / f"bool_prot_Matrix_{context_name}_{group_name}.csv" |
93 | | - abundance_matrix.to_csv(bool_filepath, index_label="entrez_gene_id") |
| 80 | + abundance_matrix.to_csv(output_boolean_filepath, index_label="entrez_gene_id") |
94 | 81 |
|
95 | 82 |
|
96 | 83 | def to_bool_context(context_name, group_ratio, hi_group_ratio, group_names): |
@@ -156,129 +143,72 @@ def load_empty_dict(): |
156 | 143 |
|
157 | 144 |
|
158 | 145 | async def proteomics_gen( |
159 | | - config_file: str, |
160 | | - rep_ratio: float = 0.5, |
161 | | - group_ratio: float = 0.5, |
162 | | - hi_rep_ratio: float = 0.5, |
163 | | - hi_group_ratio: float = 0.5, |
| 146 | + context_name: str, |
| 147 | + config_filepath: Path, |
| 148 | + matrix_filepath: Path, |
| 149 | + output_boolean_filepath: Path, |
| 150 | + output_gaussian_img_filepath: Path, |
| 151 | + output_z_score_matrix_filepath: Path, |
| 152 | + input_entrez_map: Path | None = None, |
| 153 | + replicate_ratio: float = 0.5, |
| 154 | + batch_ratio: float = 0.5, |
| 155 | + high_confidence_replicate_ratio: float = 0.7, |
| 156 | + high_confience_batch_ratio: float = 0.7, |
164 | 157 | quantile: int = 25, |
165 | 158 | ): |
166 | 159 | """Generate proteomics data.""" |
167 | | - config = Config() |
168 | | - if not config_file: |
169 | | - raise ValueError("Config file must be provided") |
| 160 | + if not config_filepath.exists(): |
| 161 | + raise FileNotFoundError(f"Config file not found at {config_filepath}") |
| 162 | + if config_filepath.suffix not in (".xlsx", ".xls"): |
| 163 | + raise FileNotFoundError(f"Config file must be an xlsx or xls file at {config_filepath}") |
| 164 | + |
| 165 | + if not matrix_filepath.exists(): |
| 166 | + raise FileNotFoundError(f"Matrix file not found at {matrix_filepath}") |
| 167 | + if matrix_filepath.suffix not in {".csv"}: |
| 168 | + raise FileNotFoundError(f"Matrix file must be a csv file at {matrix_filepath}") |
170 | 169 |
|
171 | 170 | if quantile < 0 or quantile > 100: |
172 | 171 | raise ValueError("Quantile must be an integer from 0 to 100") |
173 | 172 | quantile /= 100 |
174 | 173 |
|
175 | | - prot_config_filepath = config.data_dir / "config_sheets" / config_file |
176 | | - logger.info(f"Config file is at '{prot_config_filepath}'") |
| 174 | + config_df = pd.read_excel(config_filepath, sheet_name=context_name) |
| 175 | + matrix: pd.DataFrame = process_proteomics_data(matrix_filepath) |
177 | 176 |
|
178 | | - xl = pd.ExcelFile(prot_config_filepath) |
179 | | - sheet_names = xl.sheet_names |
| 177 | + groups = config_df["group"].unique().tolist() |
180 | 178 |
|
181 | | - for context_name in sheet_names: |
182 | | - datafilename = "".join(["protein_abundance_", context_name, ".csv"]) |
183 | | - config_sheet = pd.read_excel(prot_config_filepath, sheet_name=context_name) |
184 | | - groups = config_sheet["group"].unique().tolist() |
| 179 | + for group in groups: |
| 180 | + indices = np.where([g == group for g in config_df["group"]]) |
| 181 | + sample_columns = [*np.take(config_df["sample_name"].to_numpy(), indices).ravel().tolist(), "gene_symbol"] |
| 182 | + matrix = matrix.loc[:, sample_columns] |
185 | 183 |
|
186 | | - for group in groups: |
187 | | - group_idx = np.where([g == group for g in config_sheet["group"].tolist()]) |
188 | | - cols = [*np.take(config_sheet["sample_name"].to_numpy(), group_idx).ravel().tolist(), "gene_symbol"] |
189 | | - |
190 | | - proteomics_data = load_proteomics_data(datafilename, context_name) |
191 | | - proteomics_data = proteomics_data.loc[:, cols] |
192 | | - |
193 | | - symbols_to_ids = await load_gene_symbol_map(gene_symbols=proteomics_data["gene_symbol"].tolist()) |
194 | | - proteomics_data.dropna(subset=["gene_symbol"], inplace=True) |
195 | | - if "uniprot" in proteomics_data.columns: |
196 | | - proteomics_data.drop(columns=["uniprot"], inplace=True) |
197 | | - |
198 | | - proteomics_data = proteomics_data.groupby(["gene_symbol"]).agg("max") |
199 | | - proteomics_data["entrez_gene_id"] = symbols_to_ids["gene_id"] |
200 | | - proteomics_data.dropna(subset=["entrez_gene_id"], inplace=True) |
201 | | - proteomics_data.set_index("entrez_gene_id", inplace=True) |
202 | | - |
203 | | - # save proteomics data by test |
204 | | - abundance_to_bool_group(context_name, group, proteomics_data, rep_ratio, hi_rep_ratio, quantile) |
205 | | - to_bool_context(context_name, group_ratio, hi_group_ratio, groups) |
206 | | - |
207 | | - |
208 | | -def _main(): |
209 | | - parser = argparse.ArgumentParser( |
210 | | - prog="proteomics_gen.py", |
211 | | - description="Description goes here", |
212 | | - epilog="For additional help, please post questions/issues in the MADRID GitHub repo at " |
213 | | - "https://github.com/HelikarLab/MADRID or email babessell@gmail.com", |
214 | | - ) |
215 | | - parser.add_argument( |
216 | | - "-c", |
217 | | - "--config-file", |
218 | | - type=str, |
219 | | - required=True, |
220 | | - dest="config_file", |
221 | | - help="The configuration file for proteomics", |
222 | | - ) |
223 | | - parser.add_argument( |
224 | | - "-r", |
225 | | - "--replicate-ratio", |
226 | | - type=float, |
227 | | - required=False, |
228 | | - default=0.5, |
229 | | - dest="rep_ratio", |
230 | | - help="Ratio of replicates required for a gene to be considered active in that group", |
231 | | - ) |
232 | | - parser.add_argument( |
233 | | - "-b", |
234 | | - "--batch-ratio", |
235 | | - type=float, |
236 | | - required=False, |
237 | | - default=0.5, |
238 | | - dest="group_ratio", |
239 | | - help="Ratio of groups (batches or studies) required for a gene to be considered active in a context", |
240 | | - ) |
241 | | - parser.add_argument( |
242 | | - "-hr", |
243 | | - "--high-replicate-ratio", |
244 | | - type=float, |
245 | | - required=False, |
246 | | - default=0.5, |
247 | | - dest="hi_rep_ratio", |
248 | | - help="Ratio of replicates required for a gene to be considered high-confidence in that group", |
249 | | - ) |
250 | | - parser.add_argument( |
251 | | - "-hb", |
252 | | - "--high-batch-ratio", |
253 | | - type=float, |
254 | | - required=False, |
255 | | - default=0.5, |
256 | | - dest="hi_group_ratio", |
257 | | - help="Ratio of groups (batches or studies) required for a gene to be considered high-confidence in a context", |
258 | | - ) |
259 | | - |
260 | | - parser.add_argument( |
261 | | - "-q", |
262 | | - "--quantile", |
263 | | - type=int, |
264 | | - required=False, |
265 | | - default=25, |
266 | | - dest="quantile", |
267 | | - help="The quantile of genes to accept. This should be an integer from 0% (no proteins pass) " |
268 | | - "to 100% (all proteins pass).", |
269 | | - ) |
270 | | - args = parser.parse_args() |
271 | | - asyncio.run( |
272 | | - proteomics_gen( |
273 | | - args.config_file, |
274 | | - args.rep_ratio, |
275 | | - args.group_ratio, |
276 | | - args.hi_rep_ratio, |
277 | | - args.hi_group_ratio, |
278 | | - args.quantile, |
| 184 | + symbols_to_gene_ids = await load_gene_symbol_map( |
| 185 | + gene_symbols=matrix["gene_symbol"].tolist(), |
| 186 | + entrez_map=input_entrez_map, |
| 187 | + ) |
| 188 | + matrix.dropna(subset=["gene_symbol"], inplace=True) |
| 189 | + if "uniprot" in matrix.columns: |
| 190 | + matrix.drop(columns=["uniprot"], inplace=True) |
| 191 | + |
| 192 | + matrix = matrix.groupby(["gene_symbol"]).agg("max") |
| 193 | + matrix["entrez_gene_id"] = symbols_to_gene_ids["gene_id"] |
| 194 | + matrix.dropna(subset=["entrez_gene_id"], inplace=True) |
| 195 | + matrix.set_index("entrez_gene_id", inplace=True) |
| 196 | + |
| 197 | + # bool_filepath = output_dir / f"bool_prot_Matrix_{context_name}_{group_name}.csv" |
| 198 | + abundance_to_bool_group( |
| 199 | + context_name=context_name, |
| 200 | + abundance_filepath=matrix_filepath, |
| 201 | + abundance_matrix=matrix, |
| 202 | + replicate_ratio=replicate_ratio, |
| 203 | + high_confidence_replicate_ratio=high_confidence_replicate_ratio, |
| 204 | + quantile=quantile, |
| 205 | + output_boolean_filepath=output_boolean_filepath, |
| 206 | + output_gaussian_img_filepath=output_gaussian_img_filepath, |
| 207 | + output_z_score_matrix_filepath=output_z_score_matrix_filepath, |
279 | 208 | ) |
| 209 | + to_bool_context( |
| 210 | + context_name=context_name, |
| 211 | + group_ratio=batch_ratio, |
| 212 | + hi_group_ratio=high_confience_batch_ratio, |
| 213 | + group_names=groups, |
280 | 214 | ) |
281 | | - |
282 | | - |
283 | | -if __name__ == "__main__": |
284 | | - _main() |
0 commit comments