pysec2pri 0.2.4__tar.gz → 0.3.0__tar.gz

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.
Files changed (59) hide show
  1. {pysec2pri-0.2.4 → pysec2pri-0.3.0}/PKG-INFO +127 -98
  2. {pysec2pri-0.2.4 → pysec2pri-0.3.0}/README.md +125 -96
  3. {pysec2pri-0.2.4 → pysec2pri-0.3.0}/pyproject.toml +22 -18
  4. pysec2pri-0.3.0/src/pysec2pri/__init__.py +174 -0
  5. pysec2pri-0.3.0/src/pysec2pri/api.py +1042 -0
  6. {pysec2pri-0.2.4 → pysec2pri-0.3.0}/src/pysec2pri/cli.py +319 -506
  7. {pysec2pri-0.2.4 → pysec2pri-0.3.0}/src/pysec2pri/config/chebi.yaml +25 -15
  8. {pysec2pri-0.2.4 → pysec2pri-0.3.0}/src/pysec2pri/config/ensembl.yaml +37 -12
  9. {pysec2pri-0.2.4 → pysec2pri-0.3.0}/src/pysec2pri/config/hgnc.yaml +21 -13
  10. {pysec2pri-0.2.4 → pysec2pri-0.3.0}/src/pysec2pri/config/hmdb_metabolites.yaml +12 -8
  11. {pysec2pri-0.2.4 → pysec2pri-0.3.0}/src/pysec2pri/config/hmdb_proteins.yaml +12 -8
  12. {pysec2pri-0.2.4 → pysec2pri-0.3.0}/src/pysec2pri/config/ncbi.yaml +17 -7
  13. {pysec2pri-0.2.4 → pysec2pri-0.3.0}/src/pysec2pri/config/uniprot.yaml +8 -3
  14. {pysec2pri-0.2.4 → pysec2pri-0.3.0}/src/pysec2pri/config/vgnc.yaml +18 -13
  15. {pysec2pri-0.2.4 → pysec2pri-0.3.0}/src/pysec2pri/config/wikidata.yaml +15 -14
  16. {pysec2pri-0.2.4 → pysec2pri-0.3.0}/src/pysec2pri/consolidate.py +63 -76
  17. pysec2pri-0.3.0/src/pysec2pri/constants.py +77 -0
  18. {pysec2pri-0.2.4 → pysec2pri-0.3.0}/src/pysec2pri/download.py +2 -2
  19. {pysec2pri-0.2.4 → pysec2pri-0.3.0}/src/pysec2pri/downloads/chebi.py +9 -4
  20. {pysec2pri-0.2.4 → pysec2pri-0.3.0}/src/pysec2pri/downloads/ensembl.py +10 -30
  21. {pysec2pri-0.2.4 → pysec2pri-0.3.0}/src/pysec2pri/downloads/hmdb.py +2 -8
  22. {pysec2pri-0.2.4 → pysec2pri-0.3.0}/src/pysec2pri/exports.py +59 -36
  23. pysec2pri-0.3.0/src/pysec2pri/logging.py +29 -0
  24. pysec2pri-0.3.0/src/pysec2pri/parsers/__init__.py +89 -0
  25. pysec2pri-0.3.0/src/pysec2pri/parsers/_nomenclature.py +239 -0
  26. {pysec2pri-0.2.4 → pysec2pri-0.3.0}/src/pysec2pri/parsers/base.py +35 -57
  27. {pysec2pri-0.2.4 → pysec2pri-0.3.0}/src/pysec2pri/parsers/chebi.py +32 -54
  28. {pysec2pri-0.2.4 → pysec2pri-0.3.0}/src/pysec2pri/parsers/ensembl.py +79 -63
  29. pysec2pri-0.3.0/src/pysec2pri/parsers/hgnc.py +263 -0
  30. {pysec2pri-0.2.4 → pysec2pri-0.3.0}/src/pysec2pri/parsers/hmdb.py +18 -39
  31. {pysec2pri-0.2.4 → pysec2pri-0.3.0}/src/pysec2pri/parsers/ncbi.py +20 -59
  32. {pysec2pri-0.2.4 → pysec2pri-0.3.0}/src/pysec2pri/parsers/uniprot.py +47 -56
  33. {pysec2pri-0.2.4 → pysec2pri-0.3.0}/src/pysec2pri/parsers/vgnc.py +50 -270
  34. {pysec2pri-0.2.4 → pysec2pri-0.3.0}/src/pysec2pri/parsers/wikidata.py +33 -167
  35. {pysec2pri-0.2.4 → pysec2pri-0.3.0}/src/pysec2pri/queries/__init__.py +13 -44
  36. pysec2pri-0.3.0/src/pysec2pri/update.py +249 -0
  37. {pysec2pri-0.2.4 → pysec2pri-0.3.0}/src/pysec2pri/version.py +1 -1
  38. pysec2pri-0.2.4/src/pysec2pri/__init__.py +0 -141
  39. pysec2pri-0.2.4/src/pysec2pri/api.py +0 -2172
  40. pysec2pri-0.2.4/src/pysec2pri/constants.py +0 -41
  41. pysec2pri-0.2.4/src/pysec2pri/logging.py +0 -65
  42. pysec2pri-0.2.4/src/pysec2pri/parsers/__init__.py +0 -42
  43. pysec2pri-0.2.4/src/pysec2pri/parsers/hgnc.py +0 -481
  44. pysec2pri-0.2.4/src/pysec2pri/update_ids.py +0 -1138
  45. pysec2pri-0.2.4/src/pysec2pri/xref.py +0 -7
  46. {pysec2pri-0.2.4 → pysec2pri-0.3.0}/LICENSE +0 -0
  47. {pysec2pri-0.2.4 → pysec2pri-0.3.0}/src/pysec2pri/__main__.py +0 -0
  48. {pysec2pri-0.2.4 → pysec2pri-0.3.0}/src/pysec2pri/config/__init__.py +0 -0
  49. {pysec2pri-0.2.4 → pysec2pri-0.3.0}/src/pysec2pri/downloads/__init__.py +0 -0
  50. {pysec2pri-0.2.4 → pysec2pri-0.3.0}/src/pysec2pri/downloads/hgnc.py +0 -0
  51. {pysec2pri-0.2.4 → pysec2pri-0.3.0}/src/pysec2pri/downloads/ncbi.py +0 -0
  52. {pysec2pri-0.2.4 → pysec2pri-0.3.0}/src/pysec2pri/downloads/uniprot.py +0 -0
  53. {pysec2pri-0.2.4 → pysec2pri-0.3.0}/src/pysec2pri/py.typed +0 -0
  54. {pysec2pri-0.2.4 → pysec2pri-0.3.0}/src/pysec2pri/queries/chemical_redirects.rq +0 -0
  55. {pysec2pri-0.2.4 → pysec2pri-0.3.0}/src/pysec2pri/queries/chemical_redirects_test.rq +0 -0
  56. {pysec2pri-0.2.4 → pysec2pri-0.3.0}/src/pysec2pri/queries/gene_redirects.rq +0 -0
  57. {pysec2pri-0.2.4 → pysec2pri-0.3.0}/src/pysec2pri/queries/gene_redirects_test.rq +0 -0
  58. {pysec2pri-0.2.4 → pysec2pri-0.3.0}/src/pysec2pri/queries/protein_redirects.rq +0 -0
  59. {pysec2pri-0.2.4 → pysec2pri-0.3.0}/src/pysec2pri/queries/protein_redirects_test.rq +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pysec2pri
