在julia中按公共列值合并数组

时间:2018-06-13 15:28:53

标签: julia array-merge

假设我们在Julia中有以下3个数组:

5.0 3.5 6.0 3.6 7.0 3.0

5.0 4.5 6.0 4.7 8.0 3.0

5.0 4.0 6.0 3.2 8.0 4.0

我想通过第一列的常用值合并一个数组中的3个数组,并将第二列的值相加。结果必须是以下数组:

5.0 12 6.0 11.5 7.0 3.0 8.0 7.0

我尝试vcatreduce,但我没有得到假装结果。是否有一种相对简单的方法来编写指令,避免耗时的代码?谢谢!

3 个答案:

答案 0 :(得分:3)

可能有很多方法可以做到这一点。如果您想避免编码,可以使用DataFrames包。这不是最快的解决方案,但它很短。

假设您将数组定义为变量:

x = [5.0  3.5
     6.0  3.6
     7.0  3.0]

y = [5.0  4.5
     6.0  4.7
     8.0  3.0]

z = [5.0  4.0
     6.0  3.2
     8.0  4.0]

然后你可以这样做:

using DataFrames
Matrix(aggregate(DataFrame(vcat(x,y,z)), :x1, sum))

:x1部分是因为默认情况下DataFrame的第一列被称为:x1,如果您没有为其指定明确的名称。在这个配方中,我们将矩阵转换为DataFrame聚合它们并将结果转换回矩阵。

答案 1 :(得分:3)

如果没有额外的包,可能的解决方案可能是

function aggregate(m::Array{<:Number,2}...)

    result=sortrows(vcat(m...))

    n = size(result,1)
    if n <= 1
        return result
    end 

    key_idx=1
    key=result[key_idx,1]

    for i in 2:n
      if key==result[i,1]
          result[key_idx,2:end] += result[i,2:end]
      else
          key = result[i,1]
          key_idx += 1
          result[key_idx,1]     = key 
          result[key_idx,2:end] = result[i,2:end]
      end
    end

    return result[1:key_idx,:]
end   

演示:

x = [5.0  3.5
     6.0  3.6
     7.0  3.0]

y = [5.0  4.5
     6.0  4.7
     8.0  3.0]

z = [5.0  4.0
     6.0  3.2
     8.0  4.0]

aggregate(x,y,z)

打印:

4×2 Array{Float64,2}:
 5.0  12.0
 6.0  11.5
 7.0   3.0
 8.0   7.0

注意:此解决方案也适用于任意数量的列

答案 2 :(得分:1)

鉴于以下两个假设:

  1. 每个输入数组的第一列都已排序,
  2. 每个输入数组的第一列是唯一的,
  3. 然后对于大多数输入组合(即输入数组的数量,数组的大小),以下算法应该通过利用假设显着优于其他答案:

    function f_ag(x::Matrix{T}...)::Matrix{T} where {T<:Number}
        isempty(x) && error("Empty input")
        any([ size(y,2) != 2 for y in x ]) && error("Input matrices must have two columns")
        length(x) == 1 && return copy(x[1]) #simple case shortcut
        nxmax = [ size(y,1) for y in x ]
        nxarrinds = find(nxmax .> 0)
        nxrowinds = ones(Int, length(nxarrinds))
        z = Tuple{T,T}[]
        while !isempty(nxarrinds)
            xmin = minimum(T[ x[nxarrinds[j]][nxrowinds[j], 1] for j = 1:length(nxarrinds) ])
            minarrinds = Int[ j for j = 1:length(nxarrinds) if x[nxarrinds[j]][nxrowinds[j], 1] == xmin ]
            rowsum = sum(T[ x[nxarrinds[k]][nxrowinds[k], 2] for k in minarrinds ])
            push!(z, (xmin, rowsum))
            for k in minarrinds
                nxrowinds[k] += 1
            end
            for j = length(nxarrinds):-1:1
                if nxrowinds[j] > nxmax[nxarrinds[j]]
                    deleteat!(nxrowinds, j)
                    deleteat!(nxarrinds, j)
                end
            end
        end
        return [ z[n][j] for n = 1:length(z), j = 1:2 ]
    end
    

    如果违反了假设2,也就是说,第一列不能保证是唯一的,你仍然可以利用排序顺序,但算法会再次变得更加复杂,因为你需要另外期待每个最小索引检查重复。在这一点上,我不会让自己度过这种痛苦。

    另请注意,您可以调整以下行:

    rowsum = sum(T[ x[nxarrinds[k]][nxrowinds[k], 2] for k in minarrinds ])
    

    到此:

    rowsum = input_func(T[ x[nxarrinds[k]][nxrowinds[k], 2:end] for k in minarrinds ])
    

    现在您可以输入您喜欢的任何功能,并在输入矩阵中添加任意数量的其他列。

    这里可能会添加一些额外的优化,例如预分配z,只有两个输入矩阵时的专门例程等,但我不打扰它们。