如何在 Julia 数组中检查记录是否存在

4

假设我们有一个矩阵:

A = [1.0 2.0 3.0; 4.0 5.0 6.0] #2×3 Matrix{Float64}

还有一条记录:

b = [1.0 2.0 3.0] #1×3 Matrix{Float64}

什么是在Julia中检查记录b是否存在于矩阵A中最有效的方法?
在Julia中执行b in A会返回false。
当我们需要检查大型矩阵(许多维度和许多行)时,编写嵌套的for循环似乎效率低下。
4个回答

3
对于像你的示例这样的2D矩阵,检查很简单。
vec(b) in eachrow(A)
true

对于大型n维数组,可以使用

vec(b) in eachslice(A, dims=1)
true

2

如果矩阵中的行未排序,我会直接执行以下操作:

findfirst(==(vec(b)), eachrow(A))

如果行已经排序(当您多次搜索时建议这样做),我会考虑使用数组行视图的向量而不是searchsorted

2

这篇回答是在Przemyslaw Szufel和AboAmmar提供的建议基础上进行补充和拓展的,旨在提供一个效率比较的参考:

using BenchmarkTools

A = [1.0 2.0 3.0; 4.0 5.0 6.0]
b = [1.0 2.0 3.0]

function my_findfirst(A, b)
    if(findfirst(==(vec(b)), eachrow(A)) !== nothing)
        return true
    end
    return false
end



function my_eachslice(A, b)
    if vec(b) in eachslice(A, dims=1)
        return true
    end
    return false
end

function my_eachrow(A,b)
    if (vec(b) in eachrow(A))
        return true
    end
    return false
end

@btime my_findfirst($A, $b) #66.846 ns (2 allocations: 80 bytes)
@btime my_eachslice($A, $b) #305.164 ns (5 allocations: 144 bytes)
@btime my_eachrow($A, $b) # 66.846 ns (2 allocations: 80 bytes)

1
你正在比较错误的版本;vec(b) in eachrow(A)findfirst(==(vec(b)), eachrow(A)) 速度相同,但前者更加清晰易懂。我建议在 n 维数组中使用 eachslice - AboAmmar
是的,你说得对。我更新了这个答案以备将来参考。 - vikram-s-narayan

1
在表中查找某一行的成本取决于确切情况。在OP示例中,表很小且行在表中。通常,表更大,最坏的情况是行不在表中。基本上数据库针对这些操作进行了优化,索引是数据库查询优化的重要组成部分。
以下是对此问题其他答案的简单变体。它使用矩阵的第一列来过滤候选行以进一步进行相等性检查。除非是微不足道的情况,否则它将更快。这是因为在Julia默认矩阵内存布局中,通过列访问矩阵更快。
主要观点:如果必要,有很多优化的空间。
function my_scancolumnfirst(A, b)
  N = size(A,1)
  c = @view A[:,1]
  bv = vec(b)
  b1 = bv[1]
  @inbounds for i=1:N
    if c[i]==b1 && bv==@view A[i,:]
      return true
    end
  end
  return false
end

一个样例基准测试:

A = rand(500,500);
b = A[350,:];

using BenchmarkTools

@btime my_findfirst($A, $b)
# 2.682 μs (0 allocations: 0 bytes)
# true

@btime my_scancolumnfirst($A, $b)
#  907.333 ns (0 allocations: 0 bytes)
# true

自定义版本显示效果提高了>2倍。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接