Skip to content

Commit fcb1f97

Browse files
authored
Update for version 0.4.0 (#26)
* Update documentation for new feature * apply blue formatting using vscode plugin with JuliaFormatter.jl * Bump version to 0.4.0
1 parent b23aed7 commit fcb1f97

File tree

12 files changed

+169
-101
lines changed

12 files changed

+169
-101
lines changed

.JuliaFormatter.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
style = "blue"

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ authors = [
44
"Erik-Jan van Kesteren <[email protected]>",
55
"James Alster <[email protected]>"
66
]
7-
version = "0.3.1"
7+
version = "0.4.0"
88

99
[deps]
1010

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
[![CI](https://github.com/vankesteren/ProportionalFitting.jl/actions/workflows/CI.yml/badge.svg)](https://github.com/vankesteren/ProportionalFitting.jl/actions/workflows/CI.yml)
44
[![devdoc](https://img.shields.io/badge/docs-dev-blue.svg)](https://vankesteren.github.io/ProportionalFitting.jl/dev)
55
[![stabledoc](https://img.shields.io/badge/docs-stable-blue.svg)](https://vankesteren.github.io/ProportionalFitting.jl/stable)
6+
[![Code Style: Blue](https://img.shields.io/badge/code%20style-blue-4495d1.svg)](https://github.com/JuliaDiff/BlueStyle)
67

78
Multidimensional iterative proportional fitting in Julia.
89

docs/make.jl

Lines changed: 16 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,31 @@
11
using ProportionalFitting
22
using Documenter
33

4-
DocMeta.setdocmeta!(ProportionalFitting, :DocTestSetup, :(using ProportionalFitting); recursive=true)
4+
DocMeta.setdocmeta!(
5+
ProportionalFitting, :DocTestSetup, :(using ProportionalFitting); recursive=true
6+
)
57

6-
frmt = Documenter.HTML(;
7-
prettyurls = get(ENV, "CI", "false") == "true",
8-
canonical = "https://vankesteren.github.io/ProportionalFitting.jl",
9-
edit_link = "main",
10-
assets = String[],
8+
frmt = Documenter.HTML(;
9+
prettyurls=get(ENV, "CI", "false") == "true",
10+
canonical="https://vankesteren.github.io/ProportionalFitting.jl",
11+
edit_link="main",
12+
assets=String[],
1113
)
1214

1315
pgs = [
1416
"Home" => "index.md",
1517
"Examples" => "examples.md",
1618
"Benchmarks" => "benchmarks.md",
17-
"Reference" => "reference.md"
19+
"Reference" => "reference.md",
1820
]
1921

2022
makedocs(;
21-
modules = [ProportionalFitting],
22-
authors = "Erik-Jan van Kesteren",
23-
repo = "https://github.com/vankesteren/ProportionalFitting.jl",
24-
sitename = "ProportionalFitting.jl",
25-
format = frmt,
26-
pages = pgs,
23+
modules=[ProportionalFitting],
24+
authors="Erik-Jan van Kesteren",
25+
repo="https://github.com/vankesteren/ProportionalFitting.jl",
26+
sitename="ProportionalFitting.jl",
27+
format=frmt,
28+
pages=pgs,
2729
)
2830

29-
deploydocs(;
30-
repo = "github.com/vankesteren/ProportionalFitting.jl",
31-
devbranch = "main",
32-
)
31+
deploydocs(; repo="github.com/vankesteren/ProportionalFitting.jl", devbranch="main")

docs/src/index.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,3 +116,38 @@ We then also use `ArrayMargins` to check whether the margins of this array are i
116116
```@example ex
117117
ArrayMargins(Z, dimid)
118118
```
119+
120+
## Repeated margins
121+
122+
With multidimensional margins, it is also possible to enter a single margin multiple times in different target margins. This is allowed as long as the margin totals match, and as long as no two target margins are about the exact same dimensions. For example:
123+
124+
```@example ex
125+
# Create a 2×3×4 array with seed value 1
126+
initial_array = ones(2, 3, 4)
127+
128+
# Specify target margins for dimensions 1 and 3
129+
tgt_13 = [
130+
10.0 15.0 20.0 25.0;
131+
30.0 35.0 40.0 45.0
132+
]
133+
134+
# Specify target margins for dimensions 2 and 3
135+
tgt_23 = [
136+
12.0 16.0 20.0 24.0;
137+
18.0 22.0 26.0 30.0;
138+
10.0 12.0 14.0 16.0
139+
]
140+
141+
# dimension 3 occurs twice!
142+
target_dims = [[1, 3], [2, 3]]
143+
target_margins = [tgt_13, tgt_23]
144+
145+
# at this point, the margins are checked for consistency
146+
AM = ArrayMargins(target_margins, target_dims)
147+
148+
# We can run ipf with these duplicated margins
149+
AF = ipf(initial_array, AM)
150+
151+
# check that the margins are as specified
152+
ArrayMargins(Array(AF), target_dims)
153+
```

src/ArrayFactors.jl

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,19 +55,23 @@ struct ArrayFactors{T}
5555
di::DimIndices
5656
size::Tuple
5757

58-
function ArrayFactors(af::Vector{<:AbstractArray{T}}, di::DimIndices) where T
58+
function ArrayFactors(af::Vector{<:AbstractArray{T}}, di::DimIndices) where {T}
5959
# loop over arrays then dimensions to get size, checking for mismatches
6060
dimension_sizes = zeros(Int, ndims(di))
6161
for i in 1:length(af)
6262
for (j, d) in enumerate(di.idx[i])
6363
new_size = size(af[i], j)
64-
if dimension_sizes[d] == 0
64+
if dimension_sizes[d] == 0
6565
dimension_sizes[d] = new_size
6666
continue
6767
end
6868
# check
6969
if dimension_sizes[d] != new_size
70-
throw(DimensionMismatch("Dimension sizes not equal for dimension $d: $(dimension_sizes[d]) and $new_size"))
70+
throw(
71+
DimensionMismatch(
72+
"Dimension sizes not equal for dimension $d: $(dimension_sizes[d]) and $new_size",
73+
),
74+
)
7175
end
7276
end
7377
end
@@ -79,7 +83,7 @@ end
7983
function ArrayFactors(af::Vector{<:AbstractArray}, di::DimIndices)
8084
AT = eltype(af)
8185
PT = promote_type(eltype.(af)...)
82-
ArrayFactors(Vector{AT{PT}}(af), di)
86+
return ArrayFactors(Vector{AT{PT}}(af), di)
8387
end
8488

8589
# Constructor promoting vector to dimindices

src/ArrayMargins.jl

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -54,19 +54,23 @@ struct ArrayMargins{T}
5454
size::Tuple
5555

5656
# loop to check dimension sizes
57-
function ArrayMargins(am::Vector{<:AbstractArray{T}}, di::DimIndices) where T
57+
function ArrayMargins(am::Vector{<:AbstractArray{T}}, di::DimIndices) where {T}
5858
# loop over arrays then dimensions to get size, checking for mismatches
5959
dimension_sizes = zeros(Int, ndims(di))
6060
for i in 1:length(am)
6161
for (j, d) in enumerate(di.idx[i])
6262
new_size = size(am[i], j)
63-
if dimension_sizes[d] == 0
63+
if dimension_sizes[d] == 0
6464
dimension_sizes[d] = new_size
6565
continue
6666
end
6767
# check
6868
if dimension_sizes[d] != new_size
69-
throw(DimensionMismatch("Dimension sizes not equal for dimension $d: $(dimension_sizes[d]) and $new_size"))
69+
throw(
70+
DimensionMismatch(
71+
"Dimension sizes not equal for dimension $d: $(dimension_sizes[d]) and $new_size",
72+
),
73+
)
7074
end
7175
end
7276
end
@@ -78,7 +82,7 @@ end
7882
function ArrayMargins(am::Vector{<:AbstractArray}, di::DimIndices)
7983
AT = eltype(am)
8084
PT = promote_type(eltype.(am)...)
81-
ArrayMargins(Vector{AT{PT}}(am), di)
85+
return ArrayMargins(Vector{AT{PT}}(am), di)
8286
end
8387

8488
# Constructor promoting vector to dimindices
@@ -91,14 +95,16 @@ ArrayMargins(am::Vector{<:AbstractArray}) = ArrayMargins(am, default_dimindices(
9195
function ArrayMargins(X::AbstractArray, di::DimIndices)
9296
D = ndims(X)
9397
if D != ndims(di)
94-
throw(DimensionMismatch("Dimensions of X ($(ndims(X))) mismatch DI ($(ndims(di)))."))
98+
throw(
99+
DimensionMismatch("Dimensions of X ($(ndims(X))) mismatch DI ($(ndims(di))).")
100+
)
95101
end
96102

97103
# create the margins
98104
am = Vector{AbstractArray{eltype(X)}}()
99105
for dim in di.idx
100106
notd = Tuple(setdiff(1:D, dim))
101-
mar = dropdims(sum(X; dims = notd); dims = notd)
107+
mar = dropdims(sum(X; dims=notd); dims=notd)
102108
if !issorted(dim)
103109
mar = permutedims(mar, sortperm(sortperm(dim)))
104110
end
@@ -123,8 +129,7 @@ Base.length(AM::ArrayMargins) = length(AM.am)
123129
Base.ndims(AM::ArrayMargins) = length(AM.size)
124130

125131
# method to align all arrays so each has dimindices 1:ndims(AM)
126-
function align_margins(AM::ArrayMargins{T})::Vector{Array{T}} where T
127-
132+
function align_margins(AM::ArrayMargins{T})::Vector{Array{T}} where {T}
128133
aligned_margins = Vector{Array{T}}()
129134

130135
for i in 1:length(AM)
@@ -145,7 +150,7 @@ function align_margins(AM::ArrayMargins{T})::Vector{Array{T}} where T
145150
end
146151

147152
# methods for consistency of margins
148-
function isconsistent(AM::ArrayMargins; tol::Float64 = eps(Float64))
153+
function isconsistent(AM::ArrayMargins; tol::Float64=eps(Float64))
149154
marsums = sum.(AM.am)
150155
return (maximum(marsums) - minimum(marsums)) < tol
151156
end
@@ -155,13 +160,16 @@ function proportion_transform(AM::ArrayMargins)
155160
return ArrayMargins(mar, AM.di)
156161
end
157162

158-
function margin_totals_match(AM::ArrayMargins; tol::Float64 = eps(Float64))
163+
function margin_totals_match(AM::ArrayMargins; tol::Float64=eps(Float64))
159164

160165
# get all shared subsets of dimensions
161-
shared_subsets = vcat(
166+
shared_subsets = unique(vcat(
162167
[[i] for i in 1:ndims(AM)], #Single dimensions
163-
collect(intersect(AM.di.idx[[i,j]]...) for i in 1:length(AM.di.idx) for j in i+1:length(AM.di.idx)) #Shared subsets
164-
) |> unique
168+
collect(
169+
intersect(AM.di.idx[[i, j]]...) for i in 1:length(AM.di.idx) for
170+
j in (i + 1):length(AM.di.idx)
171+
), #Shared subsets
172+
))
165173

166174
# loop over these subsets, and check marginal totals are equal in every array margin where they appear
167175
aligned_margins = align_margins(AM)
@@ -171,10 +179,10 @@ function margin_totals_match(AM::ArrayMargins; tol::Float64 = eps(Float64))
171179
for i in 1:length(AM.am)
172180
if issubset(dd, AM.di.idx[i])
173181
complement_dims = setdiff(1:ndims(AM), dd)
174-
push!(margin_totals, sum(aligned_margins[i]; dims = complement_dims))
182+
push!(margin_totals, sum(aligned_margins[i]; dims=complement_dims))
175183
end
176184
end
177-
if !all(x -> isapprox(x, margin_totals[1]; atol = tol), margin_totals)
185+
if !all(x -> isapprox(x, margin_totals[1]; atol=tol), margin_totals)
178186
@warn "Margin totals do not match across array margin(s): $dd"
179187
check = false
180188
end

src/DimIndices.jl

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,24 +25,24 @@ struct DimIndices
2525
# uniqueness between sets of indices
2626
if !allunique(sort.(idx))
2727
error("Some sets of dimensions were duplicated, e.g. [[1,2], [2,1]].")
28-
# uniqueness within sets of indices
28+
# uniqueness within sets of indices
2929
elseif !all(allunique, idx)
3030
error("Some dimensions were duplicated within a set, e.g. [[2,1,2], [3]].")
3131
end
3232
# completeness
3333
D = maximum(maximum.(idx))
3434
dmissing = setdiff(1:D, vcat(idx...))
35-
if length(dmissing) > 0
35+
if length(dmissing) > 0
3636
error("Missing array dimensions: $dmissing.")
3737
end
3838
return new(idx)
3939
end
4040
end
4141

4242
# convenient constructor
43-
DimIndices(X::Vector) = DimIndices(Vector{Union{Int, Vector{Int}}}(X))
44-
function DimIndices(X::Vector{Union{Int, Vector{Int}}})
45-
DimIndices(broadcast((x) -> [x...], X))
43+
DimIndices(X::Vector) = DimIndices(Vector{Union{Int,Vector{Int}}}(X))
44+
function DimIndices(X::Vector{Union{Int,Vector{Int}}})
45+
return DimIndices(broadcast((x) -> [x...], X))
4646
end
4747

4848
"""
@@ -74,7 +74,7 @@ function default_dimindices(m::Vector{<:AbstractArray})
7474
j = 0
7575
dd = []
7676
for i in nd
77-
push!(dd, collect((j+1):(j+i)))
77+
push!(dd, collect((j + 1):(j + i)))
7878
j += i
7979
end
8080
return DimIndices(dd)
@@ -90,7 +90,9 @@ function Base.show(io::IO, DI::DimIndices)
9090
print("[")
9191
for i in 1:length(DI)
9292
show(io, DI.idx[i])
93-
if i != length(DI) print(", ") end
93+
if i != length(DI)
94+
print(", ")
95+
end
9496
end
95-
print("]")
97+
return print("]")
9698
end

src/ProportionalFitting.jl

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
module ProportionalFitting
22

3-
export
4-
DimIndices,
5-
ArrayMargins, isconsistent, proportion_transform, align_arrays, margin_totals_match,
3+
export DimIndices,
4+
ArrayMargins,
5+
isconsistent,
6+
proportion_transform,
7+
align_arrays,
8+
margin_totals_match,
69
ArrayFactors,
710
ipf
811

912
include("DimIndices.jl")
1013
include("ArrayMargins.jl")
1114
include("ArrayFactors.jl")
12-
include("utils.jl")
1315
include("ipf.jl")
1416

1517
end # module

0 commit comments

Comments
 (0)