Files

105 lines
3.5 KiB
Python
Raw Permalink Normal View History

2026-08-03 18:42:47 +08:00
#!/usr/bin/env python3
"""Create minimal synthetic RNA-seq data for strandness parameter validation."""
from __future__ import annotations
import gzip
import random
import shutil
from pathlib import Path
try:
import yaml # type: ignore
except Exception as exc: # pragma: no cover
raise SystemExit(f"PyYAML is required: {exc}")
PROJECT_ROOT = Path(__file__).resolve().parents[1]
BASE = PROJECT_ROOT / "项目记录" / "rnaseqUpdate" / "testdata" / "strandness_minimal"
def revcomp(seq: str) -> str:
return seq.translate(str.maketrans("ACGTN", "TGCAN"))[::-1]
def write_fastq(path: Path, records: list[tuple[str, str]]) -> None:
path.parent.mkdir(parents=True, exist_ok=True)
with gzip.open(path, "wt", encoding="utf-8") as handle:
for name, seq in records:
handle.write(f"@{name}\n{seq}\n+\n{'I' * len(seq)}\n")
def sample_yaml(strandness: str, mixed: bool = False) -> dict:
samples = {
"S1": {
"run": "SYN001",
"r1": "S1_1.clean.fastq.gz",
"r2": "S1_2.clean.fastq.gz",
"condition": "CK",
"replicate": 1,
"library_strandness": strandness,
}
}
if mixed:
samples["S2"] = {
"run": "SYN002",
"r1": "S2_1.clean.fastq.gz",
"r2": "S2_2.clean.fastq.gz",
"condition": "TRT",
"replicate": 1,
"library_strandness": "reverse",
}
return {
"species": {"code": "Test", "speciesName": "Synthetic test", "keggOrganism": ""},
"rnaseq": {"output_prefix": "Test"},
"samples": samples,
"groups": {sample: meta["condition"] for sample, meta in samples.items()},
"experiment": {"design": "two_group", "control_group": "CK"},
}
def main() -> int:
if BASE.exists():
shutil.rmtree(BASE)
input_dir = BASE / "input"
final_dir = BASE / "workspace" / "job_synthetic_strandness" / "final"
input_dir.mkdir(parents=True, exist_ok=True)
final_dir.mkdir(parents=True, exist_ok=True)
rng = random.Random(7)
genome = "".join(rng.choice("ACGT") for _ in range(1000))
(input_dir / "Test.genome.fasta").write_text(f">chr1\n{genome}\n", encoding="utf-8")
(input_dir / "Test.gtf").write_text(
'chr1\tsynthetic\texon\t80\t320\t.\t+\t.\tgene_id "gene1"; transcript_id "tx1";\n',
encoding="utf-8",
)
r1_records = []
r2_records = []
for idx, start in enumerate((100, 120, 140, 160), start=1):
frag_start = start - 1
r1 = genome[frag_start : frag_start + 50]
r2 = revcomp(genome[frag_start + 100 : frag_start + 150])
r1_records.append((f"S1_{idx}/1", r1))
r2_records.append((f"S1_{idx}/2", r2))
write_fastq(final_dir / "S1_1.clean.fastq.gz", r1_records)
write_fastq(final_dir / "S1_2.clean.fastq.gz", r2_records)
write_fastq(final_dir / "S2_1.clean.fastq.gz", r1_records)
write_fastq(final_dir / "S2_2.clean.fastq.gz", r2_records)
for strandness in ("non_stranded", "forward", "reverse"):
payload = sample_yaml(strandness)
(input_dir / f"samples_{strandness}.yaml").write_text(
yaml.safe_dump(payload, allow_unicode=True, sort_keys=False),
encoding="utf-8",
)
(input_dir / "samples_mixed.yaml").write_text(
yaml.safe_dump(sample_yaml("forward", mixed=True), allow_unicode=True, sort_keys=False),
encoding="utf-8",
)
print(BASE)
return 0
if __name__ == "__main__":
raise SystemExit(main())