|
1 | 1 | """Provider class, modules & related functions""" |
2 | 2 | import os |
3 | | -from typing import List, Optional |
| 3 | +from typing import Optional |
4 | 4 |
|
5 | 5 | import pandas as pd |
6 | 6 | from loguru import logger |
7 | 7 |
|
8 | 8 | from genomepy.files import read_readme |
9 | | -from genomepy.providers.base import ASM_FORMAT |
10 | 9 | from genomepy.providers.ensembl import EnsemblProvider |
11 | 10 | from genomepy.providers.gencode import GencodeProvider |
12 | 11 | from genomepy.providers.local import LocalProvider |
13 | | -from genomepy.providers.ncbi import NcbiProvider |
| 12 | +from genomepy.providers.ncbi import NcbiProvider, download_assembly_report |
14 | 13 | from genomepy.providers.ucsc import UcscProvider |
15 | 14 | from genomepy.providers.url import UrlProvider |
16 | 15 | from genomepy.utils import get_genomes_dir, safe |
|
22 | 21 | "search", |
23 | 22 | "map_locations", |
24 | 23 | "download_assembly_report", |
25 | | - "nearest_assembly", |
26 | 24 | "online_providers", |
27 | 25 | ] |
28 | 26 |
|
@@ -134,106 +132,6 @@ def search(term, provider: str = None): |
134 | 132 | yield ret |
135 | 133 |
|
136 | 134 |
|
137 | | -def _closest_patch_lvl(reference, targets): |
138 | | - ref_patch = int(reference.split(".")[1]) if "." in reference else 0 |
139 | | - nearest = [999, []] |
140 | | - for target in targets: |
141 | | - tgt_patch = int(target.split(".")[1]) if "." in target else 0 |
142 | | - distance = abs(tgt_patch + 0.1 - ref_patch) # tiebreaker: newer > older patches |
143 | | - if distance == nearest[0]: |
144 | | - nearest[1].append(target) |
145 | | - if distance < nearest[0]: |
146 | | - nearest = [distance, [target]] |
147 | | - return nearest[1] |
148 | | - |
149 | | - |
150 | | -def _best_accession(reference: str, targets: list): |
151 | | - """Return the nearest accession ID from a list of IDs""" |
152 | | - if len(targets) == 1: |
153 | | - return targets[0] |
154 | | - |
155 | | - # GCA/GCF |
156 | | - matching_prefix = [t for t in targets if t.startswith(reference[0:3])] |
157 | | - if len(matching_prefix) > 0: |
158 | | - targets = matching_prefix |
159 | | - |
160 | | - # patch levels |
161 | | - # e.g. GCA_000002035.4 & GCA_000002035.3 |
162 | | - targets = _closest_patch_lvl(reference, targets) |
163 | | - |
164 | | - if len(targets) > 1: |
165 | | - logger.info( |
166 | | - f"Multiple matching accession numbers found. Selecting the closest ({targets[0]})." |
167 | | - ) |
168 | | - return targets[0] |
169 | | - |
170 | | - |
171 | | -def _best_search_result(asm_acc: str, results: List[list]) -> list: |
172 | | - """Return the best search result based on accession IDs.""" |
173 | | - results = [res for res in results if res[2] is not None] |
174 | | - |
175 | | - if len(results) > 1: |
176 | | - accessions = [res[2] for res in results] |
177 | | - bes_acc = _best_accession(asm_acc, accessions) |
178 | | - results = [res for res in results if res[2] == bes_acc] |
179 | | - |
180 | | - if len(results) > 0: |
181 | | - return results[0] |
182 | | - |
183 | | - |
184 | | -def nearest_assembly(asm_acc: str, provider: str) -> list: |
185 | | - """ |
186 | | - Return the search result of the assembly nearest to |
187 | | - the given accession ID, in the specified provider. |
188 | | - """ |
189 | | - if not asm_acc.startswith(("GCA_", "GCF_")): |
190 | | - raise ValueError("asm_acc must be an assembly accession ID.") |
191 | | - |
192 | | - search_results = list(search(asm_acc, provider=provider)) |
193 | | - best_result = _best_search_result(asm_acc, search_results) |
194 | | - if best_result is None: |
195 | | - logger.warning(f"No assembly similar to {asm_acc} on {provider}.") |
196 | | - return best_result |
197 | | - |
198 | | - |
199 | | -def download_assembly_report(asm_acc: str, fname: str = None): |
200 | | - """ |
201 | | - Retrieve the NCBI assembly report. |
202 | | -
|
203 | | - Returns the assembly_report as a pandas DataFrame if fname is not specified. |
204 | | -
|
205 | | - Parameters |
206 | | - ---------- |
207 | | - asm_acc : str |
208 | | - Assembly accession (GCA or GCF) |
209 | | - fname : str, optional |
210 | | - Save assembly_report to this filename. |
211 | | -
|
212 | | - Returns |
213 | | - ------- |
214 | | - pandas.DataFrame |
215 | | - NCBI assembly report. |
216 | | - """ |
217 | | - search_result = nearest_assembly(asm_acc, "NCBI") |
218 | | - if search_result is None: |
219 | | - return |
220 | | - ncbi_acc = search_result[2] |
221 | | - ncbi_name = search_result[0] |
222 | | - |
223 | | - # NCBI FTP location of assembly report |
224 | | - assembly_report = ( |
225 | | - f"https://ftp.ncbi.nlm.nih.gov/genomes/all/{ncbi_acc[0:3]}/" |
226 | | - + f"{ncbi_acc[4:7]}/{ncbi_acc[7:10]}/{ncbi_acc[10:13]}/" |
227 | | - + f"{ncbi_acc}_{ncbi_name}/{ncbi_acc}_{ncbi_name}_assembly_report.txt" |
228 | | - ) |
229 | | - asm_report = pd.read_csv(assembly_report, sep="\t", comment="#", names=ASM_FORMAT) |
230 | | - |
231 | | - if fname: |
232 | | - asm_report.to_csv(fname, sep="\t", index=False) |
233 | | - else: |
234 | | - return asm_report |
235 | | - |
236 | | - |
237 | 135 | def map_locations( |
238 | 136 | frm: str, to: str, genomes_dir: Optional[str] = None |
239 | 137 | ) -> Optional[pd.DataFrame]: |
@@ -279,7 +177,7 @@ def map_locations( |
279 | 177 | logger.warning("Cannot map without an assembly report.") |
280 | 178 | return |
281 | 179 |
|
282 | | - asm_report = pd.read_csv(frm_asm_report, sep="\t", comment="#") |
| 180 | + asm_report = pd.read_csv(frm_asm_report, sep="\t", comment="#", dtype=str) |
283 | 181 | asm_report["ensembl_name"] = asm_report["Sequence-Name"] |
284 | 182 | asm_report["ncbi_name"] = asm_report["Sequence-Name"] |
285 | 183 | asm_report["ucsc_name"] = asm_report["UCSC-style-name"] |
|
0 commit comments