3
- Version: 0.2.4
3
+ Version: 0.3.0
4
4
  Summary: Secondary to primary identifier mapping
5
5
  Keywords: snekpack,cookiecutter
6
6
  Author: Javier Millán Acosta
@@ -40,7 +40,7 @@ Requires-Dist: numpy
40
40
  Requires-Dist: rdkit
41
41
  Requires-Dist: polars-runtime-32
42
42
  Requires-Dist: sssom-schema==1.1.0a5
43
- Requires-Dist: mapkgsutils==0.1.2
43
+ Requires-Dist: mapkgsutils==0.2.5
44
44
  Maintainer: Javier Millán Acosta
45
45
  Maintainer-email: Javier Millán Acosta <javier.millan.acosta@gmail.com>
46
46
  Requires-Python: >=3.10
@@ -96,60 +96,56 @@ uv pip install git+https://github.com/jmillanacosta/pysec2pri.git
96
96
 
97
97
  ### Generating mapping sets
98
98
 
99
- Mapping sets can be generated from bash:
99
+ Most sources have two commands. `ids` maps retired identifiers to current ones,
100
+ `labels` maps old labels to current ones:
100
101
 
101
102
  ```bash
102
- # ID mapping sets
103
- pysec2pri chebi ids (--help)
104
- pysec2pri ensembl ids (--help)
105
- pysec2pri hgnc ids (--help)
106
- pysec2pri vgnc ids (--help)
107
- pysec2pri ncbi ids (--help)
108
- pysec2pri hmdb-protein ids (--help)
109
- pysec2pri hmdb-gene ids (--help)
110
- pysec2pri uniprot ids (--help)
111
- # Label mapping sets
112
- pysec2pri chebi labels (--help)
113
- pysec2pri hgnc labels (--help)
114
- pysec2pri ensembl labels (--help)
115
- pysec2pri vgnc labels (--help)
103
+ pysec2pri hgnc ids
104
+ pysec2pri hgnc labels
116
105
  ```
