Reference

Core.ArrayMethod
Array(AF::ArrayFactors{T})

Create an array out of an ArrayFactors object.

Arguments

  • AF::ArrayFactors{T}: Array factors

Examples

julia> AF = ArrayFactors([[1,2,3], [4,5], [6,7]])
Factors for array of size (3, 2, 2):
    1: [1, 2, 3]
    2: [4, 5]
    3: [6, 7]

julia> Array(AF)
3×2×2 Array{Int64, 3}:
[:, :, 1] =
 24  30
 48  60
 72  90

[:, :, 2] =
 28   35
 56   70
 84  105
source
ProportionalFitting.ArrayFactorsType
ArrayFactors(af::Vector{<:AbstractArray}, di::DimIndices)
ArrayFactors(af::Vector{<:AbstractArray}, di::Vector)
ArrayFactors(af::Vector{<:AbstractArray})

Array factors are defined such that the array's elements are their products: M[i, j, ..., l] = af[1][i] * af[2][j] * ... * af[3][l].

The array factors can be vectors or multidimensional arrays themselves.

The main use of ArrayFactors is as a memory-efficient representation of a multidimensional array, which can be constructed using the Array() method. However, to perform elementwise multiplication of this array with another array X of the same size, it is more efficient not instantiate the full array. Instead, call adjust!(X, AF).

see also: ipf, ArrayMargins, DimIndices, adjust!

Fields

  • af::Vector{<:AbstractArray}: Vector of (multidimensional) array factors
  • di::DimIndices: Dimension indices to which the array factors belong.

Examples

julia> AF = ArrayFactors([[1,2,3], [4,5]])
Factors for array of size (3, 2):
  [1]: [1, 2, 3]
  [2]: [4, 5]

julia> eltype(AF)
Int64

julia> Array(AF)
3×2 Matrix{Int64}:
  4   5
  8  10
 12  15

julia> AF = ArrayFactors([[1,2,3], [4 5; 6 7]], DimIndices([2, [1, 3]]))
Factors for 3D array:
  [2]: [1, 2, 3]
  [1, 3]: [4 5; 6 7]

julia> Array(AF)
2×3×2 Array{Int64, 3}:
[:, :, 1] =
 4   8  12
 6  12  18

[:, :, 2] =
 5  10  15
 7  14  21
source
ProportionalFitting.ArrayMarginsType
ArrayMargins(am::Vector{<:AbstractArray}, di::DimIndices)
ArrayMargins(am::Vector{<:AbstractArray}, di::Vector)
ArrayMargins(am::Vector{<:AbstractArray})
ArrayMargins(X::AbstractArray, di::DimIndices)
ArrayMargins(X::AbstractArray)

ArrayMargins are marginal sums of an array, combined with the indices of the dimensions these sums belong to. The marginal sums can be multidimensional arrays themselves.

There are various constructors for ArrayMargins, based on either raw margins or an actual array from which the margins are then computed.

see also: DimIndices, ArrayFactors, ipf

Fields

  • am::Vector{AbstractArray}: Vector of marginal sums.
  • di::DimIndices: Dimension indices to which the elements of am belong.

Examples

julia> X = reshape(1:12, 2, 3, 2)
2×3×2 reshape(::UnitRange{Int64}, 2, 3, 2) with eltype Int64:
[:, :, 1] =
 1  3  5
 2  4  6

[:, :, 2] =
 7   9  11
 8  10  12

julia> ArrayMargins(X)
Margins from 3D array:
  [1]: [36, 42]
  [2]: [18, 26, 34]
  [3]: [21, 57]

julia> ArrayMargins(X, [1, [2, 3]])
Margins from 3D array:
  [1]: [36, 42]
  [2, 3]: [3 15; 7 19; 11 23]

julia> ArrayMargins(X, [2, [3, 1]])
Margins of 3D array:
  [2]: [18, 26, 34]
  [3, 1]: [9 12; 27 30]
source
ProportionalFitting.DimIndicesType
DimIndices(idx::Vector{Vector{Int}})

DimIndices represent an exhaustive list of indices for the dimensions of an array. It is an object containing a single element, idx, which is a nested vector of integers; e.g., [[2], [1, 3], [4]]. DimIndices objects are checked for uniqueness and completeness, i.e., all indices up to the largest index are used exactly once.

Fields

  • idx::Vector{Vector{Int}}: nested vector of dimension indices.

Examples

julia> DimIndices([2, [1, 3], 4])
Indices for 4D array:
[[2], [1, 3], [4]]
source
ProportionalFitting.adjust!Method
adjust!(X::AbstractArray{T}, AF::ArrayFactors{U})

Adjust a seed array with respect to a set of ArrayFactors. The adjustment happens through elementwise multiplication of the array by each factor, taking into account the dimensions that this factor belongs to. This performs the same operation as X .* Array(AF), but faster and more memory-efficient.

Arguments

  • X::AbstractArray: Seed array to be adjusted
  • AF::ArrayFactors: Array factors

Details

