github.com/vertgenlab/gonomics@v1.0.0/cmd/wigPeaks/wigPeaks.go (about) 1 // Command Group: "WIG Tools" 2 // Command Usage: "Identifies peaks in a WIG file" 3 4 // Takes wig file and finds peaks 5 package main 6 7 import ( 8 "flag" 9 "fmt" 10 "log" 11 12 "github.com/vertgenlab/gonomics/bed" 13 "github.com/vertgenlab/gonomics/exception" 14 "github.com/vertgenlab/gonomics/fileio" 15 "github.com/vertgenlab/gonomics/wig" 16 ) 17 18 func wigPeaks(inWig string, outBed string, threshold float64, findMinima bool) { //threshold is float64 because WigValue Value aka v2.Value is float64. 19 records := wig.Read(inWig) //type is []Wig, aka slice of Wig structs 20 var inPeak bool = false 21 var err error 22 var current bed.Bed //to store the current peak as a bed entry 23 out := fileio.EasyCreate(outBed) //instead of answer, use "chan" method to write as we go. out has type EasyCreate defined as a gonomics struct, one of the fields is File which has type *os.File, needed for bed.WriteBed 24 var v2 float64 25 26 for _, v1 := range records { //in range for loop, i is index (0,1,2..) which we don't use in this instance, v is value (content of slice). Record is slice of wig struct, each iteration is slice/object of wig struct, which looks like a block and is often organized by chromosomes (or part of chromosome), and each wig struct will produce independent peaks. The v1 iteration has chr=chrom3,step=100, and a list of WigValues where there are positions+values, values like 11 22 100 etc. 27 inPeak = false //when entering a new wig struct, set inPeak to false 28 var wigPosition = v1.Start 29 for _, v2 = range v1.Values { //each v1 is a wig struct, whose Values is a []float64 which contains the value at each position. Each i2 is index which we don't use, and each v2 is a float64. 30 if passThreshold(v2, threshold, findMinima) { //either (from outside of a peak) start a new peak or inside of a peak 31 if !inPeak { //this means start a new peak if this is currently set to false. 32 inPeak = true //must remember to set inPeak to reflect Peak status 33 current = bed.Bed{Chrom: v1.Chrom, ChromStart: wigPosition, ChromEnd: wigPosition + 1, Name: "", Score: int(v2), FieldsInitialized: 5} //ChromEnd is +1 because bed has [open,closed) interval (note: in this program, the position that ends the peak is still >threshold, not the first position that dips <threshold), Score is the max Wig of the bed region, will update when inside the peak 34 } else { //this means already inside peak 35 current.ChromEnd = wigPosition + 1 //Update ChromEnd 36 if findMinima && v2 < float64(current.Score) { 37 current.Score = int(v2) 38 } else if !findMinima && v2 > float64(current.Score) { //Update Score if found new max wig score 39 current.Score = int(v2) 40 } 41 } 42 } else { //either (from inside of a peak) ending a peak or outside of a peak 43 if inPeak { //if inside of a peak ending a peak 44 inPeak = false //must remember to set inPeak to reflect Peak status 45 bed.WriteBed(out, current) //instead of answer, use "chan" method to write as we go 46 } 47 //if outside of a peak, nothing to do 48 } 49 wigPosition = wigPosition + v1.Step 50 } 51 if inPeak { //after wig struct ends, if inPeak is still true (i.e. ended on a value>threshold), should end peak and append current 52 inPeak = false //redundant since this will happen when enter a new wig struct 53 bed.WriteBed(out, current) //instead of answer, use "chan" method to write as we go 54 } 55 } 56 err = out.Close() 57 exception.PanicOnErr(err) 58 } 59 60 func passThreshold(v2 float64, threshold float64, findMinima bool) bool { 61 if findMinima { 62 return v2 <= threshold 63 } else { 64 return v2 >= threshold 65 } 66 } 67 68 func usage() { 69 fmt.Print( 70 "wigPeaks - takes wig file and finds peaks\n" + 71 "Usage:\n" + 72 " wigPeaks in.wig out.bed\n" + 73 "options:\n") 74 flag.PrintDefaults() 75 } 76 77 func main() { 78 var expectedNumArgs int = 2 79 var peakThreshold *float64 = flag.Float64("threshold", 20, "if number of reads >= threshold, start calling peak") 80 var findMinima *bool = flag.Bool("findMinima", false, "Report local minima peaks instead of maxima past the threshold. ") 81 82 flag.Usage = usage 83 log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile) 84 flag.Parse() 85 86 if len(flag.Args()) != expectedNumArgs { 87 flag.Usage() 88 log.Fatalf("Error: expecting %d arguments, but got %d\n", expectedNumArgs, len(flag.Args())) 89 } 90 91 inWig := flag.Arg(0) 92 outBed := flag.Arg(1) 93 94 wigPeaks(inWig, outBed, *peakThreshold, *findMinima) 95 }