【问题标题】:Vectorized "in" function in julia?Julia中的矢量化“in”功能?
【发布时间】:2015-04-15 21:31:55
【问题描述】:

我经常想循环遍历数据框的长数组或列,并且对于每个项目,查看它是否是另一个数组的成员。而不是做

giant_list = ["a", "c", "j"]
good_letters = ["a", "b"]
isin = falses(size(giant_list,1))
for i=1:size(giant_list,1)
    isin[i] = giant_list[i] in good_letters
end

在 Julia 中是否有任何矢量化(双矢量化?)方法可以做到这一点?与基本运算符类比,我想做类似的事情

isin = giant_list .in good_letters

我意识到这可能是不可能的,但我只是想确保我没有遗漏任何东西。我知道我可能可以使用 DataStructures 中的 DefaultDict 来做类似的事情,但不知道基础中的任何内容。

【问题讨论】:

    标签: dataframe vectorization julia


    【解决方案1】:

    indexin 函数的作用与您想要的类似:

    indexin(a, b)

    对于a 中属于b 的每个值,返回一个包含b 中最高索引的向量。只要a 不是b 的成员,输出向量就包含0。

    由于您希望 giant_list 中的每个元素都有一个布尔值(而不是 good_letters 中的索引),您可以简单地执行以下操作:

    julia> indexin(giant_list, good_letters) .> 0
    3-element BitArray{1}:
      true
     false
     false
    

    implementation of indexin 非常简单明了,如果您不关心 b 中的索引,它指出了如何优化它:

    function vectorin(a, b)
        bset = Set(b)
        [i in bset for i in a]
    end
    

    只有有限的一组名称可以用作中缀运算符,因此不能将其用作中缀运算符。

    【讨论】:

    • 这适用于 DataArrays,尽管它不能很好地与 NA 一起使用(幸运的是,这对我来说并不重要)。
    【解决方案2】:

    有一些现代(即 Julia v1.0)解决方案可以解决这个问题:

    首先,更新标量策略。可以使用Ref 对象来实现标量广播,而不是使用 1 元素元组或数组:

    julia> in.(giant_list, Ref(good_letters))
    3-element BitArray{1}:
      true
     false
     false
    

    通过广播中缀 (\inTAB) 运算符可以实现相同的结果:

    julia> giant_list .∈ Ref(good_letters)
    3-element BitArray{1}:
      true
     false
     false
    

    此外,使用一个参数调用in 会创建一个Base.Fix2,稍后可以通过广播调用应用它。不过,与简单地定义一个函数相比,这似乎带来的好处有限。

    julia> is_good1 = in(good_letters);
           is_good2(x) = x in good_letters;
    
    julia> is_good1.(giant_list)
    3-element BitArray{1}:
      true
     false
     false
    
    julia> is_good2.(giant_list)
    3-element BitArray{1}:
      true
     false
     false
    

    总而言之,将.∈Ref 一起使用可能会得到最短、最干净的代码。

    【讨论】:

      【解决方案3】:

      您可以在 Julia v0.6 中使用 unified broadcasting syntax 非常轻松地矢量化 in

      julia> in.(giant_list, (good_letters,))
      3-element Array{Bool,1}:
        true
       false
       false
      

      使用单元素元组注意good_lettersscalarification。或者,您可以使用 Scalar 类型,例如 StaticArrays.jl 中介绍的类型。

      Julia v0.5 支持相同的语法,但需要一个专门的函数来进行标量化(或前面提到的 Scalar 类型):

      scalar(x) = setindex!(Array{typeof(x)}(), x)
      

      之后

      julia> in.(giant_list, scalar(good_letters))
      3-element Array{Bool,1}:
        true
       false
       false
      

      【讨论】:

      • 我想我在这里遗漏了一些东西:上面的命令和 0.5.0 中抛出错误的几个变体。
      • @ARM:啊,抱歉,我在 0.6.0 上进行了测试,确切的命令在那里工作。在 0.5.0 上,您需要一个零维数组(或 Scalar 类型)。
      • @ARM 我已经更新了我的解决方案,答案也适用于 0.5。
      • 注意:这适用于 Arrays 但不适用于 0.6 上的 DataArrays。 Matt B. 的回答对双方都有效。
      【解决方案4】:

      findin() 不会为您提供布尔掩码,但您可以轻松地使用它来为另一个数组中包含的值设置数组/DataFrame 的子集:

      julia> giant_list[findin(giant_list, good_letters)]
      1-element Array{String,1}:
       "a"
      

      【讨论】:

      • 请注意,findin 在 0.7 中已弃用并从 1.0 中删除。 findall(in(...), ...) 替换它。
      【解决方案5】:

      绩效考核

      其他答案忽略了一个重要方面 - 性能。所以,让我简要回顾一下。为了实现这一点,我创建了两个 Integer 向量,每个向量包含 100,000 个元素。

      using StatsBase
      
      a = sample(1:1_000_000, 100_000)
      b = sample(1:1_000_000, 100_000)
      

      为了知道什么是体面的表现,我在R做了同样的事情,导致4.4 ms的平均表现:

      # R code
      
      a <- sample.int(1000000, 100000)
      b <- sample.int(1000000, 100000)
      
      microbenchmark::microbenchmark(a %in% b)
      
      Unit: milliseconds
           expr     min       lq     mean   median       uq      max neval
       a %in% b 4.09538 4.191653 5.517475 4.376034 5.765283 65.50126   100
      

      高性能解决方案

      findall(in(b),a)
      
      5.039 ms (27 allocations: 3.63 MiB)
      

      R 慢,但也不是很多。然而,语法确实需要一些改进。

      性能不佳的解决方案

      a .∈ Ref(b)
      in.(a,Ref(b))
      findall(x -> x in b, a)
      
      3.879468 seconds (6 allocations: 16.672 KiB)
      3.866001 seconds (6 allocations: 16.672 KiB)
      3.936978 seconds (178.88 k allocations: 5.788 MiB)
      

      慢 800 倍(几乎比 R 慢 1000 倍)——这真的没什么好写的。在我看来,这三个的语法也不是很好,但至少第一个解决方案在我看来比“高性能解决方案”更好。

      is-not-a 解决方案

      这里是这个

      indexin(a,b)
      
      5.287 ms (38 allocations: 6.53 MiB)
      

      是高性能的,但对我来说这不是一个解决方案。它包含nothing 元素,其中该元素不在另一个向量中。在我看来,主要应用是对向量进行子集化,这不适用于此解决方案。

      a[indexin(b,a)]
      
      ERROR: ArgumentError: unable to check bounds for indices of type Nothing
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-27
        • 2019-12-02
        • 2019-05-21
        • 1970-01-01
        • 1970-01-01
        • 2022-01-17
        相关资源
        最近更新 更多