Skip to content

Commit e59695d

Browse files
authored
Merge pull request #21 from ArpiarSaundersLab/kenny
Kenny
2 parents a98e18f + d30b4b7 commit e59695d

220 files changed

Lines changed: 56713 additions & 2235 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎.gitignore‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,4 @@ examples/data/
2323
# run logs
2424
logs/
2525
*.log
26+
*.sh

‎manuscript/alzheimers_coexpression_null.py‎

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@
66
Usage:
77
python alzheimers_coexpression_null.py [--out-dir DIR] [--n-shuffles N]
88
[--n-parallel N] [--cell-type TYPE] [--n-cells N] [--balance-groups]
9+
[--first-shuffle K --batch-dir DIR]
10+
11+
To add shuffles to an existing run, pass --first-shuffle (one past the last
12+
saved shuffle) and --batch-dir. The batch's runs, flip_rates and panels are
13+
written there and the saved outputs are left alone; combine them with
14+
alzheimers_coexpression_null_merge.py.
915
1016
Outputs (to DIR):
1117
- alzheimers_coexpression_null_runs.csv: Real and shuffled run summaries
@@ -80,6 +86,10 @@ def main() -> None:
8086
parser.add_argument("--n-shuffles", type=int, default=25)
8187
parser.add_argument("--n-parallel", type=int, default=None,
8288
help="Shuffles run at once (default: about one per 4 cores)")
89+
parser.add_argument("--first-shuffle", type=int, default=1,
90+
help="Index of the first shuffle (seeds continue from here)")
91+
parser.add_argument("--batch-dir", type=Path, default=None,
92+
help="Write only this batch's raw tables here, for merging")
8393
add_loading_args(parser)
8494
args = parser.parse_args()
8595
out_dir = args.out_dir
@@ -95,7 +105,13 @@ def main() -> None:
95105

96106
print(f"Running co-expression null ({args.n_shuffles} shuffles)...")
97107
res = model.coexpression_null(n_shuffles=args.n_shuffles, n_parallel=args.n_parallel,
98-
verbose=10)
108+
verbose=10, first_shuffle=args.first_shuffle)
109+
if args.batch_dir is not None:
110+
args.batch_dir.mkdir(parents=True, exist_ok=True)
111+
for key in ("runs", "flip_rates", "panels"):
112+
res[key].to_csv(args.batch_dir / f"alzheimers_coexpression_null_{key}.csv", index=False)
113+
print(f"Batch written to {args.batch_dir}")
114+
return
99115
for key in ("runs", "flip_rates", "panels", "gene_frequency", "pvalues"):
100116
res[key].to_csv(out_dir / f"alzheimers_coexpression_null_{key}.csv", index=False)
101117
plot_flip_rates(res["flip_rates"], model.flip_rate_percentage,
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
"""Add batches of extra shuffles to a saved co-expression null.
2+
3+
Reads the saved outputs of alzheimers_coexpression_null.py in DIR and every
4+
batch under DIR/null_batches/ (written with --first-shuffle and --batch-dir),
5+
checks that each batch refit the same observed panel, then rewrites the
6+
outputs from all shuffles together. Per-gene p-values get a Benjamini-Hochberg
7+
adjusted column (p_adj) across the observed panel.
8+
9+
Usage:
10+
python alzheimers_coexpression_null_merge.py --out-dir DIR
11+
12+
Outputs (to DIR, overwritten):
13+
- alzheimers_coexpression_null_runs.csv
14+
- alzheimers_coexpression_null_flip_rates.csv
15+
- alzheimers_coexpression_null_panels.csv
16+
- alzheimers_coexpression_null_gene_frequency.csv (with p_adj)
17+
- alzheimers_coexpression_null_pvalues.csv
18+
- alzheimers_coexpression_null_flip_rates.png / .pdf
19+
"""
20+
21+
from __future__ import annotations
22+
23+
import argparse
24+
from pathlib import Path
25+
26+
import matplotlib.pyplot as plt
27+
import pandas as pd
28+
from scipy.stats import false_discovery_control
29+
30+
import recursieve
31+
from alzheimers_coexpression_null import plot_flip_rates
32+
33+
PREFIX = "alzheimers_coexpression_null_"
34+
TABLES = ("runs", "flip_rates", "panels")
35+
36+
37+
def read_tables(folder: Path) -> dict:
38+
return {key: pd.read_csv(folder / f"{PREFIX}{key}.csv") for key in TABLES}
39+
40+
41+
def observed_panel(panels: pd.DataFrame) -> list:
42+
obs = panels[panels["run"] == "observed"].sort_values("rank")
43+
return obs["gene"].tolist()
44+
45+
46+
def main() -> None:
47+
plt.rcParams.update({"font.family": "Arial", "font.size": 12,
48+
"pdf.fonttype": 42, "ps.fonttype": 42})
49+
parser = argparse.ArgumentParser(description=__doc__)
50+
parser.add_argument("--out-dir", type=Path, required=True)
51+
parser.add_argument("--flip-threshold", type=float, default=0.01,
52+
help="Stop threshold drawn on the flip-rate plot")
53+
args = parser.parse_args()
54+
out_dir = args.out_dir
55+
56+
saved = read_tables(out_dir)
57+
panel = observed_panel(saved["panels"])
58+
batches = sorted((out_dir / "null_batches").glob("*/"))
59+
if not batches:
60+
raise SystemExit(f"No batches in {out_dir / 'null_batches'}")
61+
62+
merged = {key: [saved[key]] for key in TABLES}
63+
for batch in batches:
64+
tables = read_tables(batch)
65+
if observed_panel(tables["panels"]) != panel:
66+
raise SystemExit(f"{batch.name}: observed panel differs from the saved one")
67+
for key in TABLES:
68+
df = tables[key]
69+
merged[key].append(df[df["run"] != "observed"])
70+
print(f"Added {batch.name}")
71+
72+
# rerunning the merge must not count a shuffle twice
73+
runs = pd.concat(merged["runs"], ignore_index=True).drop_duplicates("run", keep="first")
74+
flips = pd.concat(merged["flip_rates"], ignore_index=True).drop_duplicates(["run", "iteration"])
75+
panels = pd.concat(merged["panels"], ignore_index=True).drop_duplicates(["run", "rank"])
76+
77+
freq = recursieve.recursieve._null_gene_frequency(panels)
78+
freq["p_adj"] = false_discovery_control(freq["p_value"], method="bh")
79+
pvalues = recursieve.recursieve._null_pvalues(runs)
80+
81+
outputs = {"runs": runs, "flip_rates": flips, "panels": panels,
82+
"gene_frequency": freq, "pvalues": pvalues}
83+
for key, df in outputs.items():
84+
df.to_csv(out_dir / f"{PREFIX}{key}.csv", index=False)
85+
plot_flip_rates(flips, args.flip_threshold, out_dir / f"{PREFIX}flip_rates")
86+
87+
n = runs["run"].ne("observed").sum()
88+
print(f"{n} shuffles; {int((freq['p_adj'] < 0.05).sum())}/{len(freq)} genes with p_adj < 0.05")
89+
print(pvalues.round(4).to_string(index=False))
90+
91+
92+
if __name__ == "__main__":
93+
main()

0 commit comments

Comments
 (0)