Я не знаю ни панд, ни deedle, но я подозреваю, что обобщенная агрегация в Deedle не поспевает за потенциально специализированной версией в pandas (или панды могут быть просто оптимизированы более тщательно).
Один из подходов состоит в том, чтобы выполнить подсчет значений перед передачей наблюдений в 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
Таким образом, вариант массива выглядит значительно быстрее, а также создает меньшее давление ГХ.
Полный пример кода
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