|
1 | 1 | namespace BioFSharp.Mz |
2 | 2 |
|
| 3 | +open System |
3 | 4 | open FSharp.Stats |
4 | 5 | open FSharpAux |
5 | 6 | open FSharp.Stats.Fitting |
@@ -247,3 +248,114 @@ module FDRControl = |
247 | 248 | // takes a score from the dataset and assigns it a q value |
248 | 249 | let interpolation = Interpolation.LinearSpline.predict linearSplineCoeff |
249 | 250 | interpolation |
| 251 | + |
| 252 | + /// for given data, creates a logistic regression model and returns a mapping function for this model |
| 253 | + let getLogisticRegressionFunction (x:vector) (y:vector) epsilon = |
| 254 | + let alpha = |
| 255 | + match FSharp.Stats.Fitting.LogisticRegression.Univariable.estimateAlpha epsilon x y with |
| 256 | + | Some a -> a |
| 257 | + | None -> failwith "Could not find an alpha for logistic regression of fdr data" |
| 258 | + let weight = FSharp.Stats.Fitting.LogisticRegression.Univariable.coefficient epsilon alpha x y |
| 259 | + FSharp.Stats.Fitting.LogisticRegression.Univariable.predict weight |
| 260 | + |
| 261 | + /// Creates a Histogram based on a given score of a target/decoy dataset. Each bin contains the information of the total count, the decoy count and the median score. |
| 262 | + /// (Bin, Count, DecoyCount, Median Score) |
| 263 | + let createTargetDecoyHis bandwidth (isDecoy: 'a -> bool) (decoyScoreF: 'a -> float) (targetScoreF: 'a -> float) (data: 'a[]) = |
| 264 | + let halfBw = bandwidth / 2.0 |
| 265 | + let scoreDecoyInfo = |
| 266 | + data |
| 267 | + |> Array.map (fun x -> |
| 268 | + if isDecoy x then |
| 269 | + {|Score = decoyScoreF x; Decoy = true|} |
| 270 | + else |
| 271 | + {|Score = targetScoreF x; Decoy = false|} |
| 272 | + ) |
| 273 | + scoreDecoyInfo |
| 274 | + |> Array.groupBy (fun x -> |
| 275 | + floor (x.Score / bandwidth)) |
| 276 | + |> Array.map (fun (k,values) -> |
| 277 | + let count = (Array.length(values)) |
| 278 | + let decoyCount = (values |> Array.filter (fun x -> x.Decoy = true) |> Array.length) |
| 279 | + let medianScore = values |> Array.map (fun x -> x.Score) |> Array.median |
| 280 | + // first part of the tuple only needed for debugging |
| 281 | + if k < 0. then |
| 282 | + ((k * bandwidth) + halfBw, count, decoyCount, medianScore) |
| 283 | + else |
| 284 | + ((k + 1.) * bandwidth - halfBw, count, decoyCount, medianScore) |
| 285 | + ) |
| 286 | + |
| 287 | + /// Calculates the PEP value based on the ratio of Decoys to targets at a given score |
| 288 | + let calculatePEPValues (totalCountF: 'a -> float) (decoyCountF: 'a -> float) (scoreF: 'a -> float) (dataFreq: 'a[]) = |
| 289 | + dataFreq |
| 290 | + |> Array.map (fun x -> |
| 291 | + scoreF x,(decoyCountF x)/(totalCountF x) |
| 292 | + ) |
| 293 | + |> Array.sortBy fst |
| 294 | + |> Array.toList |
| 295 | + |
| 296 | + /// Logit transforms pep values (log10) |
| 297 | + let logitTransformPepValues score pepVal = |
| 298 | + Array.zip score pepVal |
| 299 | + // 0 and 1 are + and - infinity |
| 300 | + |> Array.filter (fun (y,x) -> x <> 0. && x <> 1.) |
| 301 | + |> Array.map (fun (score,pep) -> |
| 302 | + score, |
| 303 | + log10 (pep/(1.-pep)) |
| 304 | + ) |
| 305 | + |> Array.unzip |
| 306 | + |
| 307 | + /// Calculates monotonized PEP values for a target/decoy dataset based on the decoy/target ratio. Entries are binned with a given bandwidth as intital estiamtor based on the scores. |
| 308 | + /// Returns a function which maps from score to PEP value based on a fit of a linear function using linear regression. The linear regression is performed on the logit transformed |
| 309 | + /// pep values. The fit focuses on the pep values centered aound the middle of the score distribution |
| 310 | + let initCalculateLin (trace: string -> unit) bandwidth (isDecoy: 'a -> bool) (decoyScoreF: 'a -> float) (targetScoreF: 'a -> float) (data: 'a[]) = |
| 311 | + let lowerScore, upperScore = |
| 312 | + let decoy = |
| 313 | + data |
| 314 | + |> Array.filter isDecoy |
| 315 | + |> Array.map decoyScoreF |
| 316 | + |> Array.filter (fun x -> x < 0.) |
| 317 | + |> Array.median |
| 318 | + let target = |
| 319 | + data |
| 320 | + |> Array.filter (isDecoy >> not) |
| 321 | + |> Array.map targetScoreF |
| 322 | + |> Array.filter (fun x -> x > 0.) |
| 323 | + |> Array.median |
| 324 | + decoy, target |
| 325 | + trace (sprintf "Lower Score: %f; Upper Score: %f" lowerScore upperScore) |
| 326 | + let filteredData = |
| 327 | + data |
| 328 | + |> Array.filter (fun entry -> |
| 329 | + if isDecoy entry then |
| 330 | + let score = decoyScoreF entry |
| 331 | + score >= lowerScore && score <= upperScore |
| 332 | + else |
| 333 | + let score = targetScoreF entry |
| 334 | + score >= lowerScore && score <= upperScore |
| 335 | + ) |
| 336 | + trace (sprintf "Initial Bandwidth: %f" bandwidth) |
| 337 | + let fittingFunction, score, pep = |
| 338 | + let xPointRange = |
| 339 | + let min = Math.Min((Array.minBy targetScoreF filteredData) |> targetScoreF, (Array.minBy decoyScoreF filteredData) |> decoyScoreF) |
| 340 | + let max = Math.Max((Array.maxBy targetScoreF filteredData) |> targetScoreF, (Array.maxBy decoyScoreF filteredData) |> decoyScoreF) |
| 341 | + max-min |
| 342 | + let upperBW = Math.Min(10., xPointRange/10.) |
| 343 | + [|bandwidth .. 0.1 .. upperBW|] |
| 344 | + |> Array.choose (fun bw -> |
| 345 | + let targetDecoyHis = createTargetDecoyHis bw (isDecoy: 'a -> bool) (decoyScoreF: 'a -> float) (targetScoreF: 'a -> float) (filteredData: 'a[]) |
| 346 | + let score',pep' = |
| 347 | + calculatePEPValues (fun (_,count,_,_) -> float count) (fun (_,_,decoyCount,_) -> float decoyCount) (fun (_,_,_,medianScore) -> medianScore) targetDecoyHis |
| 348 | + |> Array.ofList |
| 349 | + |> Array.unzip |
| 350 | + let logitScore, logitPEPVal = logitTransformPepValues score' pep' |
| 351 | + let coeff = Fitting.LinearRegression.OLS.Linear.Univariable.fit (vector logitScore) (vector logitPEPVal) |
| 352 | + let fittingFunction' = (Fitting.LinearRegression.OLS.Linear.Univariable.predict coeff) >> (fun x -> 10.**(x)/(1.+10.**(x))) |
| 353 | + let sos = FSharp.Stats.Fitting.GoodnessOfFit.calculateSumOfSquares fittingFunction' score' pep' |
| 354 | + if coeff.[1] < 0. then |
| 355 | + Some (sos.Error/sos.Count, fittingFunction', score', pep', bw) |
| 356 | + else |
| 357 | + None |
| 358 | + ) |
| 359 | + |> Array.minBy (fun (error,_,_,_,_) -> error) |
| 360 | + |> fun (error, fit,s,p,bw) -> trace (sprintf "Chosen Bandwidth: %f" bw); fit,s,p |
| 361 | + fittingFunction |
0 commit comments