【问题标题】:Julia: How to return the number of unique elements in an ArrayJulia:如何返回数组中唯一元素的数量
【发布时间】:2019-10-02 14:13:43
【问题描述】:

在 Julia 中返回数组中唯一元素数量的函数是什么?

在 R 中你有length(unique(x))。我可以在 Julia 中做同样的事情,但我认为应该有更有效的方法。

【问题讨论】:

    标签: julia


    【解决方案1】:

    如果您想要一个准确的答案,length(unique(x)) 与一般对象一样有效。如果您的值具有有限域,例如UInt8,则使用固定大小的表可能更有效。如果你能接受一个近似值,那么你可以使用 HyperLogLog 数据结构/算法,它在 OnlineStats 包中实现:

    https://joshday.github.io/OnlineStats.jl/latest/api/#OnlineStats.HyperLogLog

    【讨论】:

    • length(unique(x)) 的扩展性如何?它最终是否按照@Cameron Bieganek 的建议使用Set(x)
    【解决方案2】:

    似乎length(Set(x))length(unique(x)) 快一些。

    julia> using StatsBase, BenchmarkTools
    
    julia> num_unique(x) = length(Set(x));
    
    julia> a = sample(1:100, 200);
    
    julia> num_unique(x) == length(unique(x))
    true
    
    julia> @benchmark length(unique(x)) setup=(x = sample(1:10000, 20000))
    BenchmarkTools.Trial: 
      memory estimate:  450.50 KiB
      allocs estimate:  36
      --------------
      minimum time:     498.130 μs (0.00% GC)
      median time:      570.588 μs (0.00% GC)
      mean time:        579.011 μs (2.41% GC)
      maximum time:     2.321 ms (63.03% GC)
      --------------
      samples:          5264
      evals/sample:     1
    
    julia> @benchmark num_unique(x) setup=(x = sample(1:10000, 20000))
    BenchmarkTools.Trial: 
      memory estimate:  288.68 KiB
      allocs estimate:  8
      --------------
      minimum time:     283.031 μs (0.00% GC)
      median time:      393.317 μs (0.00% GC)
      mean time:        397.878 μs (4.24% GC)
      maximum time:     33.499 ms (98.80% GC)
      --------------
      samples:          6704
      evals/sample:     1
    

    还有一个字符串数组的基准测试:

    julia> using Random
    
    julia> @benchmark length(unique(x)) setup=(x = [randstring(3) for _ in 1:10000])
    BenchmarkTools.Trial: 
      memory estimate:  450.50 KiB
      allocs estimate:  36
      --------------
      minimum time:     818.024 μs (0.00% GC)
      median time:      895.944 μs (0.00% GC)
      mean time:        906.568 μs (1.61% GC)
      maximum time:     1.964 ms (51.19% GC)
      --------------
      samples:          3049
      evals/sample:     1
    
    julia> @benchmark num_unique(x) setup=(x = [randstring(3) for _ in 1:10000])
    BenchmarkTools.Trial: 
      memory estimate:  144.68 KiB
      allocs estimate:  8
      --------------
      minimum time:     367.018 μs (0.00% GC)
      median time:      378.666 μs (0.00% GC)
      mean time:        384.486 μs (1.07% GC)
      maximum time:     1.314 ms (70.80% GC)
      --------------
      samples:          4527
      evals/sample:     1
    

    【讨论】:

    • 不错!,对于很多类型来说,计算数组唯一值最快的非变异方法似乎是length(Set(x))
    【解决方案3】:

    如果之后不需要 x 数组,length(unique!(x)) 会稍微快一些。 使用浮点数和整数,如果您的数组已经排序,则可以使用 map reduce。

    function count_unique_sorted(x)
        f(a) = (a,0)
        function op(a,b)
        if a[1] == b[1]
            return (b[1],a[2])
        else
            return (b[1],a[2]+1)
        end
        end
        return mapreduce(f,op,x)[2]+1 
    end
    

    如果不关心数组x的顺序,可以在一个函数中排序和计数:

    count_unique_sorted!(x)=count_unique_sorted(sort!(x))
    

    一些基准测试:

    using Random,StatsBase, BenchmarkTools
    x  = sample(1:100,200)
    length(unique(x)) == count_unique_sorted(sort(x)) #true
    

    使用length(unique(x))

    @benchmark length(unique(x))
    BenchmarkTools.Trial:
      memory estimate:  6.08 KiB
      allocs estimate:  17
      --------------
      minimum time:     3.350 μs (0.00% GC)
      median time:      3.688 μs (0.00% GC)
      mean time:        5.352 μs (24.35% GC)
      maximum time:     6.691 ms (99.90% GC)
      --------------
      samples:          10000
      evals/sample:     8
    

    使用Set

    @benchmark length(Set(x))
    BenchmarkTools.Trial:
      memory estimate:  2.82 KiB
      allocs estimate:  8
      --------------
      minimum time:     2.256 μs (0.00% GC)
      median time:      2.467 μs (0.00% GC)
      mean time:        3.654 μs (26.04% GC)
      maximum time:     5.297 ms (99.91% GC)
      --------------
      samples:          10000
      evals/sample:     9
    

    使用count_unique_sorted!

    x2 = copy(x)
    @benchmark count_unique_sorted!(x2)
    BenchmarkTools.Trial:
      memory estimate:  0 bytes
      allocs estimate:  0
      --------------
      minimum time:     948.387 ns (0.00% GC)
      median time:      990.323 ns (0.00% GC)
      mean time:        1.038 μs (0.00% GC)
      maximum time:     2.481 μs (0.00% GC)
      --------------
      samples:          10000
      evals/sample:     31
    

    count_unique_sorted 与已排序的数组一起使用

    x3 = sort(x)
    @benchmark count_unique_sorted(x3)
    BenchmarkTools.Trial:
      memory estimate:  0 bytes
      allocs estimate:  0
      --------------
      minimum time:     140.962 ns (0.00% GC)
      median time:      146.831 ns (0.00% GC)
      mean time:        154.121 ns (0.00% GC)
      maximum time:     381.806 ns (0.00% GC)
      --------------
      samples:          10000
      evals/sample:     852
    

    使用count_unique_sorted 并对数组进行排序

    @benchmark count_unique_sorted(sort(x))
    BenchmarkTools.Trial:
      memory estimate:  1.77 KiB
      allocs estimate:  1
      --------------
      minimum time:     1.470 μs (0.00% GC)
      median time:      1.630 μs (0.00% GC)
      mean time:        2.367 μs (21.82% GC)
      maximum time:     4.880 ms (99.94% GC)
      --------------
      samples:          10000
      evals/sample:     10
    

    对于字符串,排序和计数比创建 Set 慢。

    【讨论】:

    • 您对count_unique_sorted! 的基准不准确,因为在第一次评估中x 已排序,然后对于每个后续评估x 已排序。此外,使用更长的向量会使必须预先排序的成本更加明显。比较 x = sample(1:10_000, 20_000); @benchmark count_unique_sorted!(x)@benchmark count_unique_sorted!(x) setup=(x = sample(1:10_000, 20_000))。当您使用 setup 参数为每​​次评估提供一个新的未排序向量时,中位运行时间大约长 5 倍。
    猜你喜欢
    • 2019-07-06
    • 1970-01-01
    • 1970-01-01
    • 2018-12-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-03
    相关资源
    最近更新 更多