Aggregate results from multiple Isabl analyses into combined datasets. Use when merging VCFs, TSVs, or other outputs across samples or cohorts.
Instalação
Instalar com Codex ou Claude Copie este prompt, cole no Codex, Claude ou outro assistente e deixe que ele revise a página da skill e instale para você.
Understand result schema (what files/keys are available)
Collect result paths from storage_url
Verify files exist before merging
Choose merge strategy (concat, join, custom)
Execute merge and save output
Validate merged output
Step 1: Identify Target Analyses
import isabl_cli as ii
# Get succeeded analyses for an application in a project
analyses = ii.get_analyses(
projects=PROJECT_PK,
application__name="MUTECT",
status="SUCCEEDED"
)
print(f"Found {len(analyses)} analyses to merge")
for a in analyses[:5]:
target = a.targets[0].system_id if a.targets else"N/A"print(f" [{a.pk}] {target}")
Step 2: Understand Result Schema
# Check what results are available
sample_analysis = analyses[0]
print(f"Available results: {list(sample_analysis.results.keys())}")
# Check application's result definitions
app = ii.get_instance("applications", sample_analysis.application.pk)
print(f"Result schema: {app.results}")
Common result keys:
vcf - Variant calls
bam - Aligned reads
tsv / csv - Tabular data
summary - Summary statistics
qc_metrics - Quality metrics
Step 3: Collect Result Paths
from pathlib import Path
result_key = "tsv"# The result key to merge
paths = []
for a in analyses:
# Option 1: From results dict (if path stored there)if result_key in a.results:
path = a.results[result_key]
ifisinstance(path, dict):
path = path.get("path") or path.get("url")
paths.append({"analysis_pk": a.pk, "path": path})
# Option 2: Construct from storage_urlelse:
expected_path = Path(a.storage_url) / f"{result_key}.tsv"
paths.append({"analysis_pk": a.pk, "path": str(expected_path)})
print(f"Collected {len(paths)} paths")
Step 4: Verify Files Exist
import os
valid_paths = []
missing = []
for item in paths:
if os.path.exists(item["path"]):
valid_paths.append(item)
else:
missing.append(item)
print(f"Valid: {len(valid_paths)}, Missing: {len(missing)}")
if missing:
print("Missing files:")
for m in missing[:5]:
print(f" Analysis {m['analysis_pk']}: {m['path']}")
Step 5: Choose Merge Strategy
Strategy A: Simple Concatenation (same schema)
import pandas as pd
dfs = []
for item in valid_paths:
df = pd.read_csv(item["path"], sep="\t")
df["analysis_pk"] = item["analysis_pk"]
dfs.append(df)
merged = pd.concat(dfs, ignore_index=True)
print(f"Merged shape: {merged.shape}")
Strategy B: Wide Format (pivot by sample)
dfs = []
for a in analyses:
target_id = a.targets[0].system_id if a.targets elsef"analysis_{a.pk}"
df = pd.read_csv(a.results["tsv"], sep="\t")
df = df.rename(columns={"value": target_id})
dfs.append(df)
# Merge on common key
merged = dfs[0]
for df in dfs[1:]:
merged = merged.merge(df, on="key_column", how="outer")
Strategy C: Extract Specific Metrics
# Build summary table from individual results
data = []
for a in analyses:
target = a.targets[0] if a.targets elseNone
row = {
"analysis_pk": a.pk,
"sample_id": target.system_id if target elseNone,
"category": target.sample.category if target elseNone,
}
# Extract metrics from resultsif"summary"in a.results:
summary = a.results["summary"]
ifisinstance(summary, dict):
row.update(summary.get("data", {}))
data.append(row)
merged = pd.DataFrame(data)
Step 6: Execute Merge
# Full example: merge mutation counts from MUTECT analysesimport pandas as pd
from pathlib import Path
analyses = ii.get_analyses(
projects=PROJECT_PK,
application__name="MUTECT",
status="SUCCEEDED"
)
data = []
for a in analyses:
target = a.targets[0]
reference = a.references[0] if a.references elseNone# Read the mutations TSV
muts_path = Path(a.storage_url) / "mutations.tsv"if muts_path.exists():
muts = pd.read_csv(muts_path, sep="\t")
data.append({
"sample_id": target.system_id,
"tumor_category": target.sample.category,
"normal_id": reference.system_id if reference elseNone,
"total_mutations": len(muts),
"snvs": len(muts[muts["type"] == "SNV"]),
"indels": len(muts[muts["type"] == "INDEL"]),
"analysis_pk": a.pk,
})
merged = pd.DataFrame(data)
merged.to_csv("mutation_summary.csv", index=False)
print(f"Saved {len(merged)} samples to mutation_summary.csv")
# First, generate VCF list file from Python
vcf_paths = []
for a in analyses:
vcf_path = Path(a.storage_url) / "output.vcf.gz"if vcf_path.exists():
vcf_paths.append(str(vcf_path))
# Write to file for bcftoolswithopen("vcf_files.txt", "w") as f:
f.write("\n".join(vcf_paths))
print(f"Listed {len(vcf_paths)} VCF files")
# Then merge with bcftools
bcftools merge -l vcf_files.txt -o merged.vcf.gz -O z
Merge BAM coverage
# Collect coverage metrics from multiple analyses
coverage_data = []
for a in analyses:
metrics_file = Path(a.storage_url) / "coverage_metrics.txt"if metrics_file.exists():
# Parse coverage metrics (format varies by tool)withopen(metrics_file) as f:
for line in f:
if line.startswith("MEAN_COVERAGE"):
coverage_data.append({
"sample": a.targets[0].system_id,
"mean_coverage": float(line.split("\t")[1])
})
Project-level aggregation
# Use project-level analysis if available
project_analyses = ii.get_analyses(
project_level_analysis=PROJECT_PK,
application__name="COHORT_SUMMARY",
status="SUCCEEDED"
)
if project_analyses:
# Results already aggregated
merged = project_analyses[0].results
else:
# Need to aggregate manuallypass
Handling Large Datasets
# Process in chunks to avoid memory issuesimport pandas as pd
chunk_size = 100
output_file = "merged_large.csv"
first_chunk = Truefor i inrange(0, len(valid_paths), chunk_size):
chunk_paths = valid_paths[i:i+chunk_size]
dfs = [pd.read_csv(p["path"], sep="\t") for p in chunk_paths]
chunk_df = pd.concat(dfs, ignore_index=True)
chunk_df.to_csv(
output_file,
mode="w"if first_chunk else"a",
header=first_chunk,
index=False
)
first_chunk = Falseprint(f"Processed {min(i+chunk_size, len(valid_paths))}/{len(valid_paths)}")