Julia base具有unique
函数,该函数返回仅包含数组的唯一元素(或任何可迭代)的向量。我正在寻找一个nonunique
函数来返回一个数组,该数组包含在其输入中至少出现两次的所有元素。据我所知,茱莉亚(Julia)没有这种功能,我感到有些惊讶。
我的第一次尝试如下:
function nonunique(x::AbstractArray)
uniqueindexes = indexin(unique(x),x)
nonuniqueindexes = setdiff(1:length(x),uniqueindexes)
unique(x[nonuniqueindexes])
end
但是受Bogumił Kamiński's对indices of unique elements of vector in Julia的回答的启发,我写了第二个版本:
function nonunique(x::AbstractArray{T}) where T
uniqueset = Set{T}()
duplicatedset = Set{T}()
duplicatedvector = Vector{T}()
for i in x
if(i in uniqueset)
if !(i in duplicatedset)
push!(duplicatedset, i)
push!(duplicatedvector, i)
end
else
push!(uniqueset, i)
end
end
duplicatedvector
end
在我的测试中,该版本的速度提高了约4倍。它具有不错的属性,即按每个等效元素集的第二个(第一个重复)最初出现的顺序对返回进行排序。我相信,在检查in
的成员资格时,Set
比Array
更快,因为duplicatedset
具有两个变量duplicatedvector
和nonunique
。>
我真的有必要“推出自己的” package-lock.json
函数吗,第二个版本可以改进吗?
答案 0 :(得分:5)
通过对列表进行排序,然后搜索重复项,可以获得更高的性能:
function nonunique2(x::AbstractArray{T}) where T
xs = sort(x)
duplicatedvector = T[]
for i=2:length(xs)
if (isequal(xs[i],xs[i-1]) && (length(duplicatedvector)==0 || !isequal(duplicatedvector[end], xs[i])))
push!(duplicatedvector,xs[i])
end
end
duplicatedvector
end
以下是示例结果:
julia> x = rand(1:1000,1000);
julia> using BenchmarkTools
julia> nn = @btime nonunique($x);
42.240 μs (39 allocations: 71.23 KiB)
julia> nn2s = @btime nonunique2($x);
26.453 μs (10 allocations: 16.33 KiB)
julia> sort(nn) == sort(nn2s)
true
如果可以进行就地排序会更好:
function nonunique2!(x::AbstractArray{T}) where T
sort!(x)
duplicatedvector = T[]
for i=2:length(x)
if (isequal(x[i],x[i-1]) && (length(duplicatedvector)==0 || !isequal(duplicatedvector[end], x[i])))
push!(duplicatedvector,x[i])
end
end
duplicatedvector
end
以下是结果(相同的数据)
julia> nn2 = @btime nonunique2!($x)
9.813 μs (9 allocations: 8.39 KiB)
julia> sort(nn) == sort(nns)
true
答案 1 :(得分:4)
要添加到上面的答案中,因为它的局限性是类型T
必须是可排序的,并且它不是保留顺序的,所以我有两种可能的解决方案。
这是另一个使用StatsBase.jl的非订单保留解决方案。它可以比排序解决方案更快或更慢,具体取决于重复项的密度(它还可以完成更多工作,但在某些应用中,此信息可能有用):
nonunique3(x) = [k for (k, v) in countmap(x) if v > 1]
如果您想加快订单保留方法,可以执行以下操作:
function nonunique4(x::AbstractArray{T}) where T
status = Dict{T, Bool}()
duplicatedvector = Vector{T}()
for i in x
if haskey(status, i)
if status[i]
push!(duplicatedvector, i)
status[i] = false
end
else
status[i] = true
end
end
duplicatedvector
end
通常,对它们进行基准测试比较棘手,因为性能取决于:
x
中重复项和重复项的密度T
类型的大小(例如,如果它是非常大的不可变类型,则与标准情况相比可能会发生变化)答案 2 :(得分:2)
并不是一个真正的答案(上面有出色的答案),但有一条评论可以将原始实现稍微清理一下:
function nonunique1(x::AbstractArray{T}) where T
uniqueset = Set{T}()
duplicatedset = Set{T}()
for i in x
if(i in uniqueset)
push!(duplicatedset, i)
else
push!(uniqueset, i)
end
end
collect(duplicatedset)
end
即您无需在推入集合之前检查是否存在,也无需填充向量和单独设置。它仍然不如排序实现快。