如何在数组中对记录进行有效的时间连接?

时间:2016-06-26 00:27:35

标签: f#

我想在未来至少X天/分钟/秒加入记录到下一条记录。我需要使用具有几十万个记录的数组来完成此操作。我对序列/列表/数组持开放态度,但我相信数组可能是最快的。

我可以使用init在Deedle中快速完成此操作,但我可以更轻松地使用标准数组/序列/列表进行转换。

以下示例适用于1000条记录,但在100k时非常慢。评论here建议使用二分搜索,但我不知道如何在搜索基于不等式的情况下执行此操作。

last

1 个答案:

答案 0 :(得分:4)

问题在于这个表达式:

let r2 =
    rs
    |> Array.filter (fun x -> x.Date > futureDay)
    |> Array.tryHead

这会过滤整个数组,并在您真正想要第一个匹配项时,使用 all 匹配项创建一个新数组。这种情况发生在r 。试试这个:

let r2 = rs |> Array.tryFind (fun x -> x.Date > futureDay)

N.b。如果你处理的是序列而不是数组,那么你的逻辑会很好,因为过滤器会被懒惰地评估,但当然序列通常比数组慢。要记住的是,Seq模块是懒惰的(有一些例外),而使用ArrayList(以及Set和{{1}时如果模块,链/管道中的每一步都会急切地分配一个新的Map / list,因此在处理大型集合时会非常昂贵。

如果排序array不会影响您的逻辑或预期输出,则可以使用Array.FindIndex开始在rs索引处进行搜索,从而进一步改进而不是每次从数组的开头:

r

即使采用Array.sortInPlace rs rs |> Seq.mapi (fun i r -> let futureDay = r.Date.AddDays 4.0 let r2Index = Array.FindIndex (rs, i, (fun x -> x.Date > futureDay)) match r2Index with | -1 -> None | i' -> let x = rs.[i'] Some { Date1=r.Date; Value1=r.Value; Date2=x.Date; Value2=x.Value }) |> Seq.choose id |> Array.ofSeq 方法,也应该提供显着改进,因为每次只需要扫描少量数组元素。

以下是我老化平板电脑的FSI时序,系统处于零负载状态:

  • 10k元素:
    • unsorted + Array.tryFind(原始代码):00:00:08.783
    • unsorted + Array.filter:00:00:03.844
    • Array.tryFind + Array.sort:00:00:00.027
  • 100k元素:
    • 未分类+ Seq.mapi:我没有打扰。
    • unsorted + Array.filter:00:06:14.288
    • Array.tryFind + Array.sort:00:00:00.305