如何在 Julia 中对多维数组的列使用 searchsorted?

How to use searchsorted on a column of a multidimensional array in Julia?

我有一个这样的排序二维数组:

testv = sortrows(["zebra" "N" 20; "jimi" "V" 100; "johnny" "V" 200; "pete" "P" 33; "jimi" "N" 20])

现在我基本上想检查是否有类似

的东西
"jimi" "N" x

在数组中。如果是,我将增加 x,否则我将添加 "jimi" "N" 1 到数组。

执行此操作的懒惰方法只是遍历数组检查所有条目,或者编写我自己的二进制搜索,但我保持数组排序以尽量减少成本。如果有办法让我 searchsorted 为我做这件事,那就太理想了。

如果我可以在 "jimi" 上使用 searchsorted 也很好 - 我的理解是(如果有效的话)它会 return 一个 [=15] 的范围=] 出现在排序数组中?那很好,数组的第二列来自一个足够小的域,我不介意以缓慢的方式循环。

base julia 中没有很多函数可以处理二维矩阵的行(除了 sortrows - 请参阅 apropos("rows") 的输出)。这部分是因为具有不同类型列的矩阵表现不佳,部分是因为有一些很好的选择。这里有几个选项可能更适合您。

元组向量

通过使用元组向量而不是矩阵,您可以获得更好的性能和搜索、增长、插入、删除等的能力。它还确保每个 "row"(现在只是一个元素 - 一个元组)具有正确的类型。

julia> A = [("zebra", "N", 20), ("jimi", "V", 100), ("johnny", "V", 200), ("pete", "P", 33), ("jimi", "N", 20)];

julia> sort!(A) # Sort A in-place
5-element Array{(ASCIIString,ASCIIString,Int64),1}:
 ("jimi","N",20)
 ("jimi","V",100)
 ("johnny","V",200)
 ("pete","P",33)
 ("zebra","N",20)

julia> idxs = searchsorted(A, ("matt", "B", 100))
4:3

julia> isempty(idxs) && splice!(A, idxs, [("matt", "B", 100)])
0-element Array{(ASCIIString,ASCIIString,Int64),1}

julia> A
6-element Array{(ASCIIString,ASCIIString,Int64),1}:
 ("jimi","N",20)
 ("jimi","V",100)
 ("johnny","V",200)
 ("matt","B",100)
 ("pete","P",33)
 ("zebra","N",20)

自定义类型的向量

如果您要经常使用此类数据,另一种更有吸引力的选择是创建自定义类型。设置需要更多的工作,但它随后还允许您创建自己的方法来处理此类数据。

immutable MyData
    name::UTF8String
    initial::UTF8String
    score::Int
end
# We have to tell Julia how to sort these types by defining isless
function Base.isless(a::MyData, b::MyData)
    isless((a.name, a.initial, a.score), (b.name, b.initial, b.score))
end

julia> B = [MyData("zebra", "N", 20), MyData("jimi", "V", 100), MyData("johnny", "V", 200), MyData("pete", "P", 33), MyData("jimi", "N", 20)]

julia> sort!(B)
5-element Array{MyData,1}:
 MyData("jimi","N",20)
 MyData("jimi","V",100)
 MyData("johnny","V",200)
 MyData("pete","P",33)
 MyData("zebra","N",20)

julia> idxs = searchsorted(B, MyData("matt", "B", 100))
julia> isempty(idxs) && splice!(B, idxs, [MyData("matt", "B", 100)])

一种方法是使用字典。也许这种方法在某种程度上回避了问题的字面意义,但它非常符合提供易于高效递增的集合的目标。

julia> VERSION
v"0.3.5"

julia> testv = ["zebra" "N" 20; "jimi" "V" 100; "johnny" "V" 200; "pete" "P" 33; "jimi" "N" 20]

创建一个字典,其键由字符串对构成,值是总计。请注意,顺序与操作数据无关,但可以很容易地对数据进行排序以进行展示

julia> dv = {[testv[i,1], testv[i,2]] => testv[i,3] for i in 1:size(testv)[1]}
Dict{Any,Any} with 5 entries:
  ASCIIString["zebra","N"]  => 20
  ASCIIString["pete","P"]   => 33
  ASCIIString["jimi","N"]   => 20
  ASCIIString["jimi","V"]   => 100
  ASCIIString["johnny","V"] => 200

julia> for k in sort(collect(keys(dv)), by=(x -> join(x,"")))
           println(k[1], " ", k[2], " ", dv[k])
       end
jimi N 20
jimi V 100
johnny V 200
pete P 33
zebra N 20

根据需要增加集合

julia> k = ["mike", "S"]
julia> get!(dv, k, 0)
julia> dv[k] += 1
julia> k = ["matt", "B"]
julia> get!(dv, k, 0)
julia> dv[k] += 1
julia> k = ["jimi","N"]
julia> get!(dv, k, 0)
julia> dv[k] += 1

然后你有

julia> for k in sort(collect(keys(dv)), by=(x -> join(x,"")))
           println(k[1], " ", k[2], " ", dv[k])
       end
jimi N 21
jimi V 100
johnny V 200
matt B 1
mike S 1
pete P 33
zebra N 20

当然,还有很多可以添加到其中的润色,但我认为我已经展示了基本方法。如果您还有其他具体问题,我很乐意详细说明。

我应该告诉您如何获得您开始使用的那种数组:

julia> testv = [[k[1], k[2], dv[k]] for k in sort(collect(keys(dv)), by=(x -> join(x,"")))]

julia> testv = [i[j] for i in tv, j in 1:3]
7x3 Array{Any,2}:
 "jimi"    "N"   21
 "jimi"    "V"  100
 "johnny"  "V"  200
 "matt"    "B"    1
 "mike"    "S"    1
 "pete"    "P"   33
 "zebra"   "N"   20