pysec2pri 0.2.3__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.3 → pysec2pri-0.3.0}/PKG-INFO +131 -93
  2. {pysec2pri-0.2.3 → pysec2pri-0.3.0}/README.md +129 -91
  3. {pysec2pri-0.2.3 → 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.3 → pysec2pri-0.3.0}/src/pysec2pri/cli.py +319 -506
  7. {pysec2pri-0.2.3 → pysec2pri-0.3.0}/src/pysec2pri/config/chebi.yaml +25 -15
  8. {pysec2pri-0.2.3 → pysec2pri-0.3.0}/src/pysec2pri/config/ensembl.yaml +37 -12
  9. {pysec2pri-0.2.3 → pysec2pri-0.3.0}/src/pysec2pri/config/hgnc.yaml +21 -13
  10. {pysec2pri-0.2.3 → pysec2pri-0.3.0}/src/pysec2pri/config/hmdb_metabolites.yaml +12 -8
  11. {pysec2pri-0.2.3 → pysec2pri-0.3.0}/src/pysec2pri/config/hmdb_proteins.yaml +12 -8
  12. {pysec2pri-0.2.3 → pysec2pri-0.3.0}/src/pysec2pri/config/ncbi.yaml +17 -7
  13. {pysec2pri-0.2.3 → pysec2pri-0.3.0}/src/pysec2pri/config/uniprot.yaml +8 -3
  14. {pysec2pri-0.2.3 → pysec2pri-0.3.0}/src/pysec2pri/config/vgnc.yaml +18 -13
  15. {pysec2pri-0.2.3 → pysec2pri-0.3.0}/src/pysec2pri/config/wikidata.yaml +15 -14
  16. {pysec2pri-0.2.3 → 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.3 → pysec2pri-0.3.0}/src/pysec2pri/download.py +2 -2
  19. {pysec2pri-0.2.3 → pysec2pri-0.3.0}/src/pysec2pri/downloads/chebi.py +9 -4
  20. {pysec2pri-0.2.3 → pysec2pri-0.3.0}/src/pysec2pri/downloads/ensembl.py +10 -30
  21. {pysec2pri-0.2.3 → pysec2pri-0.3.0}/src/pysec2pri/downloads/hmdb.py +2 -8
  22. {pysec2pri-0.2.3 → 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.3 → pysec2pri-0.3.0}/src/pysec2pri/parsers/base.py +35 -57
  27. {pysec2pri-0.2.3 → pysec2pri-0.3.0}/src/pysec2pri/parsers/chebi.py +32 -54
  28. {pysec2pri-0.2.3 → 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.3 → pysec2pri-0.3.0}/src/pysec2pri/parsers/hmdb.py +18 -39
  31. {pysec2pri-0.2.3 → pysec2pri-0.3.0}/src/pysec2pri/parsers/ncbi.py +20 -59
  32. {pysec2pri-0.2.3 → pysec2pri-0.3.0}/src/pysec2pri/parsers/uniprot.py +47 -56
  33. {pysec2pri-0.2.3 → pysec2pri-0.3.0}/src/pysec2pri/parsers/vgnc.py +50 -270
  34. {pysec2pri-0.2.3 → pysec2pri-0.3.0}/src/pysec2pri/parsers/wikidata.py +33 -167
  35. {pysec2pri-0.2.3 → 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.3 → pysec2pri-0.3.0}/src/pysec2pri/version.py +1 -1
  38. pysec2pri-0.2.3/src/pysec2pri/__init__.py +0 -119
  39. pysec2pri-0.2.3/src/pysec2pri/api.py +0 -2119
  40. pysec2pri-0.2.3/src/pysec2pri/constants.py +0 -41
  41. pysec2pri-0.2.3/src/pysec2pri/logging.py +0 -65
  42. pysec2pri-0.2.3/src/pysec2pri/parsers/__init__.py +0 -42
  43. pysec2pri-0.2.3/src/pysec2pri/parsers/hgnc.py +0 -481
  44. pysec2pri-0.2.3/src/pysec2pri/update_ids.py +0 -1138
  45. pysec2pri-0.2.3/src/pysec2pri/xref.py +0 -7
  46. {pysec2pri-0.2.3 → pysec2pri-0.3.0}/LICENSE +0 -0
  47. {pysec2pri-0.2.3 → pysec2pri-0.3.0}/src/pysec2pri/__main__.py +0 -0
  48. {pysec2pri-0.2.3 → pysec2pri-0.3.0}/src/pysec2pri/config/__init__.py +0 -0
  49. {pysec2pri-0.2.3 → pysec2pri-0.3.0}/src/pysec2pri/downloads/__init__.py +0 -0
  50. {pysec2pri-0.2.3 → pysec2pri-0.3.0}/src/pysec2pri/downloads/hgnc.py +0 -0
  51. {pysec2pri-0.2.3 → pysec2pri-0.3.0}/src/pysec2pri/downloads/ncbi.py +0 -0
  52. {pysec2pri-0.2.3 → pysec2pri-0.3.0}/src/pysec2pri/downloads/uniprot.py +0 -0
  53. {pysec2pri-0.2.3 → pysec2pri-0.3.0}/src/pysec2pri/py.typed +0 -0
  54. {pysec2pri-0.2.3 → pysec2pri-0.3.0}/src/pysec2pri/queries/chemical_redirects.rq +0 -0
  55. {pysec2pri-0.2.3 → pysec2pri-0.3.0}/src/pysec2pri/queries/chemical_redirects_test.rq +0 -0
  56. {pysec2pri-0.2.3 → pysec2pri-0.3.0}/src/pysec2pri/queries/gene_redirects.rq +0 -0
  57. {pysec2pri-0.2.3 → pysec2pri-0.3.0}/src/pysec2pri/queries/gene_redirects_test.rq +0 -0
  58. {pysec2pri-0.2.3 → pysec2pri-0.3.0}/src/pysec2pri/queries/protein_redirects.rq +0 -0
  59. {pysec2pri-0.2.3 → 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.3
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.1
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,6 +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
 
181
+ In Python, `load_mapping` reads a written SSSOM file back in, so you can
182
+ generate once and reuse the file:
183
+
184
+ ```python
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")
188
+ ```
189
+
190
+ Use `load_label_mapping` for a label mapping set.
191
+
185
192
  Ambiguous mappings (where a deprecated ID or label serves as a recommended for
186
193
  another entity) are not resolved, but flagged for users to solve them manually.
187
194
  If the input file has a column of known aliases or synonyms for each row, pass
@@ -200,56 +207,80 @@ pysec2pri ambiguous hgnc-labels
200
207
 
201
208
  ## Mapping types
202
209
 
203
- ### Deprecations (IDs)
210
+ Every row is one secondary (`subject`) and one primary (`object`). Which
211
+ predicate joins them says what happened to it.
204
212
 
205
- A deprecated ID is mapped to its replacement via `IAO:0100001` ("term replaced
206
- by"). Each row is 1-to-1: one secondary `subject_id` : one primary `object_id`.
213
+ ### IDs
214
+
215
+ A retired ID either has a replacement or does not:
207
216
 
208
217
  ```mermaid
209
218
  flowchart LR
210
- D["subject_id (deprecated)"]
211
- P["object_id (primary)"]
212
- 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
213
224
  ```
214
225
 
215
- **Ambiguity** happens when the same ID appears as both a `subject_id`
216
- (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:
217
233
 
218
234
  ```mermaid
219
235
  flowchart LR
220
- A["A (primary of C and secondary of B)"] -->|term replaced by| B["B (primary)"]
221
- 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
222
241
  ```
223
242
 
224
- ### 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.
225
246
 
226
- The same 1-to-1 pattern applies to label (or symbol) mappings: a previous or
227
- alias label (`subject_label`) maps to the current label (`object_label`) of the
228
- same entity via `IAO:0100001`.
247
+ ### Looking further back with `--consolidate`
229
248
 
230
- **Ambiguity** appears when the same label is both a `subject_label` (previous
231
- name, secondary) and an `object_label` (current name, primary) across different
232
- 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:
233
252
 
234
- ### Aliases / synonyms
253
+ ```bash
254
+ pysec2pri hgnc ids --consolidate -o hgnc.sssom.tsv
255
+ ```
256
+
257
+ ```python
258
+ from pysec2pri import generate_ids, supports_consolidate
259
+ supports_consolidate("hgnc", "ids")
260
+ generate_ids("hgnc", consolidate=True)
261
+ ```
235
262
 
236
- Alias mappings use `oboInOwl:hasExactSynonym`. The alias is the `subject_label`
237
- and the primary name is the `object_label`/`object_id`.
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.
238
267
 
239
268
  ```mermaid
240
269
  flowchart LR
241
- A["subject_label (alias / synonym)"]
242
- P["object_label/object_id (primary)"]
243
- 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)"]
244
272
  ```
245
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
+
246
277
  ### Resolving ambiguity with alias/synonym hints
247
278
 
248
279
  When a name is ambiguous, alias mappings are used as evidence. For each
249
280
  candidate interpretation the resolver checks whether any user-supplied hint
250
281
  matches a known alias of that candidate's primary entity. A hit on the secondary
251
282
  candidate's target confirms the name is being used as a previous name; a hit on
252
- the primary candidate's own aliases confirms it is already current.
283
+ the primary candidate's aliases confirms it is already current.
253
284
 
254
285
  ```mermaid
255
286
  flowchart TD
@@ -270,23 +301,21 @@ flowchart TD
270
301
 
271
302
  Alias hints are one kind of _context_: a per-row piece of independent evidence
272
303
  that helps decide which entity an ambiguous name actually means. `update_ids`
273
- and `update_labels` support three kinds, via `pysec2pri.context.ContextSpec`:
304
+ and `update_labels` support three kinds, via `ContextSpec`:
274
305
 
275
306
  - **`label`** -- an alias/synonym string (the `synonyms=`/`--synonyms` shown
276
307
  above).
277
308
  - **`id`** -- a related/foreign identifier string, matched the same way.
278
309
  - **`xref`** -- a cross-reference token (e.g. an Ensembl ID) resolved through an
279
- independent crosswalk table (`pysec2pri.context.XrefMapping`), rather than the
280
- mapping set's own alias index.
310
+ independent crosswalk table (`XrefMapping`).
281
311
 
282
312
  All three only ever touch cells already flagged ambiguous, and every attempt can
283
313
  be written to an auditable decision log:
284
314
 
285
315
  ```python
286
- from pysec2pri import generate_hgnc_labels, update_labels
287
- from pysec2pri.context import load_xref_mapping
316
+ from pysec2pri import generate_labels, load_xref_mapping, update_labels
288
317
 
289
- label_ms = generate_hgnc_labels()
318
+ label_ms = generate_labels("hgnc")
290
319
  ensembl_to_hgnc = load_xref_mapping("ensembl_to_hgnc.tsv") # subject_id/object_id/object_label
291
320
 
292
321
  resolved = update_labels(
@@ -305,43 +334,52 @@ pysec2pri update-labels genes.tsv hgnc --at gene_name \
305
334
  --report decisions.tsv
306
335
  ```
307
336
 
308
- ### `crosswalk`: a direct identifier lookup helper
337
+ ### Crosswalk tables
309
338
 
310
- For the common case of "map this one column of identifiers to an HGNC ID (or
311
- 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:
312
341
 
313
- ```bash
314
- pysec2pri crosswalk --value TP53 --from symbol --to hgnc_id
315
- pysec2pri crosswalk genes.tsv --from ensembl --to hgnc_id --at ensembl_id -o out.tsv
316
- ```
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 |
317
348
 
318
- ```python
319
- from pysec2pri import crosswalk
320
- crosswalk("TP53", frm="symbol", to="hgnc_id") # {'TP53': 'HGNC:11998'}
321
- crosswalk("ENSG00000141510", frm="ensembl", to="symbol") # via HGNC's own crosswalk
322
- ```
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`
323
352
 
324
- ### Consolidating mapping dates across releases
353
+ #### Bringing your own table
325
354
 
326
- A single release snapshot only presents each mapping's _last-seen_ date.
327
- `consolidate` builds a first-seen-date index by walking a datasource's
328
- historical archive (or, where the parser already exposes a real per-row date
329
- such as HGNC's `date_symbol_changed`, a single fast pass) and writes it back out
330
- as a real SSSOM mapping set whose `mapping_date` is each mapping's true first
331
- appearance:
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):
358
+
359
+ ```text
360
+ subject_id object_id object_label
361
+ ENSG00000121410 HGNC:5 A1BG
362
+ ENSG00000175899 HGNC:7 A2M
363
+ ```
332
364
 
333
365
  ```bash
334
- pysec2pri chebi consolidate --mode release # walks ~250 ChEBI releases; slow, run as a one-off
335
- pysec2pri hgnc consolidate --mode date # fast: uses HGNC's own per-row date
366
+ pysec2pri update-ids genes.tsv hgnc --at gene_id --xref ensembl \
367
+ --xref-file my_crosswalk.tsv
336
368
  ```
337
369
 
338
- Supported for `chebi`, `ensembl`, `hgnc`, `ncbi`, `uniprot`, and `vgnc`. Ensembl
339
- additionally supports `label-history`, which derives previous-symbol ->
340
- current-symbol transitions by diffing each release's labels per stable ID
341
- (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:
342
372
 
343
- ```bash
344
- 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)
345
383
  ```
346
384
 
347
385
  ### Diffing mapping sets