速度问题:在 F# 中创建具有 Deedle/Getting 个唯一值的系列
Speed issue: Creating Series with Deedle/Getting unique values in F#
我在这个问题中使用了 Tomas Petricek 建议的解决方案:
我已经做了一个快速测试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”我平均执行大约 1500 毫秒(仅创建随机系列需要 150 毫秒)。
我也测试了类似的代码(在 PyCharm 的 Python 控制台中使用 Python 3.7)
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))
我得到,创建 DataFrame + value_counts() 大约 40 毫秒(每一步减半)。
关于如何至少加快 F# 系列创建的任何提示?我的代码可能不是最有效的,我想知道我能做什么。我正试图改变我团队的情绪,将一些研究从 Python 转移到 F#,我不想听到他们说 F# 会变慢。谢谢大家!
我既不知道 pandas 也不知道 deedle,但我怀疑 Deedle 中的通用聚合跟不上 pandas 中潜在的专用版本(或者 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
所以数组变体似乎明显更快并且产生的 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
我在这个问题中使用了 Tomas Petricek 建议的解决方案:
我已经做了一个快速测试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”我平均执行大约 1500 毫秒(仅创建随机系列需要 150 毫秒)。
我也测试了类似的代码(在 PyCharm 的 Python 控制台中使用 Python 3.7)
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))
我得到,创建 DataFrame + value_counts() 大约 40 毫秒(每一步减半)。
关于如何至少加快 F# 系列创建的任何提示?我的代码可能不是最有效的,我想知道我能做什么。我正试图改变我团队的情绪,将一些研究从 Python 转移到 F#,我不想听到他们说 F# 会变慢。谢谢大家!
我既不知道 pandas 也不知道 deedle,但我怀疑 Deedle 中的通用聚合跟不上 pandas 中潜在的专用版本(或者 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
所以数组变体似乎明显更快并且产生的 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