cellucid 0.0.1a0__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.
@@ -0,0 +1,105 @@
1
+ Metadata-Version: 2.4
2
+ Name: cellucid
3
+ Version: 0.0.1a0
4
+ Summary: Utilities to export data for the 3D UMAP WebGL viewer.
5
+ Author: cellucid contributors
6
+ License: Proprietary
7
+ Project-URL: Repository, https://github.com/theislab/cellucid-data
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Programming Language :: Python :: 3 :: Only
10
+ Classifier: License :: Other/Proprietary License
11
+ Classifier: Intended Audience :: Science/Research
12
+ Classifier: Topic :: Scientific/Engineering :: Visualization
13
+ Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
14
+ Requires-Python: >=3.10
15
+ Description-Content-Type: text/markdown
16
+ Requires-Dist: numpy>=1.22
17
+ Requires-Dist: pandas>=1.5
18
+ Requires-Dist: scipy>=1.9
19
+ Requires-Dist: tqdm>=4.66
20
+ Provides-Extra: analysis
21
+ Requires-Dist: anndata>=0.10; extra == "analysis"
22
+ Requires-Dist: scanpy>=1.10; extra == "analysis"
23
+ Provides-Extra: docs
24
+ Requires-Dist: sphinx>=7.2; extra == "docs"
25
+ Requires-Dist: myst-parser>=2.0; extra == "docs"
26
+ Requires-Dist: sphinx-autodoc-typehints>=1.25; extra == "docs"
27
+ Requires-Dist: sphinx_rtd_theme>=1.3; extra == "docs"
28
+
29
+ <p>
30
+ <img src="cellucid-logo.svg" alt="Cellucid logo" width="320">
31
+ </p>
32
+
33
+ [![PyPI version](https://img.shields.io/pypi/v/cellucid.svg)](https://pypi.org/project/cellucid/)
34
+ [![Python versions](https://img.shields.io/pypi/pyversions/cellucid.svg)](https://pypi.org/project/cellucid/)
35
+ [![Documentation Status](https://readthedocs.org/projects/cellucid/badge/?version=latest)](https://cellucid.readthedocs.io/en/latest/)
36
+
37
+ # cellucid (Python)
38
+
39
+ Python utilities for exporting AnnData/numpy payloads into the manifest + binary format consumed by the Cellucid WebGL viewer. Everything here is independent of the website; generate data wherever you like, then copy the resulting files into the viewer’s `assets/exports/` directory when you host the site.
40
+
41
+ ## Layout (repo: theislab/cellucid-data)
42
+ - `src/cellucid/prepare_data.py`: `export_data_for_web` writes points/obs/var/connectivity binaries into an `exports/` directory (configurable).
43
+ - `src/cellucid/anndata_variations.py`: UMAP sweep helper for experimentation (uses Scanpy).
44
+ - `src/cellucid/repo_to_text.py`: utility to dump repository contents to a single text file.
45
+ - `notebooks/`: exploratory notebook that demonstrates exporting viewer assets.
46
+ - `data/`: example/raw inputs (h5ad, pickles) and experiment outputs live here; this folder stays untracked by default.
47
+
48
+ ## Installation
49
+ Requires Python 3.10+.
50
+
51
+ From the repo root:
52
+
53
+ ```bash
54
+ pip install -e python
55
+ # or with optional extras
56
+ pip install -e python[analysis,docs]
57
+ ```
58
+
59
+ The package name is `cellucid` and imports under `cellucid`.
60
+
61
+ ## Usage
62
+ ```python
63
+ import scanpy as sc
64
+ from cellucid.prepare_data import export_data_for_web
65
+
66
+ adata = sc.read_h5ad("path/to/adata.h5ad")
67
+ export_data_for_web(
68
+ X_umap=adata.obsm["X_umap"],
69
+ latent_space=adata.X,
70
+ obs=adata.obs,
71
+ var=adata.var,
72
+ gene_expression=adata.X,
73
+ connectivities=adata.obsp.get("connectivities"),
74
+ out_dir="exports", # default; change as needed
75
+ var_quantization=8,
76
+ obs_continuous_quantization=8,
77
+ compression=6,
78
+ )
79
+ ```
80
+
81
+ The exporter defaults to `exports/` under your current working directory. After generation, copy or sync that folder into the website’s `assets/exports/` directory so the viewer can load the files.
82
+
83
+ To run the UMAP sweep helper:
84
+
85
+ ```bash
86
+ python -m cellucid.anndata_variations
87
+ ```
88
+
89
+ Ensure `python/data/raw` contains the expected h5ad/pickle files or override the module constants before calling. Paths in the notebook and sweep helper assume the `data/` directory inside this `python/` project; point them to your own locations as needed.
90
+
91
+ ## Companion web viewer (theislab/cellucid)
92
+ The static viewer lives in a separate repository (`theislab/cellucid`). After running `export_data_for_web`, copy or sync the resulting `exports/` directory into that repo’s `assets/exports/` folder before hosting. The manifest/binary schema is shared; no build step is required on the web side.
93
+
94
+ ## Documentation
95
+ - Build locally: `pip install -e python[docs] && sphinx-build -b html docs docs/_build/html`
96
+ - Read the Docs: `python/.readthedocs.yaml` targets `docs/conf.py` with the `docs` extra.
97
+
98
+ ## Publishing
99
+ Install packaging tools if needed: `pip install build twine`
100
+
101
+ 1. From `python/`, bump the version in `pyproject.toml`.
102
+ 2. Build: `python -m build`
103
+ 3. Upload to PyPI (or TestPyPI) with `twine upload dist/*`.
104
+
105
+ Update the `license`, `authors`, and `Repository` URL in `pyproject.toml` before publishing if needed.
@@ -0,0 +1,77 @@
1
+ <p>
2
+ <img src="cellucid-logo.svg" alt="Cellucid logo" width="320">
3
+ </p>
4
+
5
+ [![PyPI version](https://img.shields.io/pypi/v/cellucid.svg)](https://pypi.org/project/cellucid/)
6
+ [![Python versions](https://img.shields.io/pypi/pyversions/cellucid.svg)](https://pypi.org/project/cellucid/)
7
+ [![Documentation Status](https://readthedocs.org/projects/cellucid/badge/?version=latest)](https://cellucid.readthedocs.io/en/latest/)
8
+
9
+ # cellucid (Python)
10
+
11
+ Python utilities for exporting AnnData/numpy payloads into the manifest + binary format consumed by the Cellucid WebGL viewer. Everything here is independent of the website; generate data wherever you like, then copy the resulting files into the viewer’s `assets/exports/` directory when you host the site.
12
+
13
+ ## Layout (repo: theislab/cellucid-data)
14
+ - `src/cellucid/prepare_data.py`: `export_data_for_web` writes points/obs/var/connectivity binaries into an `exports/` directory (configurable).
15
+ - `src/cellucid/anndata_variations.py`: UMAP sweep helper for experimentation (uses Scanpy).
16
+ - `src/cellucid/repo_to_text.py`: utility to dump repository contents to a single text file.
17
+ - `notebooks/`: exploratory notebook that demonstrates exporting viewer assets.
18
+ - `data/`: example/raw inputs (h5ad, pickles) and experiment outputs live here; this folder stays untracked by default.
19
+
20
+ ## Installation
21
+ Requires Python 3.10+.
22
+
23
+ From the repo root:
24
+
25
+ ```bash
26
+ pip install -e python
27
+ # or with optional extras
28
+ pip install -e python[analysis,docs]
29
+ ```
30
+
31
+ The package name is `cellucid` and imports under `cellucid`.
32
+
33
+ ## Usage
34
+ ```python
35
+ import scanpy as sc
36
+ from cellucid.prepare_data import export_data_for_web
37
+
38
+ adata = sc.read_h5ad("path/to/adata.h5ad")
39
+ export_data_for_web(
40
+ X_umap=adata.obsm["X_umap"],
41
+ latent_space=adata.X,
42
+ obs=adata.obs,
43
+ var=adata.var,
44
+ gene_expression=adata.X,
45
+ connectivities=adata.obsp.get("connectivities"),
46
+ out_dir="exports", # default; change as needed
47
+ var_quantization=8,
48
+ obs_continuous_quantization=8,
49
+ compression=6,
50
+ )
51
+ ```
52
+
53
+ The exporter defaults to `exports/` under your current working directory. After generation, copy or sync that folder into the website’s `assets/exports/` directory so the viewer can load the files.
54
+
55
+ To run the UMAP sweep helper:
56
+
57
+ ```bash
58
+ python -m cellucid.anndata_variations
59
+ ```
60
+
61
+ Ensure `python/data/raw` contains the expected h5ad/pickle files or override the module constants before calling. Paths in the notebook and sweep helper assume the `data/` directory inside this `python/` project; point them to your own locations as needed.
62
+
63
+ ## Companion web viewer (theislab/cellucid)
64
+ The static viewer lives in a separate repository (`theislab/cellucid`). After running `export_data_for_web`, copy or sync the resulting `exports/` directory into that repo’s `assets/exports/` folder before hosting. The manifest/binary schema is shared; no build step is required on the web side.
65
+
66
+ ## Documentation
67
+ - Build locally: `pip install -e python[docs] && sphinx-build -b html docs docs/_build/html`
68
+ - Read the Docs: `python/.readthedocs.yaml` targets `docs/conf.py` with the `docs` extra.
69
+
70
+ ## Publishing
71
+ Install packaging tools if needed: `pip install build twine`
72
+
73
+ 1. From `python/`, bump the version in `pyproject.toml`.
74
+ 2. Build: `python -m build`
75
+ 3. Upload to PyPI (or TestPyPI) with `twine upload dist/*`.
76
+
77
+ Update the `license`, `authors`, and `Repository` URL in `pyproject.toml` before publishing if needed.
@@ -0,0 +1,44 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "cellucid"
7
+ version = "0.0.1a0"
8
+ description = "Utilities to export data for the 3D UMAP WebGL viewer."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = { text = "Proprietary" }
12
+ authors = [{ name = "cellucid contributors" }]
13
+ dependencies = [
14
+ "numpy>=1.22",
15
+ "pandas>=1.5",
16
+ "scipy>=1.9",
17
+ "tqdm>=4.66",
18
+ ]
19
+ classifiers = [
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3 :: Only",
22
+ "License :: Other/Proprietary License",
23
+ "Intended Audience :: Science/Research",
24
+ "Topic :: Scientific/Engineering :: Visualization",
25
+ "Topic :: Scientific/Engineering :: Bio-Informatics",
26
+ ]
27
+
28
+ [project.optional-dependencies]
29
+ analysis = [
30
+ "anndata>=0.10",
31
+ "scanpy>=1.10",
32
+ ]
33
+ docs = [
34
+ "sphinx>=7.2",
35
+ "myst-parser>=2.0",
36
+ "sphinx-autodoc-typehints>=1.25",
37
+ "sphinx_rtd_theme>=1.3",
38
+ ]
39
+
40
+ [project.urls]
41
+ Repository = "https://github.com/theislab/cellucid-data"
42
+
43
+ [tool.setuptools.packages.find]
44
+ where = ["src"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,12 @@
1
+ """Utilities for exporting AnnData/array data for the 3D UMAP web viewer."""
2
+
3
+ from importlib import metadata as _metadata
4
+
5
+ try:
6
+ __version__ = _metadata.version("cellucid")
7
+ except _metadata.PackageNotFoundError: # pragma: no cover - handled in dev installs
8
+ __version__ = "0.0.0"
9
+
10
+ from .prepare_data import export_data_for_web
11
+
12
+ __all__ = ["export_data_for_web", "__version__"]
@@ -0,0 +1,149 @@
1
+ import gc
2
+ import pandas as pd
3
+ import itertools
4
+ from pathlib import Path
5
+ from tqdm import tqdm
6
+
7
+ try:
8
+ import scanpy as sc
9
+ except ImportError as exc: # pragma: no cover - optional dependency
10
+ raise ImportError(
11
+ "scanpy is required for cellucid.anndata_variations; install with "
12
+ "`pip install cellucid[analysis]` or add scanpy to your environment."
13
+ ) from exc
14
+
15
+ def _find_repo_root() -> Path:
16
+ """Locate the python project root (contains pyproject.toml and data/)."""
17
+ for candidate in Path(__file__).resolve().parents:
18
+ if (candidate / "pyproject.toml").exists() and (candidate / "data").exists():
19
+ return candidate
20
+ return Path.cwd()
21
+
22
+
23
+ REPO_ROOT = _find_repo_root()
24
+ RAW_DATA_DIR = REPO_ROOT / "data" / "raw"
25
+ EXPERIMENT_DIR = REPO_ROOT / "data" / "experiments"
26
+
27
+ INPUT_H5AD = RAW_DATA_DIR / "adata_latent_scvi-adata_unified_20250925_001_integration-supergpu02.scidom.de-bb886d36409e78a3.h5ad"
28
+ METADATA_PICKLE = RAW_DATA_DIR / "adata_obs_extracted.pickle"
29
+ OUTPUT_DIR = EXPERIMENT_DIR / "umap_parameter_sweep"
30
+
31
+ MIN_DISTS = [0.1, 0.15, 0.20, 0.25, 0.30, 0.35, 0.40, 0.45, 0.50]
32
+ N_NEIGHBORS_LIST = [5, 10, 15, 30, 50, 90, 120]
33
+ RANDOM_SEED = 42
34
+
35
+
36
+ def _print_setup():
37
+ """Log resolved paths so it's obvious what is being used."""
38
+ print("==== AnnData UMAP sweep setup ====")
39
+ print(f"Current working dir : {Path.cwd()}")
40
+ print(f"Repo root : {REPO_ROOT}")
41
+ print(f"Input h5ad : {INPUT_H5AD} (exists: {INPUT_H5AD.exists()})")
42
+ print(f"Metadata pickle : {METADATA_PICKLE} (exists: {METADATA_PICKLE.exists()})")
43
+ print(f"Output directory : {OUTPUT_DIR}")
44
+ print("==================================")
45
+
46
+
47
+ def validate_inputs() -> None:
48
+ """Ensure required files and folders exist before heavy work."""
49
+ missing = [path for path in (INPUT_H5AD, METADATA_PICKLE) if not path.exists()]
50
+ if missing:
51
+ missing_list = "\n - ".join(str(path) for path in missing)
52
+ raise FileNotFoundError(f"Missing required input files:\n - {missing_list}")
53
+
54
+ EXPERIMENT_DIR.mkdir(parents=True, exist_ok=True)
55
+ OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
56
+
57
+
58
+
59
+ def load_and_preprocess_data():
60
+ validate_inputs()
61
+ print("Loading and preparing data (this happens only once)...")
62
+ adata = sc.read_h5ad(INPUT_H5AD)
63
+ adata_complete = pd.read_pickle(METADATA_PICKLE)
64
+
65
+ if "qc_fail" not in adata_complete.columns:
66
+ raise KeyError("Metadata pickle missing required column 'qc_fail'.")
67
+
68
+ missing_cells = set(adata.obs_names) - set(adata_complete.index)
69
+ if missing_cells:
70
+ raise ValueError(f"Metadata missing {len(missing_cells)} cells (e.g., {next(iter(missing_cells))}).")
71
+
72
+ adata_complete = adata_complete.loc[adata.obs_names]
73
+ qc_mask = ~adata_complete["qc_fail"].fillna(False)
74
+
75
+ # Apply QC filtering to both the matrix and obs
76
+ adata = adata[qc_mask].copy()
77
+ adata_complete = adata_complete.loc[qc_mask]
78
+ adata.obs = adata_complete
79
+
80
+ # Drop heavy derived fields to reduce per-run memory
81
+ for container in (adata.obsm, adata.obsp):
82
+ for key in list(container.keys()):
83
+ del container[key]
84
+ adata.uns.clear()
85
+
86
+ print(f"Data prepared. Final: {adata}")
87
+ print(f"Cells kept after QC : {adata.n_obs}")
88
+ print(f"Features : {adata.n_vars}")
89
+ return adata
90
+
91
+
92
+ def run_parameter_sweep(_adata):
93
+ """
94
+ Iterates through parameters, calculating UMAPs and saving results.
95
+ """
96
+ OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
97
+ param_combinations = list(itertools.product(N_NEIGHBORS_LIST, MIN_DISTS))
98
+ print(f"Starting sweep over {len(param_combinations)} combinations...")
99
+ print(f"Writing outputs to: {OUTPUT_DIR}")
100
+
101
+ existing_before = 0
102
+ for k, min_dist in param_combinations:
103
+ dist_str = f"{min_dist:.2f}".replace('.', '_')
104
+ filename = OUTPUT_DIR / f"adata|k__{k}|min_dist_{dist_str}.h5ad"
105
+ if filename.exists():
106
+ existing_before += 1
107
+ to_compute = len(param_combinations) - existing_before
108
+ print(f"Found {existing_before} outputs already present; {to_compute} will be computed.")
109
+
110
+ if to_compute == 0:
111
+ print("All combinations already exist. Nothing to do.")
112
+ return
113
+
114
+ skipped = 0
115
+ created = 0
116
+
117
+ for k, min_dist in tqdm(param_combinations, desc="Processing"):
118
+ dist_str = f"{min_dist:.2f}".replace('.', '_')
119
+ filename = OUTPUT_DIR / f"adata|k__{k}|min_dist_{dist_str}.h5ad"
120
+ if filename.exists():
121
+ skipped += 1
122
+ tqdm.write(f"Exists, skipping: {filename.name}")
123
+ continue
124
+
125
+ adata = _adata.copy()
126
+ try:
127
+ sc.pp.neighbors(adata, n_neighbors=k, random_state=RANDOM_SEED)
128
+ sc.tl.umap(
129
+ adata,
130
+ n_components=3,
131
+ min_dist=min_dist,
132
+ random_state=RANDOM_SEED,
133
+ )
134
+ adata.write_h5ad(filename)
135
+ created += 1
136
+ except Exception as e:
137
+ tqdm.write(f"Calculation error for {filename.name}: {e}")
138
+ gc.collect()
139
+
140
+ print(f"Finished sweep. Created {created}, skipped {skipped}, total {len(param_combinations)}.")
141
+
142
+ if __name__ == "__main__":
143
+ try:
144
+ _print_setup()
145
+ clean_adata = load_and_preprocess_data()
146
+ run_parameter_sweep(clean_adata)
147
+ print("Processing complete.")
148
+ except Exception as e:
149
+ print(f"An error occurred: {e}")