Figure 4: Genetic Validation (manuscript Fig 4)¶
Purpose¶
Validate biological meaningfulness through genetic associations and demonstrate genetic architecture of signatures.
Panels Required:¶
- Panel A: PRS differences between disease subtypes
- Panel B: Manhattan plot of signature-specific genetic associations
- Panel C: Signature-modifying variants (genetic × signature interaction)
- Panel D: Genetic correlation network of signatures
Key Message:¶
Demonstrate that signatures have heritable biological architecture
In [ ]:
# Setup
import sys
import os
sys.path.append('/Users/sarahurbut/aladynoulli2/pyScripts/new_oct_revision')
import torch
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from pathlib import Path
# Set style
sns.set_style("whitegrid")
plt.rcParams['figure.dpi'] = 300
print("Setup complete")
In [ ]:
%run /Users/sarahurbut/aladynoulli2/pyScripts/dec_6_revision/new_notebooks/main_paper_figures/generate_prs_signature_plots.py --batch_dir='/Users/sarahurbut/Library/CloudStorage/Dropbox/censor_e_batchrun_vectorized/' --output_dir='/Users/sarahurbut/aladynoulli2/pyScripts/dec_6_revision/new_notebooks/results/paper_figs/prs_signatures_corrected_E_PCS_SEX' --n_top 30
In [ ]:
%run /Users/sarahurbut/aladynoulli2/pyScripts/dec_6_revision/new_notebooks/results/paper_figs/rap/visualize_genetic_loci.py
In [ ]:
%run /Users/sarahurbut/aladynoulli2/pyScripts/dec_6_revision/new_notebooks/reviewer_responses/notebooks/R1/check_loci_uniqueness.py
In [ ]:
%run /Users/sarahurbut/aladynoulli2/pyScripts/dec_6_revision/new_notebooks/main_paper_figures/generate_genetic_validation_multipanel.py
In [ ]:
"""
Compare loci from individual SIG files with all_loci_annotated.tsv
to verify they match and identify unique loci.
"""
import pandas as pd
import numpy as np
from pathlib import Path
from collections import defaultdict
# Path to SIG files directory (user mentioned it's in Desktop)
sig_files_dir = Path("/Users/sarahurbut/Desktop/leadforpaper")
# Load all_loci_annotated.tsv
loci_file = Path("/Users/sarahurbut/Library/CloudStorage/Dropbox-Personal/all_loci_annotated.tsv")
print(f"Loading all_loci_annotated.tsv from {loci_file}...")
all_loci_df = pd.read_csv(loci_file, sep='\t')
all_loci_df['SIG_NUM'] = all_loci_df['SIG'].str.replace('SIG', '').astype(int)
# Create unique identifier: CHR:POS:EA:OA (UID) or CHR:POS if UID not available
if 'UID' in all_loci_df.columns:
all_loci_df['unique_id'] = all_loci_df['UID']
else:
# Try different column name variations
chr_col = None
for col in all_loci_df.columns:
if col.upper().replace('#', '').replace('_', '').strip() == 'CHR' or col == '#CHR':
chr_col = col
break
if chr_col:
all_loci_df['unique_id'] = all_loci_df[chr_col].astype(str) + ':' + all_loci_df['POS'].astype(str)
else:
print("Warning: Could not find CHR column in all_loci_annotated.tsv")
print(f"Available columns: {all_loci_df.columns.tolist()}")
print(f"Loaded {len(all_loci_df)} loci from all_loci_annotated.tsv")
print(f"Signatures: {sorted(all_loci_df['SIG_NUM'].unique())}")
# Check if directory exists
if not sig_files_dir.exists():
print(f"⚠️ Directory does not exist: {sig_files_dir}")
else:
print(f"✓ Found directory: {sig_files_dir}")
print(f"Files in directory:")
for item in sorted(sig_files_dir.iterdir()):
if item.is_file():
size = item.stat().st_size
print(f" {item.name} ({size} bytes)")
# Load individual SIG files
sig_files_data = {}
sig_file_paths = {}
for sig_num in range(21):
# Try different file naming patterns
patterns = [
f"SIG{sig_num}_AUC_ukb_eur*.lead.sumstats.txt",
f"*SIG{sig_num}_AUC_ukb_eur*.lead.sumstats.txt",
f"*SIG{sig_num}*.txt"
]
sig_df = None
file_path = None
for pattern in patterns:
matches = list(sig_files_dir.glob(pattern))
if matches:
file_path = matches[0]
break
if file_path and file_path.exists():
# Check if file is empty
if file_path.stat().st_size == 0:
print(f" ⚠ SIG{sig_num}: File is empty - {file_path.name}")
continue
try:
# Read the file - header starts with #CHR, so don't use comment='#'
# Read normally and pandas will handle the # in the column name
sig_df = pd.read_csv(file_path, sep='\t')
if sig_df is None or len(sig_df) == 0:
print(f" ⚠ SIG{sig_num}: No data rows - {file_path.name}")
continue
# Print column names for debugging (first file only)
if sig_num == 0:
print(f"\n Column names in SIG files:")
print(f" {list(sig_df.columns)}")
# Create unique identifier - use UID if available, otherwise CHR:POS
if 'UID' in sig_df.columns:
sig_df['unique_id'] = sig_df['UID']
else:
# Try to find CHR column (might be #CHR)
chr_col = None
for col in sig_df.columns:
if col == '#CHR' or col.upper().replace('#', '').strip() == 'CHR':
chr_col = col
break
if chr_col and 'POS' in sig_df.columns:
sig_df['unique_id'] = sig_df[chr_col].astype(str) + ':' + sig_df['POS'].astype(str)
else:
print(f" ✗ SIG{sig_num}: Could not find CHR/POS columns")
print(f" Available columns: {list(sig_df.columns)}")
continue
sig_df['SIG_NUM'] = sig_num
sig_files_data[sig_num] = sig_df
sig_file_paths[sig_num] = file_path
print(f" ✓ SIG{sig_num}: {len(sig_df)} loci from {file_path.name}")
except Exception as e:
print(f" ✗ SIG{sig_num}: Error processing file - {e}")
import traceback
traceback.print_exc()
print(f"\nLoaded {len(sig_files_data)} signature files")
if len(sig_files_data) == 0:
print("⚠️ No signature files were successfully loaded. Please check:")
print(" 1. File paths and naming conventions")
print(" 2. File formats (should be tab-separated)")
print(" 3. File contents (some may be empty)")
exit(1)
# Compare loci
comparison_results = []
for sig_num in range(21):
if sig_num not in sig_files_data:
continue
sig_df = sig_files_data[sig_num]
all_loci_sig = all_loci_df[all_loci_df['SIG_NUM'] == sig_num]
# Get unique IDs
sig_unique_ids = set(sig_df['unique_id'].values)
all_loci_unique_ids = set(all_loci_sig['unique_id'].values)
# Find matches and differences
in_both = sig_unique_ids & all_loci_unique_ids
only_in_sig_file = sig_unique_ids - all_loci_unique_ids
only_in_all_loci = all_loci_unique_ids - sig_unique_ids
comparison_results.append({
'Signature': sig_num,
'In_SIG_file': len(sig_unique_ids),
'In_all_loci': len(all_loci_unique_ids),
'In_both': len(in_both),
'Only_in_SIG_file': len(only_in_sig_file),
'Only_in_all_loci': len(only_in_all_loci),
'Match_rate': len(in_both) / max(len(sig_unique_ids), 1) * 100
})
if len(only_in_sig_file) > 0 or len(only_in_all_loci) > 0:
print(f"\nSIG{sig_num} discrepancies:")
if len(only_in_sig_file) > 0:
print(f" Only in SIG file ({len(only_in_sig_file)}):")
for uid in list(only_in_sig_file)[:5]: # Show first 5
row = sig_df[sig_df['unique_id'] == uid].iloc[0]
rsid = row.get('rsid', 'N/A')
print(f" {uid} (rsid: {rsid})")
if len(only_in_all_loci) > 0:
print(f" Only in all_loci ({len(only_in_all_loci)}):")
for uid in list(only_in_all_loci)[:5]: # Show first 5
row = all_loci_sig[all_loci_sig['unique_id'] == uid].iloc[0]
rsid = row.get('rsid', 'N/A')
print(f" {uid} (rsid: {rsid})")
# Create summary table
comparison_df = pd.DataFrame(comparison_results)
print("\n" + "="*80)
print("COMPARISON SUMMARY")
print("="*80)
print(comparison_df.to_string(index=False))
# Calculate total unique loci across all signatures
all_sig_unique_ids = set()
for sig_df in sig_files_data.values():
all_sig_unique_ids.update(sig_df['unique_id'].values)
all_loci_unique_ids = set(all_loci_df['unique_id'].values)
print(f"\n" + "="*80)
print("OVERALL SUMMARY")
print("="*80)
print(f"Total unique loci in SIG files: {len(all_sig_unique_ids)}")
print(f"Total unique loci in all_loci_annotated.tsv: {len(all_loci_unique_ids)}")
print(f"Loci in both: {len(all_sig_unique_ids & all_loci_unique_ids)}")
print(f"Only in SIG files: {len(all_sig_unique_ids - all_loci_unique_ids)}")
print(f"Only in all_loci: {len(all_loci_unique_ids - all_sig_unique_ids)}")
# Save comparison table
output_file = Path("/Users/sarahurbut/aladynoulli2/pyScripts/dec_6_revision/new_notebooks/results/loci_comparison_table.csv")
output_file.parent.mkdir(parents=True, exist_ok=True)
comparison_df.to_csv(output_file, index=False)
print(f"\n✓ Saved comparison table to: {output_file}")
# Create detailed table of unique loci per signature
unique_loci_table = []
for sig_num in range(21):
if sig_num not in sig_files_data:
continue
sig_df = sig_files_data[sig_num]
for _, row in sig_df.iterrows():
# Get column values with fallbacks
chr_col = '#CHR' if '#CHR' in sig_df.columns else 'CHR'
chr_val = row.get(chr_col, 'N/A')
pos_val = row.get('POS', 'N/A')
uid_val = row.get('UID', 'N/A')
rsid_val = row.get('rsid', 'N/A')
log10p_val = row.get('LOG10P', 'N/A')
gene_val = row.get('nearestgene', 'N/A') if 'nearestgene' in row else 'N/A'
unique_loci_table.append({
'Signature': sig_num,
'CHR': chr_val,
'POS': pos_val,
'UID': uid_val,
'rsid': rsid_val,
'LOG10P': log10p_val,
'nearestgene': gene_val
})
unique_loci_df = pd.DataFrame(unique_loci_table)
unique_loci_output = output_file.parent / "unique_loci_per_signature.csv"
unique_loci_df.to_csv(unique_loci_output, index=False)
print(f"✓ Saved unique loci table to: {unique_loci_output}")
print(f" Total loci: {len(unique_loci_df)}")
In [ ]:
"""
Convert unique_loci_per_signature.csv to VCF format for Ensembl VEP upload
"""
import pandas as pd
from pathlib import Path
# Load your loci table
loci_df = pd.read_csv("/Users/sarahurbut/aladynoulli2/pyScripts/dec_6_revision/new_notebooks/results/unique_loci_per_signature.csv")
# Create VCF file
vcf_lines = []
vcf_lines.append("##fileformat=VCFv4.2")
vcf_lines.append("##source=unique_loci_per_signature")
vcf_lines.append("#CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\tINFO")
# For each locus, create a VCF line
# Note: We'll need to handle cases where we don't have REF/ALT
for _, row in loci_df.iterrows():
chrom = f"chr{int(row['CHR'])}"
pos = int(row['POS'])
rsid = row.get('rsid', '.')
# Try to extract REF/ALT from UID if available
# UID format is like "1:154400320:A:G"
ref = '.'
alt = '.'
if 'UID' in row and pd.notna(row['UID']):
uid_parts = str(row['UID']).split(':')
if len(uid_parts) >= 4:
ref = uid_parts[2]
alt = uid_parts[3]
# If still missing, use placeholder
if ref == '.' or alt == '.':
ref = 'N'
alt = 'N'
# Create VCF line
vcf_line = f"{chrom}\t{pos}\t{rsid}\t{ref}\t{alt}\t.\t.\t."
vcf_lines.append(vcf_line)
# Write VCF file
output_vcf = Path("/Users/sarahurbut/aladynoulli2/pyScripts/dec_6_revision/new_notebooks/results/unique_loci_for_vep.vcf")
with open(output_vcf, 'w') as f:
f.write('\n'.join(vcf_lines))
print(f"✓ Created VCF file: {output_vcf}")
print(f" Total variants: {len(loci_df)}")
print(f"\nYou can now upload this file to:")
print(f" https://www.ensembl.org/Tools/VEP")
In [ ]:
%run /Users/sarahurbut/aladynoulli2/pyScripts/dec_6_revision/new_notebooks/annotate_loci.py
In [ ]:
%run /Users/sarahurbut/aladynoulli2/pyScripts/dec_6_revision/new_notebooks/create_annotation_comparison.py