117
106
 
118
- Or python:
107
+ Run `pysec2pri --help` to see every source, and `pysec2pri <source> ids --help`
108
+ for one source's options. Input files are downloaded unless you pass them:
119
109
 
120
- ```
121
- from pysec2pri import generate_ensembl_labels, generate_ensembl
110
+ ```bash
111
+ pysec2pri hgnc ids --withdrawn withdrawn.txt --complete hgnc_complete_set.txt
122
112
  ```
123
113
 
124
- Replacing Ensembl by the supported database. These functions return either a
125
- `IdMappingSet` or `LabelMappingSet`, SSSOM `MappingSet`s.
114
+ In Python there are two functions, one per kind:
126
115
 
127
- For more options and help on any command:
116
+ ```python
117
+ from pysec2pri import generate_ids, generate_labels, sources
128
118
 
129
- ```bash
130
- pysec2pri --help
119
+ sources() # every source
120
+ sources("labels") # sources with labels
121
+
122
+ hgnc = generate_ids("hgnc")
123
+ chebi = generate_labels("chebi", subset="3star", version="350")
124
+ ensembl = generate_ids("ensembl", version="115", species="9606")
131
125
  ```
132
126
 
133
- The default output is in [SSSOM](https://mapping-commons.github.io/sssom/)
134
- (Simple Standard for Sharing Ontology Mappings) TSV format.
127
+ Both return an SSSOM `MappingSet`: `IdMappingSet` or `LabelMappingSet`. Subjects
128
+ are secondary, objects are primary. Options that a source does not have are
129
+ ignored, so `species` on HGNC does nothing.
130
+
131
+ The default output is [SSSOM](https://mapping-commons.github.io/sssom/) TSV.
135
132
 
136
133
  ### Updating IDs and labels
137
134
 
138
- A generated mapping set can be used to update IDs and labels in Python: ChEBI
139
- synonyms:
135
+ Use a mapping set to update your own data. Labels:
140
136
 
141
137
  ```python
142
- from pysec2pri import generate_chebi_synonyms, resolve_labels
143
- chebi_ms = generate_chebi_synonyms()
144
- resolve_labels(["Glucose", "ATP", "Guanine"], cs)
138
+ from pysec2pri import generate_labels, resolve_labels
139
+ chebi = generate_labels("chebi")
140
+ resolve_labels(["Glucose", "ATP", "Guanine"], chebi)
145
141
  ```
146
142
 
147
- Ensembl gene identifiers in a dataframe:
143
+ Identifiers in a dataframe:
148
144
 
149
145
  ```python
150
- from pysec2pri import update_ids, generate_ensembl
151
- ens_ms = generate_ensembl(version="115", species="9606")
152
- df_with_new_column = update_ids(mapping_set=ens_ms, ids = df, at="Ensembl_id") # `at` is the name of the column
146
+ from pysec2pri import generate_ids, update_ids
147
+ ensembl = generate_ids("ensembl", version="115", species="9606")
148
+ df_with_new_column = update_ids(mapping_set=ensembl, ids=df, at="Ensembl_id") # `at` is the column name
153
149
  ```
154
150
 
155
151
  Or from the command line, given a TSV file `gene_ex.tsv`:
@@ -182,15 +178,17 @@ pysec2pri hgnc ids # outputs hgnc_{version}_sssom.tsv
182
178
  pysec2pri update-ids gene_ex.tsv hgnc --at gene --mapping hgnc_{version}_sssom.tsv
183
179
  ```
184
180
 
185
- In Python, `load_<datasource>` reads a written SSSOM file back into the same
186
- `IdMappingSet`/`LabelMappingSet`:
181
+ In Python, `load_mapping` reads a written SSSOM file back in, so you can
182
+ generate once and reuse the file:
187
183
 
188
184
  ```python
189
- from pysec2pri import load_hgnc, update_ids
190
- hgnc_ms = load_hgnc("hgnc_115_sssom.tsv")
191
- df_with_new_column = update_ids(mapping_set=hgnc_ms, ids=df, at="gene")
185
+ from pysec2pri import load_mapping, update_ids
186
+ hgnc = load_mapping("hgnc_115_sssom.tsv")
187
+ df_with_new_column = update_ids(mapping_set=hgnc, ids=df, at="gene")
192
188
  ```
193
189
 
190
+ Use `load_label_mapping` for a label mapping set.
191
+
194
192
  Ambiguous mappings (where a deprecated ID or label serves as a recommended for
195
193
  another entity) are not resolved, but flagged for users to solve them manually.
196
194
  If the input file has a column of known aliases or synonyms for each row, pass
@@ -209,56 +207,80 @@ pysec2pri ambiguous hgnc-labels
209
207
 
210
208
  ## Mapping types
211
209
 
212
- ### Deprecations (IDs)
210
+ Every row is one secondary (`subject`) and one primary (`object`). Which
211
+ predicate joins them says what happened to it.
212
+
213
+ ### IDs
213
214
 
214
- A deprecated ID is mapped to its replacement via `IAO:0100001` ("term replaced
215
- by"). Each row is 1-to-1: one secondary `subject_id` : one primary `object_id`.
215
+ A retired ID either has a replacement or does not:
216
216
 
217
217
  ```mermaid
218
218
  flowchart LR
219
- D["subject_id (deprecated)"]
220
- P["object_id (primary)"]
221
- D -->|"term replaced by"| P
219
+ S["subject_id (retired)"]
220
+ P["object_id (current)"]
221
+ N["sssom:NoTermFound"]
222
+ S -->|"IAO:0100001 (term replaced by)"| P
223
+ S -->|"oboInOwl:consider (no replacement)"| N
222
224
  ```
223
225
 
224
- **Ambiguity** happens when the same ID appears as both a `subject_id`
225
- (secondary) and an `object_id` (primary) across different mappings.
226
+ `mapping_cardinality` says how the two sides line up: `1:1`, `n:1` when several
227
+ retired IDs were merged into one, `1:0` for a withdrawal with no replacement.
228
+
229
+ ### Labels
230
+
231
+ One label mapping set holds both of a source's label changes, told apart by
232
+ predicate:
226
233
 
227
234
  ```mermaid
228
235
  flowchart LR
229
- A["A (primary of C and secondary of B)"] -->|term replaced by| B["B (primary)"]
230
- C["C (secondary)"] -->|term replaced by| A
236
+ PREV["subject_label (previous symbol)"]
237
+ ALIAS["subject_label (alias / synonym)"]
238
+ CUR["object_label / object_id (current)"]
239
+ PREV -->|"IAO:0100001 (term replaced by)"| CUR
240
+ ALIAS -->|"oboInOwl:hasExactSynonym"| CUR
231
241
  ```
232
242
 
233
- ### Labels
243
+ A previous symbol is one the entity used to have. An alias is another name it
244
+ still goes by. Only the first is a rename; the second is what the resolver uses
245
+ as evidence below.
234
246
 
235
- The same 1-to-1 pattern applies to label (or symbol) mappings: a previous or
236
- alias label (`subject_label`) maps to the current label (`object_label`) of the
237
- same entity via `IAO:0100001`.
247
+ ### Looking further back with `--consolidate`
238
248
 
239
- **Ambiguity** appears when the same label is both a `subject_label` (previous
240
- name, secondary) and an `object_label` (current name, primary) across different
241
- mappings.
249
+ `--consolidate` reads all of the source's past releases, finds mappings the
250
+ current release no longer mentions, and gives every mapping the release it first
251
+ appeared in:
242
252
 
243
- ### Aliases / synonyms
253
+ ```bash
254
+ pysec2pri hgnc ids --consolidate -o hgnc.sssom.tsv
255
+ ```
244
256
 
245
- Alias mappings use `oboInOwl:hasExactSynonym`. The alias is the `subject_label`
246
- and the primary name is the `object_label`/`object_id`.
257
+ ```python
258
+ from pysec2pri import generate_ids, supports_consolidate
259
+ supports_consolidate("hgnc", "ids")
260
+ generate_ids("hgnc", consolidate=True)
261
+ ```
262
+
263
+ ### Ambiguity
264
+
265
+ A value is ambiguous when it is retired in one row and current in another. This
266
+ is not resolved: the row is flagged and left alone.
247
267
 
248
268
  ```mermaid
249
269
  flowchart LR
250
- A["subject_label (alias / synonym)"]
251
- P["object_label/object_id (primary)"]
252
- A -->|"oboInOwl:hasExactSynonym"| P
270
+ C["C (retired)"] -->|term replaced by| A["A (retired, and current for C)"]
271
+ A -->|term replaced by| B["B (current)"]
253
272
  ```
254
273
 
274
+ The same holds for labels: a symbol can be a `subject_label` (someone's old
275
+ name) and an `object_label` (someone else's current name).
276
+
255
277
  ### Resolving ambiguity with alias/synonym hints
256
278
 
257
279
  When a name is ambiguous, alias mappings are used as evidence. For each
258
280
  candidate interpretation the resolver checks whether any user-supplied hint
259
281
  matches a known alias of that candidate's primary entity. A hit on the secondary
260
282
  candidate's target confirms the name is being used as a previous name; a hit on
261
- the primary candidate's own aliases confirms it is already current.
283
+ the primary candidate's aliases confirms it is already current.
262
284
 
263
285
  ```mermaid
264
286
  flowchart TD
@@ -279,23 +301,21 @@ flowchart TD
279
301
 
280
302
  Alias hints are one kind of _context_: a per-row piece of independent evidence
281
303
  that helps decide which entity an ambiguous name actually means. `update_ids`
282
- and `update_labels` support three kinds, via `pysec2pri.context.ContextSpec`:
304
+ and `update_labels` support three kinds, via `ContextSpec`:
283
305
 
284
306
  - **`label`** -- an alias/synonym string (the `synonyms=`/`--synonyms` shown
285
307
  above).
286
308
  - **`id`** -- a related/foreign identifier string, matched the same way.
287
309
  - **`xref`** -- a cross-reference token (e.g. an Ensembl ID) resolved through an
288
- independent crosswalk table (`pysec2pri.context.XrefMapping`), rather than the
289
- mapping set's own alias index.
310
+ independent crosswalk table (`XrefMapping`).
290
311
 
291
312
  All three only ever touch cells already flagged ambiguous, and every attempt can
292
313
  be written to an auditable decision log:
293
314
 
294
315
  ```python
295
- from pysec2pri import generate_hgnc_labels, update_labels
296
- from pysec2pri.context import load_xref_mapping
316
+ from pysec2pri import generate_labels, load_xref_mapping, update_labels
297
317
 
298
- label_ms = generate_hgnc_labels()
318
+ label_ms = generate_labels("hgnc")
299
319
  ensembl_to_hgnc = load_xref_mapping("ensembl_to_hgnc.tsv") # subject_id/object_id/object_label
300
320
 
301
321
  resolved = update_labels(
@@ -314,43 +334,52 @@ pysec2pri update-labels genes.tsv hgnc --at gene_name \
314
334
  --report decisions.tsv
315
335
  ```
316
336
 
317
- ### `crosswalk`: a direct identifier lookup helper
337
+ ### Crosswalk tables
318
338
 
319
- For the common case of "map this one column of identifiers to an HGNC ID (or
320
- back to a symbol)", `crosswalk` is a thin wrapper over the same machinery:
339
+ `--xref-source` names a table listed in the source's config. `hgnc_custom` is
340
+ HGNC download, one row per gene:
321
341
 
322
- ```bash
323
- pysec2pri crosswalk --value TP53 --from symbol --to hgnc_id
324
- pysec2pri crosswalk genes.tsv --from ensembl --to hgnc_id --at ensembl_id -o out.tsv
325
- ```
342
+ | HGNC ID | Approved symbol | Status | Previous symbols | NCBI Gene ID | Ensembl ID | UniProt ID |
343
+ | ---------- | --------------- | ---------------- | --------------------------- | ------------ | --------------- | ---------- |
344
+ | HGNC:5 | A1BG | Approved | | 1 | ENSG00000121410 | P04217 |
345
+ | HGNC:37133 | A1BG-AS1 | Approved | NCRNA00181, A1BGAS, A1BG-AS | 503538 | ENSG00000268895 | |
346
+ | HGNC:6 | A1S9T | Symbol Withdrawn | | | | |
347
+ | HGNC:7 | A2M | Approved | | 2 | ENSG00000175899 | P01023 |
326
348
 
327
- ```python
328
- from pysec2pri import crosswalk
329
- crosswalk("TP53", frm="symbol", to="hgnc_id") # {'TP53': 'HGNC:11998'}
330
- crosswalk("ENSG00000141510", frm="ensembl", to="symbol") # via HGNC's own crosswalk
331
- ```
349
+ Two columns of it are already a crosswalk: pick `Ensembl ID` and `HGNC ID` and
350
+ you can map one to the other with
351
+ `--xref ensembl --xref-source hgnc_custom --xref-on ensembl`
352
+
353
+ #### Bringing your own table
332
354
 
333
- ### Consolidating mapping dates across releases
355
+ Pass any table with `--xref-file`. It needs three columns: `subject_id` (what
356
+ you key on), `object_id` (this source's identifier), and `object_label` (its
357
+ label):
334
358
 
335
- A single release snapshot only presents each mapping's _last-seen_ date.
336
- `consolidate` builds a first-seen-date index and writes it back out as a real
337
- SSSOM mapping set whose `mapping_date` is each mapping's true first appearance.
338
- The path is chosen automatically: datasources with a versioned archive are
339
- walked release by release, while those without one (NCBI, VGNC) are covered in a
340
- single fast pass over the current release:
359
+ ```text
360
+ subject_id object_id object_label
361
+ ENSG00000121410 HGNC:5 A1BG
362
+ ENSG00000175899 HGNC:7 A2M
363
+ ```
341
364
 
342
365
  ```bash
343
- pysec2pri chebi consolidate # walks ~250 ChEBI releases (versioned archive); slow, run as a one-off
344
- pysec2pri ncbi consolidate # single fast pass (no versioned archive)
366
+ pysec2pri update-ids genes.tsv hgnc --at gene_id --xref ensembl \
367
+ --xref-file my_crosswalk.tsv
345
368
  ```
346
369
 
347
- Supported for `chebi`, `ensembl`, `hgnc`, `ncbi`, `uniprot`, and `vgnc`. Ensembl
348
- additionally supports `label-history`, which derives previous-symbol ->
349
- current-symbol transitions by diffing each release's labels per stable ID
350
- (Ensembl has no previous-symbol table of its own):
370
+ In Python you can point at the columns instead of renaming them, so a file like
371
+ HGNC download works like:
351
372
 
352
- ```bash
353
- pysec2pri ensembl label-history --species 9606
373
+ ```python
374
+ from pysec2pri import generate_ids, load_xref_mapping, update_ids
375
+
376
+ xref = load_xref_mapping(
377
+ "hgnc_custom.tsv",
378
+ subject_col="Ensembl ID(supplied by Ensembl)",
379
+ object_col="HGNC ID",
380
+ object_label_col="Approved symbol",
381
+ )
382
+ update_ids(df, generate_ids("hgnc"), at="gene_id", xref="ensembl", xref_mapping=xref)
354
383
  ```
355
384
 
356
385
  ### Diffing mapping sets