Note that this will fail if the seed array is integer and the array factors are not. This will result in an InexactError. The solution is to use X .* Array(AF) or to first convert the seed matrix to contain floats: X = convert(AbstractArray{Float64}, X).

Examples

julia> AF = ArrayFactors([[1, 2, 3, 4], [.1, .2, .3, .4]])
Factors for 2D array:
  [1]: [1.0, 2.0, 3.0, 4.0]
  [2]: [0.1, 0.2, 0.3, 0.4]

julia> X = [40 30 20 10; 35 50 100 75; 30 80 70 120; 20 30 40 50]
4×4 Matrix{Int64}:
 40  30   20   10
 35  50  100   75
 30  80   70  120
 20  30   40   50

julia> adjust!(X, AF)
julia> X
4×4 Matrix{Int64}:
 4   6   6    4
 7  20  60   60
 9  48  63  144
 8  24  48   80
source
ProportionalFitting.default_dimindicesMethod
default_dimindices(m::Vector{<:AbstractArray})

Create default dimensions from a vector of arrays. These dimensions are assumed to be ordered. For example, for the dimensions will be [[1], [2], [3]]. For [[1, 2], [2 1 ; 3 4]], it will be [[1], [2, 3]].

See also: DimIndices

Arguments

  • m::Vector{<:AbstractArray}: Array margins or factors.

Examples

julia> default_dimindices([[1, 2], [2, 1], [3, 4]])
Indices for 3D array:
    [[1], [2], [3]]

julia> default_dimindices([[1, 2], [2 1 ; 3 4]])
Indices for 3D array:
    [[1], [2, 3]]
source
ProportionalFitting.ipfMethod
ipf(X::AbstractArray{<:Real}, mar::ArrayMargins; maxiter::Int = 1000, tol::AbstractFloat = 1e-10; force_consistency::Bool=false)
ipf(X::AbstractArray{<:Real}, mar::Vector{<:Vector{<:Real}})
ipf(mar::ArrayMargins)
ipf(mar::Vector{<:Vector{<:Real}})

Perform iterative proportional fitting (factor method). The array (X) can be any number of dimensions, and the margins can be multidimensional as well. If only the margins are given, then the seed array X is assumed to be an array filled with ones of the correct size and element type.

If the margins are not an ArrayMargins object, they will be coerced to this type.

This function returns the update matrix as an ArrayFactors object. To compute the updated matrix, use Array(result) .* X (see examples).

If the margin arrays do not have the same total, the input array and margins will be converted to proportions. In this case, the updated matrix should be accessed with Array(result) .* (X / sum(X)).

If dimensions repeated across the margin arrays do not have identical totals, an error is thrown by default. Passing force_consistency=true will run ipf to converge on the mean of the inconsistent margins. Note that this will lead to outputs that will not match the specified margins.

If decreasing memory usage is a concern, it is possible to set precision to be lower than Float64. It is also possible to decrease memory usage (by up to almost 50%) by supplying X as an an object of type Array{precision}.

see also: ArrayFactors, ArrayMargins

Arguments

  • X::AbstractArray{<:Real}: Seed array to be adjusted
  • mar::ArrayMargins: Target margins as an ArrayMargins object
  • maxiter::Int=1000: Maximum number of iterations
  • precision::DataType=Float64: The numeric precision to which calculations are carried out. Note that there is no bounds checking, however. Must be <:AbstractFloat.
  • tol::AbstractFloat=1e-10: Factor change tolerance for convergence
  • force_consistency::Bool=false: If dimensions repeated across margin arrays have inconsistent totals, converge to the mean (true) or throw an error (false)?

Examples

julia> X = [40 30 20 10; 35 50 100 75; 30 80 70 120; 20 30 40 50]
julia> u = [150, 300, 400, 150]
julia> v = [200, 300, 400, 100]
julia> AF = ipf(X, [u, v])
Factors for 2D array:
    [1]: [0.9986403503185242, 0.8833622306385376, 1.1698911437112522, 0.8895042701910321]
    [2]: [1.616160156063788, 1.5431801747375655, 1.771623700829941, 0.38299396265192226]

julia> X_adj = X .* Array(AF)
4×4 Matrix{Float64}:
 64.5585   46.2325   35.3843   3.82473
 49.9679   68.1594  156.499   25.3742
 56.7219  144.428   145.082   53.7673
 28.7516   41.18     63.0347  17.0337

julia> ArrayMargins(X_adj)
Margins of 2D array:
  [1]: [150.0000000009452, 299.99999999962523, 399.99999999949796, 149.99999999993148]
  [2]: [200.0, 299.99999999999994, 399.99999999999994, 99.99999999999997]

If you have a large seed array to be adjusted, it is much more memory-efficient and much faster to directly run the in-place adjust!() method to avoid copying the array:

julia> X = rand(500, 100, 20) .* 10
julia> AM = ArrayMargins(X + rand(500, 100, 20) ./ 10)
julia> AF = ipf(X, AM)
julia> adjust!(X, AF)
source