| """SQL provider example — extract from a simulated invitroDB and tensorize. |
| |
| This shows the provider-aware path: SQL source tables are preserved as |
| Arrow tables, keys/lineage are tracked, and pushdown filters reduce what's |
| pulled before canonicalization. |
| """ |
|
|
| import sqlite3 |
| import tempfile |
| import os |
| import warnings |
| warnings.filterwarnings("ignore") |
|
|
| from toxarrow.adapters import SqlProviderAdapter, SqlTableMapping |
| from toxarrow.compile.arrow_store import ArrowStudyStore |
| from toxarrow.schemas.tensor import DenseGridSpec |
| from toxarrow.materialize.materializer import TensorMaterializer |
|
|
| |
| db_path = os.path.join(tempfile.mkdtemp(), "invitro.sqlite") |
| conn = sqlite3.connect(db_path) |
| conn.executescript(""" |
| CREATE TABLE chemical (chid TEXT PRIMARY KEY, chnm TEXT); |
| CREATE TABLE assay (aeid TEXT PRIMARY KEY, aenm TEXT, assay_family TEXT); |
| CREATE TABLE mc5 ( |
| m5id INTEGER PRIMARY KEY, |
| chid TEXT, aeid TEXT, |
| modl_acc REAL, modl_ga REAL, hitc INTEGER, |
| FOREIGN KEY (chid) REFERENCES chemical(chid), |
| FOREIGN KEY (aeid) REFERENCES assay(aeid) |
| ); |
| |
| INSERT INTO chemical VALUES |
| ('CHEM001','Bisphenol A'), |
| ('CHEM002','Genistein'), |
| ('CHEM003','17b-Estradiol'), |
| ('CHEM004','Atrazine'); |
| |
| INSERT INTO assay VALUES |
| ('A01','ER_alpha_agonism','nuclear_receptor'), |
| ('A02','AR_antagonism','nuclear_receptor'), |
| ('A03','Mitochondrial_tox','cytotoxicity'); |
| |
| -- Only active hits (hitc=1) with measurable AC50 |
| INSERT INTO mc5 VALUES |
| (1,'CHEM001','A01',0.45,85.0,1), |
| (2,'CHEM001','A03',12.0,40.0,1), |
| (3,'CHEM002','A01',0.08,95.0,1), |
| (4,'CHEM002','A02',2.4,60.0,1), |
| (5,'CHEM003','A01',0.002,100.0,1), |
| (6,'CHEM003','A02',0.15,75.0,1), |
| (7,'CHEM004','A03',45.0,25.0,1); |
| -- CHEM001 has NO hit for A02 (inactive in AR assay) |
| -- CHEM004 has hits only in A03 |
| """) |
| conn.commit() |
|
|
| |
| mappings = [ |
| SqlTableMapping( |
| table="chemical", entity="unit", primary_key="chid", |
| column_map={"unit_id": "chid", "label": "chnm"}, |
| order_by="chid", |
| ), |
| SqlTableMapping( |
| table="assay", entity="endpoint", primary_key="aeid", |
| column_map={ |
| "endpoint_id": "aeid", "endpoint_name": "aenm", |
| "endpoint_family": "assay_family", |
| }, |
| order_by="aeid", |
| ), |
| |
| SqlTableMapping( |
| table="mc5", entity="observation", primary_key="m5id", |
| column_map={ |
| "unit_id": "chid", "endpoint_id": "aeid", |
| "value": "modl_acc", |
| }, |
| where="hitc = 1", |
| order_by="chid, aeid", |
| ), |
| ] |
|
|
| adapter = SqlProviderAdapter( |
| conn, mappings, study_id="invitro-example", |
| provider_name="invitrodb-synthetic", organism_or_system="in_vitro", |
| ) |
|
|
| |
| tables = adapter.stage() |
| print(f"Staged {len(tables)} source tables:") |
| for name, tbl in tables.items(): |
| print(f" {name}: {tbl.num_rows} rows, columns={tbl.column_names}") |
|
|
| group = next(adapter.read()) |
| print(f"\nCanonical: {len(group.units)} chemicals, {len(group.endpoints)} assays, " |
| f"{len(group.observations)} active hits") |
|
|
| |
| store = ArrowStudyStore.from_records(group) |
| import numpy as np |
|
|
| rows = store.observations.to_pylist() |
| chem_ids = sorted([u.unit_id for u in group.units]) |
| assay_order = sorted([e.endpoint_id for e in group.endpoints]) |
| assay_idx = {a: i for i, a in enumerate(assay_order)} |
|
|
| vals = np.full((len(chem_ids), len(assay_order)), np.nan, dtype=np.float32) |
| mask = np.ones((len(chem_ids), len(assay_order)), dtype=bool) |
| for r in rows: |
| ci = chem_ids.index(r["unit_id"]) |
| ai = assay_idx[r["endpoint_id"]] |
| v = r.get("value") |
| if v is not None: |
| vals[ci, ai] = float(v) |
| mask[ci, ai] = False |
|
|
| print(f"\nChemical × Assay grid: shape={vals.shape}") |
| print(f"Missing cells (masked): {mask.sum()}") |
| for ci, cid in enumerate(chem_ids): |
| hits = [f"{a}:{vals[ci,i]:.2f}" if not mask[ci,i] else f"{a}:---" for i,a in enumerate(assay_order)] |
| print(f" {cid}: {hits}") |
|
|
| |
| assert mask[chem_ids.index("CHEM001"), assay_idx["A02"]] == True, "CHEM001 missing AR" |
| assert mask[chem_ids.index("CHEM004"), assay_idx["A01"]] == True, "CHEM004 missing ER" |
| assert mask[chem_ids.index("CHEM004"), assay_idx["A02"]] == True, "CHEM004 missing AR" |
|
|
| print("\nAll checks passed: missingness masks correct, source tables preserved") |
| conn.close() |
|
|