0

I found this neat trick of calculating the mean and the standard deviation in one pass over the data. I wanted to have this working for float32 and float. Again, I'm struggling to get this right with generic numbers.

module Seq =
    let inline private avgVarianceReducer toFloat (count, oldM, oldS) x =
        if count = 1 then
            2, x, LanguagePrimitives.GenericZero
        else
            let meanFree = x - oldM
            let newM = oldM + meanFree / (toFloat count)
            count + 1, newM, oldS + meanFree * (x - newM)

    let inline private avgVarianceWith toFloat source =
        match source |> Seq.fold (avgVarianceReducer toFloat) (1, LanguagePrimitives.GenericZero, LanguagePrimitives.GenericZero) with
        | 0, _, _ -> LanguagePrimitives.GenericZero, LanguagePrimitives.GenericZero
        | 1, mean, _ -> mean, LanguagePrimitives.GenericZero
        | n, mean, var -> mean, var / (n - 2 |> toFloat)

    let avgVariance source = source |> avgVarianceWith float
    let avgVariancef source = source |> avgVarianceWith float32

This works for both types, but I have the additional avgVariancef plus I have to choose the right one when calling.

To me the central problem is the conversion to the right kind of float in avgVarianceReducer which I solved by passing in the correct conversion funtion. I tried my way with op_Explicit but failed.

Anybody have an idea for a more elegant solution?

primfaktor
  • 2,831
  • 25
  • 34

1 Answers1

3

Have you tried FSharpPlus? It contains a module for Generic Math and the generic explicit function you're looking for.

Here's how your code would look like:

#r @"FsControl.dll"
#r @"FSharpPlus.dll"

open FSharpPlus
open FSharpPlus.Operators.GenericMath

module Seq =
    let inline private avgVarianceReducer (count, oldM, oldS) (x:'R) =
        if count = 1 then
            2, x, 0G
        else
            let meanFree = x - oldM
            let newM = oldM + meanFree / explicit count
            count + 1, newM, oldS + meanFree * (x - newM)

    let inline avgVariance source : 'R * 'R =
        match source |> Seq.fold avgVarianceReducer (1, 0G, 0G) with
        | 0, _, _ -> 0G, 0G
        | 1, mean, _ -> mean, 0G
        | n, mean, var -> mean, var / (n - 2 |> explicit)

    // or if you prefer specific functions
    let avgVarianceF32 source : float32 * float32 = avgVariance source
    let avgVarianceF   source : float   * float   = avgVariance source

    // it will work with other types as well
    let avgVarianceD source : decimal * decimal   = avgVariance source

In fact you don't need the function explicit, you can use instead the function fromIntegral which is more specific for numbers.

You can also browse the source code of the library and extract only the code you need for your specific case.

Gus
  • 25,839
  • 2
  • 51
  • 76