What is the best way to write your functions so they can handle seamlessly:
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.
Answer: 1° Celsius is equivalent to 33.8° 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).
Answer: 50° Celsius is equal to 122° Fahrenheit.
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.
Just convert the timestamped seq into a float seq like this:
xs
|> Seq.map (fun x -> x.value)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With