Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In F#, how do you handle (float seq) and { ts : DateTime; value: float } in a generic manner?

What is the best way to write your functions so they can handle seamlessly:

  • a float seq
  • a timestamped series of the type { ts : DateTime; value: float } seq, where the values are in the float called value

I need in particular to write functions such as computing the average/variance/random transformations of the time series, and I would like to write only 1 version of these functions.

like image 625
BlueTrin Avatar asked Oct 24 '12 09:10

BlueTrin


People also ask

What is 1 C equal to in Fahrenheit?

Answer: 1° Celsius is equivalent to 33.8° Fahrenheit.

What is the difference between 1 degree Celsius and 1 degree Fahrenheit?

In the Celsius scale there are 100 degrees between the freezing point and the boiling point of water compared to 180 degrees in the Fahrenheit scale. This means that 1 °C = 1.8 °F (check the section about temperature differences below).

What temperature in Fahrenheit is 50 C?

Answer: 50° Celsius is equal to 122° Fahrenheit.


2 Answers

As asked in a comment, the key question is what kind of operations do you want to write over the data type. It might be possible to do this for basic numerical operations, but if you need some more complex computations, then it will probably be easier to write two separate functions.

Anyway, if you want to use basic numerical operations, then you need to define standard numeric operators for your time stamped type. I described this in a recent article. The following implements +, division by integer and zero:

open System

type TimedFloat = 
  { Time : DateTime
    Value : float }
  static member (+) (tf1, tf2) = 
    { Time = DateTime(tf1.Time.Ticks + tf2.Time.Ticks)
      Value = tf1.Value + tf2.Value }
  static member Zero = 
    { Time = DateTime.MinValue
      Value = 0.0 } 
  static member DivideByInt(tf, n) =
    { Time = DateTime(tf.Time.Ticks / int64 n)
      Value = tf.Value / float n }

The + operator is a bit suspicious, because you'll end up with some very large date (and perhaps using TimeSpan from the last would make more sense). However, once you define the operators, you can, for example, use Seq.average:

[ { Time = DateTime.Now
    Value = 3.0 }
  { Time = DateTime.Now.AddDays(2.0)
    Value = 10.0 } ]
|> Seq.average

The Seq.average function works on both types because it is written using static member constraints (meaning that it works on any type that has necessary members). Writing such functions is more difficult than writing normal function, so I would probably not use this style by default. Anyway, here is an introduction with more examples and this SO answer shows more useful tricks.

EDIT - As Jon Harrop points out, this is quite complex approach and it gives you only limited benefits. If you just need to work with the values then converting to a sequence of values is a better approach. If you need more complex computations, then I don't think it is worth writing a generic function. Writing two separate functions for float and timestamped values will probably be easier.

like image 194
Tomas Petricek Avatar answered Oct 01 '22 02:10

Tomas Petricek


Just convert the timestamped seq into a float seq like this:

xs
|> Seq.map (fun x -> x.value)
like image 36
J D Avatar answered Oct 01 '22 04:10

J D