速度问题:使用Deedle创建系列/在F#

时间:2019-05-18 04:53:11

标签: f# deedle

在这个问题中,我使用了Tomas Petricek建议的解决方案:Count unique in a Deedle Series

我已经完成了python python和上述解决方案的快速测试。我已经稍微修改了Tomas建议的功能,以相反的顺序对计数进行排序,以匹配Python函数在Series.count_values()上的输出。

let unique s = 
    s |> Series.groupInto (fun _ v -> v) (fun _ g -> Stats.count g)
      |> Series.sortBy (fun v -> -v)

当我在F#Interactive中执行以下代码时

let rand = Random()
let l = [|1..1_000_000|] |> Array.map (fun _ -> rand.Next(1000))
                         |> Series.ofValues
                         |> unique

使用“ #time”,平均执行时间约为1500ms(仅用于创建随机系列的执行时间为150ms)。

我还测试了类似的代码(在使用Python 3.7的PyCharm的Python控制台中)

import time
import pandas
start = time.time()
df = pandas.DataFrame(np.random.randint(0,1000,size=(1000000, 1)), columns=list('A'))
a = df['A'].value_counts()
print(a)
print("ms: %", 1000*(time.time()-start))

我得到了大约40毫秒(每个步骤的一半)的数据帧+ value_counts()的创建。

关于至少如何加快在F#中创建Series的任何提示?我的代码可能不是最有效的,我想知道我能做什么。我试图改变我的团队的心情,以便将一些研究从Python切换到F#,但我不想从他们那里听到F#是减慢速度的方法。谢谢大家!

1 个答案:

答案 0 :(得分:2)

我既不了解大熊猫也不了解大熊猫,但我怀疑Deedle中的通用聚合不能跟上大熊猫潜在的专业化版本(或者可能只是更彻底地优化了大熊猫)。

一种方法是在将观测值传递给Deedle之前对值进行计数。

例如:

let rand = Random ()
let variant () = 
  Array.init 1_000_000 (fun _ -> rand.Next(1000))
  |> Array.groupBy id
  |> Array.map (fun (k, vs) -> (k, vs.Length))
  |> Array.sortBy (fun (_, c) -> -c)
  |> Series.ofObservations

将原始代码与上面的变体进行比较时,我得到了以下数字。

Original took: 1197 ms with (60, 30, 11) cc
Variant took: 56 ms with (2, 0, 0) cc

因此,阵列变体看起来明显更快,并且产生的GC压力更低。

完整代码示例

open System
open System.Diagnostics
open System.Linq

open Deedle

let now =
  let sw = Stopwatch ()
  sw.Start ()
  fun () -> sw.ElapsedMilliseconds

let time a =
  let inline cc i       = GC.CollectionCount i
  GC.Collect (2, GCCollectionMode.Forced)
  GC.WaitForFullGCComplete () |> ignore
  let before            = now ()
  let bcc0, bcc1, bcc2  = cc 0, cc 1, cc 2
  let v                 = a ()
  let acc0, acc1, acc2  = cc 0, cc 1, cc 2
  let after             = now ()
  v, after - before, (acc0 - bcc0, acc1 - bcc1, acc2 - bcc2)

let seed = 982301576

let run () =
  let rand = Random seed
  let original () = 
    [|1..1_000_000|] 
    |> Array.map (fun _ -> rand.Next(1000))
    |> Series.ofValues
    |> Series.groupInto (fun _ v -> v) (fun _ g -> Stats.count g)
    |> Series.sortBy (fun v -> -v)

  let _, ms, cc = time original
  printfn "Original took: %d ms with %A cc" ms cc

  let rand = Random seed
  let variant () = 
    Array.init 1_000_000 (fun _ -> rand.Next(1000))
    |> Array.groupBy id
    |> Array.map (fun (k, vs) -> (k, vs.Length))
    |> Array.sortBy (fun (_, c) -> -c)
    |> Series.ofObservations

  let _, ms, cc = time variant
  printfn "Variant took: %d ms with %A cc" ms cc

[<EntryPoint>]
let main argv =
  run ()
  0