julia中的矢量化“in”功能?

时间:2015-04-15 21:31:56

标签: dataframe vectorization julia

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

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

在朱莉娅有没有任何矢量化(双向矢量?)方式?与基本操作符类似,我想做类似

的操作
isin = giant_list .in good_letters

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

5 个答案:

答案 0 :(得分:7)

indexin函数的功能与您想要的类似:

  

indexin(a, b)

     

ba的成员中的每个值返回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

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

答案 1 :(得分:4)

您可以使用unified broadcasting syntax在Julia v0.6中轻松地对in进行矢量化。

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

使用单元素元组注意good_letters的{​​{3}}。或者,您可以使用Scalar类型,例如StaticArrays.jl中引入的类型。

Julia v0.5支持相同的语法,但需要一个特殊的scalarificiation函数(或前面提到的Scalar类型):

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

之后

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

答案 2 :(得分:3)

针对此问题,有几种现代的解决方案(例如Julia v1.0):

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

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

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

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 :(得分:1)

findin()没有给你一个布尔掩码,但你可以很容易地使用它来为一个数组/ DataFrame配置另一个数组中包含的值:

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

答案 4 :(得分:0)

性能审核

其他答案都忽略了一个重要方面-性能。因此,让我简要回顾一下。为了使这一现实成为现实,我创建了两个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倍)-这真的没什么好写的。我认为这三个语法也不是很好,但是至少第一个解决方案对我来说比“性能解决方案”好。

这不是解决方案

这是这里

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