================================================================================ README: OCR Peak Extraction and Gene-Proximal Peak Finding Working directory: /home/yan/TaRGET_II/Li_adt/DAR/ Date: 2026-03-10 ================================================================================ Two approaches are documented here for finding OCR peaks near genes of interest (Esr1, Dapk2, Nod1): - Method A: Gene body window (bedtools window) - Method B: Nearest TSS (bedtools closest) [recommended, used in final output] ================================================================================ METHOD A: Gene Body Window Approach ================================================================================ -------------------------------------------------------------------------------- STEP A1: Extract unique OCR regions from OCR_allTissue.txt and make BED file -------------------------------------------------------------------------------- Input: OCR_allTissue.txt - Tab-separated, has header row - Column 1 (OCR): genomic region in format chr,start,end - Multiple rows per OCR region (one per tissue/condition combination) Output: OCR_allTissue.bed (193,519 unique regions, BED3 format) Code: awk 'NR>1 {print $1}' OCR_allTissue.txt \ | sort -u \ | awk -F',' '{print $1"\t"$2"\t"$3}' \ > OCR_allTissue.bed Explanation: - awk 'NR>1 {print $1}' : skip header, extract column 1 (OCR IDs) - sort -u : deduplicate so each region appears once - awk -F',' '{print $1"\t"$2"\t"$3}' : split chr,start,end by comma, reprint as tab-separated BED columns -------------------------------------------------------------------------------- STEP A2: Extract gene body coordinates and create gene BED file -------------------------------------------------------------------------------- Gene body coordinates extracted from: /home/yan/TaRGET_II/Li_adt/peaks_gene_finding/gencode.vM10.annotation.gtf (GENCODE vM10, mm10/GRCm38) Code (to inspect/reproduce coordinates): grep -w "gene" /home/yan/TaRGET_II/Li_adt/peaks_gene_finding/gencode.vM10.annotation.gtf \ | grep -E '"Esr1"|"Dapk2"|"Nod1"' \ | awk '{print $1"\t"$4-1"\t"$5"\t"$10"\t"$14"\t"$7}' \ | tr -d '";"' Explanation: - grep -w "gene" : keep only "gene" feature rows (not exon/transcript) - grep -E '"Esr1"...' : filter for our 3 genes by name - awk $4-1 : convert GTF 1-based start to BED 0-based - tr -d '";"' : strip quote/semicolon characters from GTF fields Gene body coordinates (0-based BED): Nod1: chr6 54923948 54972612 (strand: -) Dapk2: chr9 66158222 66272242 (strand: +) Esr1: chr10 4611592 5005614 (strand: +) Code (create gene BED file): printf "chr6\t54923948\t54972612\tNod1\n\ chr9\t66158222\t66272242\tDapk2\n\ chr10\t4611592\t5005614\tEsr1\n" \ > genes_Esr1_Dapk2_Nod1.bed Output: genes_Esr1_Dapk2_Nod1.bed (BED4: chr start end gene_name) -------------------------------------------------------------------------------- STEP A3: Find OCR peaks within 1Mb of each gene body -------------------------------------------------------------------------------- Input: OCR_allTissue.bed, genes_Esr1_Dapk2_Nod1.bed Output: OCR_peaks_Esr1_Dapk2_Nod1_1Mb.bed (BED4: chr start end gene_name) 899 total peaks (Esr1: 211, Dapk2: 358, Nod1: 330) Code: bedtools window \ -a OCR_allTissue.bed \ -b genes_Esr1_Dapk2_Nod1.bed \ -w 1000000 \ | awk '{print $1"\t"$2"\t"$3"\t"$7}' \ | sort -k1,1 -k2,2n \ > OCR_peaks_Esr1_Dapk2_Nod1_1Mb.bed Explanation: - bedtools window -w 1000000 : find all peaks in -a that fall within 1Mb of any feature in -b; the window extends 1Mb on BOTH sides of the entire gene body, i.e. [gene_start-1Mb, gene_end+1Mb] - awk '{print $1,$2,$3,$7}' : columns 1-3 are the peak coordinates; column 7 is the gene name from -b file - sort -k1,1 -k2,2n : sort by chromosome then by start position Limitation: a peak can appear multiple times if it is within 1Mb of more than one gene. Distance to gene body edge (not TSS) is not reported. ================================================================================ METHOD B: Nearest TSS Approach (recommended) ================================================================================ This method follows the same logic as: /home/yan/TaRGET_II/Li_adt/Target_expose/motif_peaks_to_gene.sh Each peak is assigned to its single nearest TSS genome-wide. Only peaks whose nearest TSS belongs to Esr1, Dapk2, or Nod1 AND is within ±1Mb are kept. The signed distance to TSS is reported (negative = upstream, positive = downstream). -------------------------------------------------------------------------------- STEP B1: Sort OCR BED file (required by bedtools closest -sorted) -------------------------------------------------------------------------------- Input: OCR_allTissue.bed Output: OCR_allTissue.sorted.bed Code: sort -k1,1 -k2,2n OCR_allTissue.bed > OCR_allTissue.sorted.bed Explanation: - bedtools closest -sorted requires both input files to be sorted by chromosome and start position. OCR_allTissue.bed was not pre-sorted. -------------------------------------------------------------------------------- STEP B2: Find nearest TSS for every OCR peak genome-wide -------------------------------------------------------------------------------- TSS reference file: /home/yan/TaRGET_II/Li_adt/Target_expose/gencode_mm10_gene_TSS_pc_lncRNA.sorted.bed - GENCODE vM10 mm10, protein-coding and lncRNA genes - Columns: chr TSS_start TSS_end ensembl_id gene_name gene_type strand Input: OCR_allTissue.sorted.bed Output: OCR_allTissue_nearestTSS_1Mb_tmp.txt (intermediate, 11 columns) Code: TSS="/home/yan/TaRGET_II/Li_adt/Target_expose/gencode_mm10_gene_TSS_pc_lncRNA.sorted.bed" bedtools closest \ -a OCR_allTissue.sorted.bed \ -b "$TSS" \ -D a \ -k 1 \ -sorted \ > OCR_allTissue_nearestTSS_1Mb_tmp.txt Explanation: - -D a : report signed distance from the -a feature (peak) perspective. Negative = peak is upstream of TSS; positive = downstream. - -k 1 : report only the 1 nearest TSS per peak (not all ties). - -sorted: use memory-efficient chromosome-sweep algorithm; requires both files to be sorted. Output columns (11 total): 1-3 : peak chr, start, end 4-10 : nearest TSS chr, start, end, ensembl_id, gene_name, gene_type, strand 11 : signed distance from peak to TSS (bp) -------------------------------------------------------------------------------- STEP B3: Filter for ±1Mb and keep only Esr1, Dapk2, Nod1 -------------------------------------------------------------------------------- Input: OCR_allTissue_nearestTSS_1Mb_tmp.txt Output: OCR_peaks_Esr1_Dapk2_Nod1_TSS1Mb.bed (BED5: chr start end gene_name distance_to_TSS) 97 total peaks (Esr1: 39, Dapk2: 34, Nod1: 24) AWK script (filter_peaks.awk): BEGIN{OFS="\t"} { dist = $11 gene = $8 if (dist != "." && dist <= 1000000 && dist >= -1000000) { if (gene == "Esr1" || gene == "Dapk2" || gene == "Nod1") { print $1, $2, $3, gene, dist } } } Code: awk -f filter_peaks.awk OCR_allTissue_nearestTSS_1Mb_tmp.txt \ | sort -k1,1 -k2,2n \ > OCR_peaks_Esr1_Dapk2_Nod1_TSS1Mb.bed Explanation: - dist != "." : exclude peaks with no nearby gene (bedtools closest returns "." when no feature is found) - dist <= 1000000 && dist >= -1000000 : keep only peaks within ±1Mb of a TSS - gene == "Esr1"... : keep only peaks whose nearest TSS is one of our 3 target genes - print $1,$2,$3,gene,dist : output peak coordinates + gene name + signed distance to TSS ================================================================================ COMPARISON OF METHODS ================================================================================ Method Window center # Peaks Assignment logic --------------- ---------------- ---------- --------------------------- A (gene body) Gene body ends 899 Any peak within 1Mb of body B (TSS) TSS only 97 Only if target gene is the NEAREST TSS within ±1Mb Method B is more stringent: a peak is only assigned to Esr1/Dapk2/Nod1 if no other gene has a closer TSS. This reduces false positives but may miss peaks that are genuinely regulatory but located closer to an intervening gene's TSS. Overlap between methods (unique peaks): Method A total : 899 Method B total : 97 Overlap (A ∩ B) : 97 (100% of Method B) Method A only : 802 Method B only : 0 Method A (899 unique peaks) ┌─────────────────────────────────────┐ │ │ │ Method A only: 802 │ │ ┌──────────────┐ │ │ │ Both: 97 │ │ │ │ (= all B) │ │ │ └──────────────┘ │ └─────────────────────────────────────┘ Method B only: 0 All 97 Method B peaks are fully contained within Method A (Method B is a complete subset of Method A). The 802 peaks unique to Method A are cases where a peak is near the target gene body, but another gene's TSS sits closer to the peak genome-wide. Code used to compute overlap: # Count unique peaks in each method cut -f1-3 OCR_peaks_Esr1_Dapk2_Nod1_1Mb.bed | sort -u | wc -l # → 899 cut -f1-3 OCR_peaks_Esr1_Dapk2_Nod1_TSS1Mb.bed | sort -u | wc -l # → 97 # Count peaks present in both (intersection) comm -12 \ <(cut -f1-3 OCR_peaks_Esr1_Dapk2_Nod1_1Mb.bed | sort -u) \ <(cut -f1-3 OCR_peaks_Esr1_Dapk2_Nod1_TSS1Mb.bed | sort -u) \ | wc -l # → 97 Explanation: - cut -f1-3 : extract only chr/start/end columns (ignore gene/dist) - sort -u : deduplicate to count unique genomic regions - comm -12 : output only lines present in BOTH sorted files (-1 suppresses file1-only, -2 suppresses file2-only) ================================================================================ METHOD C: Annotate OCR_allTissue.txt with Both Methods ================================================================================ Maps both Method A and Method B results back onto every row of the original OCR_allTissue.txt, adding three new columns: Gene_bodyWindow_1Mb : gene name from Method A, or "." if not assigned Nearest_TSS_1Mb : gene name from Method B, or "." if not assigned Dist_to_TSS : signed distance to TSS from Method B, or "." The original file is NOT modified. A new annotated file is created. Annotation summary (4,219,852 data rows): Method A only: 18,118 rows Method B only: 0 rows Both methods: 2,360 rows Neither: 4,199,374 rows Note: Method B is a subset of Method A. Every peak with a Method B annotation also has a Method A annotation, because nearest-TSS (B) is more stringent than gene-body-window (A). Method A alone captures peaks where a closer intervening TSS exists between the peak and the target gene. -------------------------------------------------------------------------------- STEP C1: Build AWK annotation script (annotate_OCR.awk) -------------------------------------------------------------------------------- AWK script (annotate_OCR.awk): BEGIN { OFS="\t" } # Pass 1: Load Method A BED (gene body window) FILENAME == methodA { key = $1 "," $2 "," $3 geneA[key] = $4 next } # Pass 2: Load Method B BED (nearest TSS) FILENAME == methodB { key = $1 "," $2 "," $3 geneB[key] = $4 distB[key] = $5 next } # Pass 3: Annotate OCR_allTissue.txt FILENAME == mainfile { if (FNR == 1) { print $0, "Gene_bodyWindow_1Mb", "Nearest_TSS_1Mb", "Dist_to_TSS" } else { key = $1 gA = (key in geneA) ? geneA[key] : "." gB = (key in geneB) ? geneB[key] : "." dB = (key in distB) ? distB[key] : "." print $0, gA, gB, dB } } Explanation: - The script is passed three files sequentially: Method A BED, Method B BED, then OCR_allTissue.txt. - FILENAME == methodA/methodB/mainfile : awk variables set at runtime via -v flags determine which file is currently being read, so each pass loads data into the appropriate lookup array. - key = $1 "," $2 "," $3 : builds the OCR ID string (chr,start,end) from BED columns to match the format used in OCR_allTissue.txt col 1. - geneA[] / geneB[] / distB[] : associative arrays keyed by OCR ID, storing gene name and TSS distance for fast O(1) lookup. - (key in geneA) ? ... : "." : ternary; returns gene name if the OCR was found in the results, otherwise "." (missing value). - FNR == 1 on the main file : prints header with new column names before processing data rows. -------------------------------------------------------------------------------- STEP C2: Run annotation -------------------------------------------------------------------------------- Input: OCR_peaks_Esr1_Dapk2_Nod1_1Mb.bed (Method A results) OCR_peaks_Esr1_Dapk2_Nod1_TSS1Mb.bed (Method B results) OCR_allTissue.txt (original, unmodified) Output: OCR_allTissue_annotated.txt - Same as OCR_allTissue.txt with 3 new columns appended - 4,219,853 lines (1 header + 4,219,852 data rows) Code: awk -v methodA="OCR_peaks_Esr1_Dapk2_Nod1_1Mb.bed" \ -v methodB="OCR_peaks_Esr1_Dapk2_Nod1_TSS1Mb.bed" \ -v mainfile="OCR_allTissue.txt" \ -f annotate_OCR.awk \ OCR_peaks_Esr1_Dapk2_Nod1_1Mb.bed \ OCR_peaks_Esr1_Dapk2_Nod1_TSS1Mb.bed \ OCR_allTissue.txt \ > OCR_allTissue_annotated.txt Explanation: - -v methodA=... -v methodB=... -v mainfile=... : pass file paths as awk variables so FILENAME comparisons inside the script work correctly. - Files are listed TWICE: once as -v values (for FILENAME matching) and once as positional arguments (for awk to actually read them). - The three positional files are processed in order: Method A first to populate geneA[], then Method B to populate geneB[]/distB[], then the main file which is annotated on output. ================================================================================ SUMMARY: METHOD A & B ANNOTATION BY LAB AND CONDITION ================================================================================ Rows in OCR_allTissue_annotated.txt with gene annotation, grouped by Lab and Exposure. MethodA_rows = rows where Gene_bodyWindow_1Mb != "."; MethodB_rows = rows where Nearest_TSS_1Mb != ".". Lab Exposure Total_rows MethodA_rows MethodB_rows ----- ---------- ------------ -------------- -------------- AL TCDD 440,950 2,168 269 BA BPA10mg 588,604 2,826 292 BA BPA10ug 587,458 2,817 291 BI PM2.5 415,442 1,987 228 DO DEHP 466,717 2,247 266 DO Pb 466,008 2,249 268 MU PM2.5 456,675 2,170 253 WK TBT 458,071 2,259 280 ZB As 339,927 1,755 213 Note: rows > unique peaks because OCR_allTissue.txt contains multiple rows per OCR region (one per tissue/condition/sample combination). Code used to generate this summary (summary_by_lab_condition.awk): AWK script: BEGIN { FS="\t"; OFS="\t" } NR == 1 { next } { exposure = $10 lab = $11 key = lab "\t" exposure total[key]++ if ($12 != ".") methodA[key]++ if ($13 != ".") methodB[key]++ } END { print "Lab", "Exposure", "Total_rows", "MethodA_rows", "MethodB_rows" for (k in total) { a = (k in methodA) ? methodA[k] : 0 b = (k in methodB) ? methodB[k] : 0 print k, total[k], a, b } } Run command: awk -f summary_by_lab_condition.awk OCR_allTissue_annotated.txt \ | awk 'NR==1{header=$0; next} {print}' \ | sort -k1,1 -k2,2 \ | awk 'BEGIN{print "Lab\tExposure\tTotal_rows\tMethodA_rows\tMethodB_rows"} {print}' Explanation: - $10 / $11 : Exposure and Lab columns from OCR_allTissue_annotated.txt - $12 / $13 : Gene_bodyWindow_1Mb (Method A) and Nearest_TSS_1Mb (Method B) - key : combined Lab+Exposure string used as array index - total[]/methodA[]/methodB[] : count all rows, Method A hits, Method B hits per Lab+Exposure group - Header is separated before sort then re-prepended to keep it at top ================================================================================ SUMMARY: BPA MALE LIVER (Tissue=Li, Sex=M, Exposure=BPA10mg/BPA10ug) ================================================================================ Rows filtered from OCR_allTissue_annotated.txt for male liver BPA samples, broken down by dose and age (adt=adult, wl=weanling). Columns show total rows, DAR (differentially accessible) rows, and Method A/B annotation counts split by gene. Exposure Age Total_rows DAR_rows MethodA MethodB A_Esr1 A_Dapk2 A_Nod1 B_Esr1 B_Dapk2 B_Nod1 -------- --- ---------- -------- ------- ------- ------ ------- ------ ------ ------- ------ BPA10mg adt 87,562 10,444 433 54 91 189 153 23 21 10 BPA10mg wl 87,815 4,267 473 56 106 215 152 23 23 10 BPA10ug adt 86,946 16,201 429 53 89 187 153 22 21 10 BPA10ug wl 86,795 568 467 53 103 212 152 22 21 10 Columns: DAR_rows : rows where DAR == "TRUE" (differentially accessible regions) MethodA : rows with Gene_bodyWindow_1Mb annotation (gene body ± 1Mb) MethodB : rows with Nearest_TSS_1Mb annotation (nearest TSS ± 1Mb) A_*/B_* : Method A/B rows broken down by individual gene Code (bpa_summary.awk): AWK script: BEGIN { FS="\t"; OFS="\t" } NR == 1 { next } $7 == "Li" && $9 == "M" && ($10 == "BPA10mg" || $10 == "BPA10ug") { key = $10 "\t" $8 total[key]++ if ($6 == "TRUE") dar[key]++ if ($12 != ".") { methodA[key]++ geneA[key "_" $12]++ } if ($13 != ".") { methodB[key]++ geneB[key "_" $13]++ } } END { print "Exposure\tAge\tTotal_rows\tDAR_rows\tMethodA_rows\tMethodB_rows\t" \ "A_Esr1\tA_Dapk2\tA_Nod1\tB_Esr1\tB_Dapk2\tB_Nod1" n = asorti(total, sorted_keys) for (i = 1; i <= n; i++) { k = sorted_keys[i] d = (k in dar) ? dar[k] : 0 a = (k in methodA) ? methodA[k] : 0 b = (k in methodB) ? methodB[k] : 0 aE = (k"_Esr1" in geneA) ? geneA[k"_Esr1"] : 0 aD = (k"_Dapk2" in geneA) ? geneA[k"_Dapk2"] : 0 aN = (k"_Nod1" in geneA) ? geneA[k"_Nod1"] : 0 bE = (k"_Esr1" in geneB) ? geneB[k"_Esr1"] : 0 bD = (k"_Dapk2" in geneB) ? geneB[k"_Dapk2"] : 0 bN = (k"_Nod1" in geneB) ? geneB[k"_Nod1"] : 0 print k, total[k], d, a, b, aE, aD, aN, bE, bD, bN } } Run command: awk -f bpa_summary.awk OCR_allTissue_annotated.txt Explanation: - $7 == "Li" : filter Tissue = liver - $9 == "M" : filter Sex = male - $10 == "BPA10mg/ug" : filter BPA exposure doses - key = $10 "\t" $8 : group by Exposure + Age - $6 == "TRUE" : DAR column; count differentially accessible rows - geneA[key "_" $12] : track per-gene counts by appending gene name to key - asorti() : sort output keys alphabetically for consistent order ================================================================================ OUTPUT FILES SUMMARY ================================================================================ OCR_allTissue.bed All 193,519 unique OCR regions (BED3) OCR_allTissue.sorted.bed Sorted version of above genes_Esr1_Dapk2_Nod1.bed Gene body coords for 3 genes (BED4) OCR_peaks_Esr1_Dapk2_Nod1_1Mb.bed Method A result: 899 peaks (BED4) OCR_allTissue_nearestTSS_1Mb_tmp.txt Method B intermediate: all peaks with nearest TSS annotation (11 col) OCR_peaks_Esr1_Dapk2_Nod1_TSS1Mb.bed Method B result: 97 peaks (BED5) OCR_allTissue_annotated.txt Method C result: full annotated table (original 11 cols + 3 new cols) annotate_OCR.awk AWK script used in Method C filter_peaks.awk AWK script used in Method B Step 3 ================================================================================ SOFTWARE ================================================================================ bedtools (/usr/bin/bedtools) awk, sort (GNU coreutils) Reference: GENCODE vM10 (mm10/GRCm38) ================================================================================