baseqdb 0.9.0__tar.gz → 0.10.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.
- {baseqdb-0.9.0 → baseqdb-0.10.0}/PKG-INFO +3 -1
- {baseqdb-0.9.0 → baseqdb-0.10.0}/README.md +2 -0
- {baseqdb-0.9.0 → baseqdb-0.10.0}/pyproject.toml +1 -1
- {baseqdb-0.9.0 → baseqdb-0.10.0}/src/baseqdb/__init__.py +3 -1
- {baseqdb-0.9.0 → baseqdb-0.10.0}/src/baseqdb/_client.py +24 -1
- baseqdb-0.10.0/src/baseqdb/_genes.py +72 -0
- {baseqdb-0.9.0 → baseqdb-0.10.0}/src/baseqdb/guide.md +6 -0
- {baseqdb-0.9.0 → baseqdb-0.10.0}/LICENSE +0 -0
- {baseqdb-0.9.0 → baseqdb-0.10.0}/src/baseqdb/_cli.py +0 -0
- {baseqdb-0.9.0 → baseqdb-0.10.0}/src/baseqdb/_records.py +0 -0
- {baseqdb-0.9.0 → baseqdb-0.10.0}/src/baseqdb/_track.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: baseqdb
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.10.0
|
|
4
4
|
Summary: Your baSeq datasets, analyses and results from Python, with lamindb's names
|
|
5
5
|
Project-URL: Homepage, https://baseq.bio
|
|
6
6
|
Project-URL: Documentation, https://baseq.bio/docs/python-and-r.html
|
|
@@ -34,6 +34,8 @@ bq.Analysis.filter(kind="bulk").to_dataframe() # the analyses you can open, new
|
|
|
34
34
|
ds = bq.Dataset.get("kTIqMTTm89P2gbNU0000") # one version, by its uid
|
|
35
35
|
folder = ds.cache() # its files, downloaded once
|
|
36
36
|
sheet = ds.sample_sheet # a DataFrame
|
|
37
|
+
bq.Gene.standardize(["ENSG00000109906.15"]) # ['ZBTB16']: genes named as baSeq names them
|
|
38
|
+
genes = bq.Gene.df("human") # every gene: id, symbol, biotype
|
|
37
39
|
|
|
38
40
|
analysis = ds.analyses[0]
|
|
39
41
|
analysis.steps.to_dataframe() # each step, its settings, when it ran
|
|
@@ -13,6 +13,8 @@ bq.Analysis.filter(kind="bulk").to_dataframe() # the analyses you can open, new
|
|
|
13
13
|
ds = bq.Dataset.get("kTIqMTTm89P2gbNU0000") # one version, by its uid
|
|
14
14
|
folder = ds.cache() # its files, downloaded once
|
|
15
15
|
sheet = ds.sample_sheet # a DataFrame
|
|
16
|
+
bq.Gene.standardize(["ENSG00000109906.15"]) # ['ZBTB16']: genes named as baSeq names them
|
|
17
|
+
genes = bq.Gene.df("human") # every gene: id, symbol, biotype
|
|
16
18
|
|
|
17
19
|
analysis = ds.analyses[0]
|
|
18
20
|
analysis.steps.to_dataframe() # each step, its settings, when it ran
|
|
@@ -8,6 +8,7 @@ Python, with lamindb's names; and, with a key that may add data, new datasets an
|
|
|
8
8
|
ds = bq.Dataset.get("kTIqMTTm89P2gbNU0000") # one version, by its uid
|
|
9
9
|
folder = ds.cache() # its files, downloaded once
|
|
10
10
|
bq.Dataset(["counts.tsv"], name=ds.name, kind="bulk").save() # its next version
|
|
11
|
+
bq.Gene.standardize(["ENSG00000109906.15", "fkbp5"]) # ['ZBTB16', 'FKBP5'], as baSeq names them
|
|
11
12
|
|
|
12
13
|
bq.track() # this script as one run: what it reads, adds and saves
|
|
13
14
|
bq.Record("dge.csv").save() # a file of the run
|
|
@@ -18,9 +19,10 @@ with it, what it found said at the end. `baseqdb init` writes that guide into a
|
|
|
18
19
|
Docs: https://baseq.bio/docs/python-and-r.html
|
|
19
20
|
"""
|
|
20
21
|
from baseqdb._client import VERSION as __version__, BaseqError, login, me
|
|
22
|
+
from baseqdb._genes import Gene
|
|
21
23
|
from baseqdb._records import (Analysis, Dataset, File, Files, Finding, GeneHit, Record, Results, Run, Step, Table,
|
|
22
24
|
Tables)
|
|
23
25
|
from baseqdb._track import finish, track
|
|
24
26
|
|
|
25
27
|
__all__ = ["login", "me", "BaseqError", "Dataset", "File", "Files", "Analysis", "Step", "Finding", "GeneHit", "Table",
|
|
26
|
-
"Tables", "Record", "Run", "Results", "track", "finish", "__version__"]
|
|
28
|
+
"Tables", "Record", "Run", "Results", "Gene", "track", "finish", "__version__"]
|
|
@@ -9,7 +9,7 @@ from pathlib import Path
|
|
|
9
9
|
import httpx
|
|
10
10
|
|
|
11
11
|
DEFAULT_URL = "https://baseq.bio"
|
|
12
|
-
VERSION = "0.
|
|
12
|
+
VERSION = "0.10.0"
|
|
13
13
|
RUN_HEADER = "X-Baseq-Run"
|
|
14
14
|
RUN_FILE = Path(".baseqdb") / "run" # an agent's task, begun with `baseqdb track`, for every command in the folder
|
|
15
15
|
_state: dict = {"client": None, "base": None, "run": None}
|
|
@@ -231,6 +231,29 @@ def download(path: str, target: Path, sha256: str | None = None, read: dict | No
|
|
|
231
231
|
return target
|
|
232
232
|
|
|
233
233
|
|
|
234
|
+
def download_current(path: str, target: Path, **params) -> Path:
|
|
235
|
+
"""``path`` saved as ``target``, and again only when baSeq has changed it: the copy's ETag goes with the request,
|
|
236
|
+
and the copy stays when the answer is 304 (Not Modified)."""
|
|
237
|
+
tag_file = target.with_name(target.name + ".etag")
|
|
238
|
+
headers = {}
|
|
239
|
+
if target.is_file() and tag_file.is_file():
|
|
240
|
+
headers["If-None-Match"] = tag_file.read_text(encoding="utf-8").strip()
|
|
241
|
+
http, base = _http()
|
|
242
|
+
response = http.get(base + path, params={k: v for k, v in params.items() if v is not None}, headers=headers)
|
|
243
|
+
if response.status_code == 304:
|
|
244
|
+
return target
|
|
245
|
+
_raise(response)
|
|
246
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
247
|
+
part = target.with_name(target.name + ".part")
|
|
248
|
+
part.write_bytes(response.content)
|
|
249
|
+
os.replace(part, target)
|
|
250
|
+
if response.headers.get("ETag"):
|
|
251
|
+
tag_file.write_text(response.headers["ETag"], encoding="utf-8")
|
|
252
|
+
else:
|
|
253
|
+
tag_file.unlink(missing_ok=True)
|
|
254
|
+
return target
|
|
255
|
+
|
|
256
|
+
|
|
234
257
|
def cache_dir() -> Path:
|
|
235
258
|
"""Where downloads are kept: BASEQDB_CACHE, or ~/.cache/baseqdb."""
|
|
236
259
|
return Path(os.environ.get("BASEQDB_CACHE") or Path.home() / ".cache" / "baseqdb")
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""Gene names, with bionty's names: ``Gene.standardize`` gives a table's genes their symbols (or Ensembl ids) as baSeq
|
|
2
|
+
names them, and ``Gene.df`` is every gene of one organism, kept on this computer. Both from the gene reference baSeq
|
|
3
|
+
uses: Ensembl release 114, for human, mouse, cattle, zebrafish and salmon."""
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import re
|
|
7
|
+
|
|
8
|
+
from baseqdb import _client as _c
|
|
9
|
+
from baseqdb._records import _pandas
|
|
10
|
+
|
|
11
|
+
PART = 50_000 # names a request, the most the API takes
|
|
12
|
+
_LONGEST = 120 # no gene's name is longer: a longer one is kept as written without asking
|
|
13
|
+
RETURN_FIELDS = ("symbol", "ensembl_gene_id")
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _text(value) -> str | None:
|
|
17
|
+
"""A name as text, or None for a missing value (None, NaN, pandas' NA)."""
|
|
18
|
+
if value is None:
|
|
19
|
+
return None
|
|
20
|
+
try:
|
|
21
|
+
if value != value:
|
|
22
|
+
return None
|
|
23
|
+
except TypeError:
|
|
24
|
+
return None
|
|
25
|
+
return str(value)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class Gene:
|
|
29
|
+
"""Genes as baSeq's gene reference has them (Ensembl 114: human, mouse, cattle, zebrafish and salmon)."""
|
|
30
|
+
|
|
31
|
+
@staticmethod
|
|
32
|
+
def standardize(names, return_field: str = "symbol", return_mapper: bool = False, organism: str | None = None):
|
|
33
|
+
"""Each name as baSeq names the gene, as bionty's ``Gene.standardize``: its symbol, or with
|
|
34
|
+
``return_field="ensembl_gene_id"`` its Ensembl id. A name may be an Ensembl id with or without its version
|
|
35
|
+
(ENSG00000109906.15 is ZBTB16), a symbol in any case, or another of its names; a symbol several genes share
|
|
36
|
+
means the one on the primary assembly. A name the reference does not have, or one that several genes share,
|
|
37
|
+
is kept as written. The names tell their organism, as an analysis's genes do; ``organism`` ("human",
|
|
38
|
+
"mouse", "cattle", "zebrafish" or "salmon") says it when they cannot.
|
|
39
|
+
|
|
40
|
+
A list in the order of ``names`` (a list, a pandas Index or Series; one name gives one name), or with
|
|
41
|
+
``return_mapper=True`` a dict of the names that changed."""
|
|
42
|
+
if return_field not in RETURN_FIELDS:
|
|
43
|
+
raise ValueError(f"return_field is {' or '.join(map(repr, RETURN_FIELDS))}, not {return_field!r}")
|
|
44
|
+
single = isinstance(names, str)
|
|
45
|
+
given = [names] if single else list(names)
|
|
46
|
+
texts = [_text(v) for v in given]
|
|
47
|
+
asked = [t for t in dict.fromkeys(texts) if t and len(t) <= _LONGEST]
|
|
48
|
+
named: dict[str, str] = {}
|
|
49
|
+
for i in range(0, len(asked), PART):
|
|
50
|
+
part = asked[i:i + PART]
|
|
51
|
+
answer = _c.post("/genes/standardize", json={"names": part, "organism": organism,
|
|
52
|
+
"return_field": return_field})
|
|
53
|
+
organism = organism or answer["organism"] or None # the later parts as the first was told
|
|
54
|
+
named.update(zip(part, answer["names"]))
|
|
55
|
+
changed = [named[t] if t in named and named[t] != t else None for t in texts]
|
|
56
|
+
if return_mapper:
|
|
57
|
+
return {v: new for v, new in zip(given, changed) if new is not None}
|
|
58
|
+
out = [v if new is None else new for v, new in zip(given, changed)]
|
|
59
|
+
return out[0] if single else out
|
|
60
|
+
|
|
61
|
+
@staticmethod
|
|
62
|
+
def df(organism: str):
|
|
63
|
+
"""Every gene of one organism baSeq's reference has, as a pandas DataFrame, as bionty's
|
|
64
|
+
``Gene.public(organism).df()``: ensembl_gene_id, symbol (missing when Ensembl gives none), biotype, and
|
|
65
|
+
primary_assembly (False for a copy on a haplotype or a patch). Downloaded once and kept in baseqdb's cache
|
|
66
|
+
(genes/), for as long as the reference stays the same."""
|
|
67
|
+
pd = _pandas()
|
|
68
|
+
key = re.sub(r"[^a-z0-9]+", "_", str(organism).strip().lower()).strip("_") or "organism"
|
|
69
|
+
path = _c.download_current("/genes", _c.cache_dir() / "genes" / f"{key}.tsv", organism=organism)
|
|
70
|
+
table = pd.read_csv(path, sep="\t", dtype=str, keep_default_na=False, na_values=[""])
|
|
71
|
+
table["primary_assembly"] = table["primary_assembly"] == "1"
|
|
72
|
+
return table
|
|
@@ -60,6 +60,12 @@ data this way before asking the person which dataset they mean. In R, the same n
|
|
|
60
60
|
`Dataset$get(uid)$cache()`, `ds$sample_sheet`, `Dataset$filter(condition = "dexamethasone")`, `Dataset$search("dex")`,
|
|
61
61
|
`Analysis$filter()`.
|
|
62
62
|
|
|
63
|
+
## Gene names
|
|
64
|
+
|
|
65
|
+
Count tables often name genes by their Ensembl id. `bq.Gene.standardize(names)` gives each its symbol as baSeq names
|
|
66
|
+
it (with `return_field="ensembl_gene_id"`, the other way round), and `bq.Gene.df("human")` is every gene of an
|
|
67
|
+
organism; in R, `Gene$standardize()` and `Gene$df()`. A finding names its genes by symbol, as `--gene` does.
|
|
68
|
+
|
|
63
69
|
## Adding
|
|
64
70
|
|
|
65
71
|
A new version of a dataset: the name and kind of one the person has, with a key that may add data.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|