Reference
Core.Array
ProportionalFitting.ArrayFactors
ProportionalFitting.ArrayMargins
ProportionalFitting.DimIndices
ProportionalFitting.adjust!
ProportionalFitting.default_dimindices
ProportionalFitting.ipf
Core.Array
— MethodArray(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
ProportionalFitting.ArrayFactors
— TypeArrayFactors(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 factorsdi::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
ProportionalFitting.ArrayMargins
— TypeArrayMargins(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 ofam
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]
ProportionalFitting.DimIndices
— TypeDimIndices(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]]
ProportionalFitting.adjust!
— Methodadjust!(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 adjustedAF::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
ProportionalFitting.default_dimindices
— Methoddefault_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]]
ProportionalFitting.ipf
— Methodipf(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 adjustedmar::ArrayMargins
: Target margins as an ArrayMargins objectmaxiter::Int=1000
: Maximum number of iterationsprecision::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 convergenceforce_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)