3

我有两个股票数据序列,我正在尝试排列日期并组合数据,以便我可以将其传递给将对其进行一些统计的其他函数。本质上,我想传递两个(或更多)如下所示的序列:

sequenceA = [(float,DateTime)]
sequenceB = [(float,DateTime)]

到一个函数,并让它返回一个序列,其中所有数据都由 DateTime 正确对齐。就像是:

return = [(float,float,DateTime)]

其中浮点数是该日期时间的两个序列的收盘价。

我尝试过使用嵌套的 for 循环,并且我相当肯定它应该可以工作(尽管我遇到了一些麻烦),但似乎 F# 的匹配表达式也应该能够处理这个问题。我查看了一些文档和匹配表达式的示例,但我遇到了许多我无法解决的不同问题。

这是我最近尝试简化版本的尝试。如您所见,我只是想查看序列“x”的第一个元素是否具有日期“2011 年 1 月 11 日”。问题是 1)它总是返回“是”,并且 2)我不知道如何从这里到整个序列,然后最终是 2+ 序列。

let x = seq[(1.0,System.DateTime.Parse("1/8/2011"));(2.0,System.DateTime.Parse("1/9/2011"))]
type t = seq<float*DateTime>    

let align (a:t) =
    let testDate = System.DateTime.Parse("1/11/2011")
    let b = Seq.head(a)
    match snd b with
        | testDate -> printfn "Yes"
        | _ -> printfn "No"

align x

我对 F# 比较陌生,但我很确定这应该可以通过匹配表达式来实现。任何帮助将非常感激!

4

3 回答 3

2

你的问题有两个部分:

至于模式匹配,在上面的模式中,testDate 是一个名称,将绑定到元组 b 中的第二项。两种模式都将匹配任何日期,但由于第一个模式匹配,您的示例总是打印“是”。

如果要匹配日期的特定值,可以在模式中使用“when”关键字:

let dateValue = DateTime.Today

match dateValue with
| someDate when someDate = DateTime.Today -> "Today"
| _ -> "Not Today"

如果我必须实现 align 函数,我可能不会尝试使用模式匹配。您可以使用 Seq.groupBy 收集具有相同日期的所有条目。

///Groups two sequences together by key
let align a b =

    let simplifyEntry (key, values) =
        let prices = [for value in values -> snd value]
        key, prices

    a 
    |> Seq.append b
    |> Seq.groupBy fst
    |> Seq.map simplifyEntry
    |> Seq.toList

//Demonstrate alignment of two sequences
let s1 = [DateTime.Today, 1.0]
let s2 = [
    DateTime.Today, 2.0 
    DateTime.Today.AddDays(2.0), 10.0]

let pricesByDate = align s1 s2

for day, prices in pricesByDate do
    let pricesText =
        prices
        |> Seq.map string
        |> String.concat ", "
    printfn "%A %s" day pricesText
于 2013-09-06T17:46:22.447 回答
0
open System
let x = seq[(1.0,System.DateTime.Parse("1/8/2011"));(2.0,DateTime.Parse("1/9/2011"))]
//type t = seq<float*DateTime>    
let (|EqualDate|_|) str dt=
  DateTime.TryParse str|>function
   |true,x when x=dt->Some()
   |_->None
let align a =
    //let testDate = System.DateTime.Parse("1/11/2011")
    let b = Seq.head(a)
    match b with
        |_,EqualDate "1/9/2011" -> printfn "Yes"
        | _ -> printfn "No"

align x
x|>Seq.skip 1|>align 
于 2013-09-06T18:17:41.790 回答
0

我碰巧正在开发一个用于处理时间序列数据的库,它具有执行此操作的功能 - 它实际上更通用一些,因为它返回DateTime * float option * float option以表示一个系列在指定日期具有值的情况,但是另一个没有。

该函数假定这两个序列已经排序——这意味着它只需要遍历它们一次(对于未排序的序列,您需要进行多次迭代或构建一些临时表)。

另请注意,与您的示例相比,参数交换。你需要给它DateTime * float。该函数不是特别好 - 它的工作原理IEnumerable是它需要使用可变枚举器(通常是丑陋的命令式东西)。一般来说,模式匹配不能很好地与序列一起工作——你可以得到头部,但你不能得到尾部——因为那样效率很低。你可以为 F# 列表写一个更好的...

open System.Collections.Generic

let alignWithOrdering (seq1:seq<'T * 'TAddress>) (seq2:seq<'T * 'TAddress>) (comparer:IComparer<_>) = seq {
  let withIndex seq = Seq.mapi (fun i v -> i, v) seq
  use en1 = seq1.GetEnumerator()
  use en2 = seq2.GetEnumerator()
  let en1HasNext = ref (en1.MoveNext())
  let en2HasNext = ref (en2.MoveNext())
  let returnAll (en:IEnumerator<_>) hasNext f = seq { 
    if hasNext then
      yield f en.Current
      while en.MoveNext() do yield f en.Current }

  let rec next () = seq {
    if not en1HasNext.Value then yield! returnAll en2 en2HasNext.Value (fun (k, i) -> k, None, Some i)
    elif not en2HasNext.Value then yield! returnAll en1 en1HasNext.Value (fun (k, i) -> k, Some i, None)
    else
      let en1Val, en2Val = fst en1.Current, fst en2.Current
      let comparison = comparer.Compare(en1Val, en2Val)
      if comparison = 0 then 
        yield en1Val, Some(snd en1.Current), Some(snd en2.Current)
        en1HasNext := en1.MoveNext()
        en2HasNext := en2.MoveNext()
        yield! next()
      elif comparison < 0 then
        yield en1Val, Some(snd en1.Current), None
        en1HasNext := en1.MoveNext()
        yield! next ()
      else 
        yield en2Val, None, Some(snd en2.Current)
        en2HasNext := en2.MoveNext() 
        yield! next () }
  yield! next () }

假设我们想使用字符串作为键(而不是你的DateTime),你可以这样称呼它:

alignWithOrdering 
    [ ("b", 0); ("c", 1); ("d", 2) ] 
    [ ("a", 0); ("b", 1); ("c", 2) ] (Comparer<string>.Default) |> List.ofSeq

// Returns 
[ ("a", None, Some 0); ("b", Some 0, Some 1); 
  ("c", Some 1, Some 2); ("d", Some 2, None) ]

如果您对在 F# 中处理股票数据的时间序列感兴趣,您可能有兴趣加入F# Foundation的F# for Data and Machine Learning工作组。我们目前正在开发一个支持时间序列的开源库,这使得这变得更好:-)。如果您有兴趣查看并为早期预览做出贡献,那么您可以通过此工作组进行。

于 2013-09-06T17:39:44.930 回答