-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathload_data.py
More file actions
147 lines (124 loc) · 4.86 KB
/
Copy pathload_data.py
File metadata and controls
147 lines (124 loc) · 4.86 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#!/usr/bin/env python3
"""
load_data.py — Part 1: Data Management.
Run with: python load_data.py
Creates teiko.db in the repo root, builds the relational schema, and loads every
row of cell-count.csv into it. Re-running is safe: it rebuilds from scratch
(idempotent), so the database always matches the current CSV.
"""
from __future__ import annotations
import os
import sys
import pandas as pd
import db
def load(csv_path: str = db.CSV_PATH, db_path: str = db.DB_PATH) -> None:
if not os.path.exists(csv_path):
sys.exit(
f"ERROR: '{csv_path}' not found.\n"
"Place the real cell-count.csv in the repo root (or run "
"`python make_synthetic_csv.py` to generate a test file)."
)
# --- read + normalize -------------------------------------------------
raw = pd.read_csv(csv_path)
colmap = db.normalize_columns(raw.columns)
df = raw.rename(columns=colmap)
# Identify population columns = anything that isn't known metadata.
pop_cols = [c for c in df.columns if c not in db.METADATA_COLS]
if not pop_cols:
sys.exit("ERROR: no population (count) columns detected in the CSV.")
required_meta = {"project", "subject", "sample"}
missing = required_meta - set(df.columns)
if missing:
sys.exit(f"ERROR: CSV is missing required column(s): {sorted(missing)}")
# Coerce types; blank/NaN response/age -> NULL.
if "response" in df.columns:
df["response"] = df["response"].apply(
lambda v: None if (pd.isna(v) or str(v).strip() == "") else str(v)
)
for numeric in ["age", "time_from_treatment_start", *pop_cols]:
if numeric in df.columns:
df[numeric] = pd.to_numeric(df[numeric], errors="coerce")
def clean(v):
"""NaN/empty -> None for safe SQLite insertion."""
return None if (v is None or (not isinstance(v, str) and pd.isna(v))) else v
# --- (re)build schema -------------------------------------------------
if os.path.exists(db_path):
os.remove(db_path)
conn = db.get_connection(db_path)
conn.executescript(db.SCHEMA)
# --- projects ---------------------------------------------------------
projects = sorted(df["project"].dropna().unique())
conn.executemany("INSERT INTO projects (project_name) VALUES (?)", [(p,) for p in projects])
project_ids = {
row["project_name"]: row["project_id"]
for row in conn.execute("SELECT project_id, project_name FROM projects")
}
# --- subjects (one row per subject; take first occurrence) ------------
def col(name):
return name if name in df.columns else None
subj_rows = []
seen = set()
for _, r in df.iterrows():
s = r["subject"]
if s in seen:
continue
seen.add(s)
subj_rows.append((
s,
project_ids[r["project"]],
clean(r.get("condition")),
None if pd.isna(r.get("age")) else int(r["age"]),
clean(r.get("sex")),
clean(r.get("treatment")),
clean(r.get("response")),
))
conn.executemany(
"INSERT INTO subjects (subject_id, project_id, condition, age, sex, treatment, response) "
"VALUES (?,?,?,?,?,?,?)",
subj_rows,
)
# --- samples ----------------------------------------------------------
sample_rows = []
seen_samples = set()
for _, r in df.iterrows():
sid = r["sample"]
if sid in seen_samples:
continue
seen_samples.add(sid)
tp = r.get("time_from_treatment_start")
sample_rows.append((
sid,
r["subject"],
clean(r.get("sample_type")),
None if pd.isna(tp) else int(tp),
))
conn.executemany(
"INSERT INTO samples (sample_id, subject_id, sample_type, time_from_treatment_start) "
"VALUES (?,?,?,?)",
sample_rows,
)
# --- cell_counts (wide -> long) --------------------------------------
long_df = df.melt(
id_vars=["sample"], value_vars=pop_cols,
var_name="population", value_name="count",
).dropna(subset=["count"])
count_rows = [
(r["sample"], r["population"], int(r["count"]))
for _, r in long_df.iterrows()
]
conn.executemany(
"INSERT OR IGNORE INTO cell_counts (sample_id, population, count) VALUES (?,?,?)",
count_rows,
)
conn.commit()
# --- summary ----------------------------------------------------------
def n(table):
return conn.execute(f"SELECT COUNT(*) FROM {table}").fetchone()[0]
print("Loaded cell-count.csv into", db_path)
print(f" projects : {n('projects')}")
print(f" subjects : {n('subjects')}")
print(f" samples : {n('samples')}")
print(f" cell_counts: {n('cell_counts')} (populations: {', '.join(pop_cols)})")
conn.close()
if __name__ == "__main__":
load()