julia 中的向量化 "in" 函数?
Vectorized "in" function in 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
在 julia 中是否有任何矢量化(双重矢量化?)的方式来做到这一点?与基本运算符类比,我想做类似
的事情
isin = giant_list .in good_letters
我知道这可能是不可能的,但我只是想确保我没有遗漏任何东西。我知道我可能可以使用 DataStructures 中的 DefaultDict 来做类似的事情,但不知道 base 中的任何内容。
indexin
函数执行与您想要的类似的操作:
indexin(a, b)
Returns a vector containing the highest index in b
for each value in a
that is a member of b
. The output vector contains 0 wherever a
is not a member of b
.
由于您希望 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
只有有限的一组名称可以用作中缀运算符,因此不能将其用作中缀运算符。
您可以在 Julia v0.6 中使用 unified broadcasting syntax.
非常轻松地矢量化 in
julia> in.(giant_list, (good_letters,))
3-element Array{Bool,1}:
true
false
false
注意 good_letters
的 scalarification 使用单元素元组。或者,您可以使用 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
findin()
不会给你一个布尔掩码,但你可以很容易地使用它来为另一个数组中包含的值设置 array/DataFrame 的子集:
julia> giant_list[findin(giant_list, good_letters)]
1-element Array{String,1}:
"a"
这个问题有一些现代的(即 Julia v1.0)解决方案:
首先,更新标量策略。可以使用 Ref
对象来实现标量广播,而不是使用 1 元素元组或数组:
julia> in.(giant_list, Ref(good_letters))
3-element BitArray{1}:
true
false
false
同样的结果可以通过广播中缀 ∈
(\in
TAB) operator:
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
可能会导致最短、最干净的代码。
绩效评估
其他答案都忽略了一个重要方面——性能。那么,让我简要回顾一下。为了实现这一点,我创建了两个 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 倍)——这真的没什么好写的。在我看来,这三个的语法也不是很好,但至少第一个解决方案对我来说比 'performant solution'.
更好。
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
我经常想遍历一个长数组或数据框的一列,并且对于每个项目,看看它是否是另一个数组的成员。而不是做
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 来做类似的事情,但不知道 base 中的任何内容。
indexin
函数执行与您想要的类似的操作:
indexin(a, b)
Returns a vector containing the highest index in
b
for each value ina
that is a member ofb
. The output vector contains 0 wherevera
is not a member ofb
.
由于您希望 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
只有有限的一组名称可以用作中缀运算符,因此不能将其用作中缀运算符。
您可以在 Julia v0.6 中使用 unified broadcasting syntax.
非常轻松地矢量化in
julia> in.(giant_list, (good_letters,))
3-element Array{Bool,1}:
true
false
false
注意 good_letters
的 scalarification 使用单元素元组。或者,您可以使用 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
findin()
不会给你一个布尔掩码,但你可以很容易地使用它来为另一个数组中包含的值设置 array/DataFrame 的子集:
julia> giant_list[findin(giant_list, good_letters)]
1-element Array{String,1}:
"a"
这个问题有一些现代的(即 Julia v1.0)解决方案:
首先,更新标量策略。可以使用 Ref
对象来实现标量广播,而不是使用 1 元素元组或数组:
julia> in.(giant_list, Ref(good_letters))
3-element BitArray{1}:
true
false
false
同样的结果可以通过广播中缀 ∈
(\in
TAB) operator:
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
可能会导致最短、最干净的代码。
绩效评估
其他答案都忽略了一个重要方面——性能。那么,让我简要回顾一下。为了实现这一点,我创建了两个 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 倍)——这真的没什么好写的。在我看来,这三个的语法也不是很好,但至少第一个解决方案对我来说比 'performant solution'.
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