我经常想要遍历数据帧的长数组或列,并且对于每个项,看看它是否是另一个数组的成员。而不是做
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做类似但不知道基础中的任何内容。
答案 0 :(得分:7)
indexin
函数的功能与您想要的类似:
indexin(a, b)
为
b
中a
的成员中的每个值返回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