Skip to content

Commit e54eb11

Browse files
authored
Handle under-sampled neighborhoods in CoKriging (#25)
* Knock out missing indices in LHS and RHS * Fix predictmean in the presence of missing * Remove unnecessary branch in krigmean
1 parent 6f3934d commit e54eb11

File tree

2 files changed

+69
-11
lines changed

2 files changed

+69
-11
lines changed

src/krig.jl

+66-8
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ mutable struct KrigingState{D<:AbstractGeoTable,F,A}
2020
LHS::F
2121
RHS::A
2222
ncon::Int
23+
miss::Vector{Int}
2324
end
2425

2526
"""
@@ -51,21 +52,22 @@ status(fitted::FittedKriging) = issuccess(fitted.state.LHS)
5152

5253
function fit(model::KrigingModel, data)
5354
# initialize Kriging system
54-
LHS, RHS, ncon = initkrig(model, domain(data))
55+
LHS, RHS, ncon, miss = initkrig(model, data)
5556

5657
# factorize LHS
5758
FLHS = lhsfactorize(model, LHS)
5859

5960
# record Kriging state
60-
state = KrigingState(data, FLHS, RHS, ncon)
61+
state = KrigingState(data, FLHS, RHS, ncon, miss)
6162

6263
FittedKriging(model, state)
6364
end
6465

6566
# initialize Kriging system
66-
function initkrig(model::KrigingModel, domain)
67+
function initkrig(model::KrigingModel, data)
6768
fun = model.fun
68-
dom = domain
69+
dom = domain(data)
70+
tab = values(data)
6971

7072
# retrieve matrix parameters
7173
V, (_, nobs, nvar) = GeoStatsFunctions.matrixparams(fun, dom)
@@ -84,10 +86,16 @@ function initkrig(model::KrigingModel, domain)
8486
# set blocks of constraints
8587
lhsconstraints!(model, LHS, nvar, dom)
8688

89+
# find locations with missing values
90+
miss = missingindices(tab)
91+
92+
# knock out entries with missing values
93+
lhsmissings!(LHS, ncon, miss)
94+
8795
# pre-allocate memory for RHS
8896
RHS = similar(LHS, nrow, nvar)
8997

90-
LHS, RHS, ncon
98+
LHS, RHS, ncon, miss
9199
end
92100

93101
# choose appropriate factorization of LHS
@@ -122,6 +130,38 @@ end
122130
# no adjustments in case of general geostatistical functions
123131
lhsadjustments!(LHS, fun, dom) = nothing
124132

133+
# find locations with missing values
134+
function missingindices(tab)
135+
cols = Tables.columns(tab)
136+
vars = Tables.columnnames(cols)
137+
nvar = length(vars)
138+
139+
# find locations with missing values and
140+
# map to entries of blocks in final matrix
141+
entries = map(1:nvar) do j
142+
vals = Tables.getcolumn(cols, vars[j])
143+
inds = findall(ismissing, vals)
144+
[nvar * (i - 1) + j for i in inds]
145+
end
146+
147+
# sort indices to improve locality
148+
sort(reduce(vcat, entries))
149+
end
150+
151+
# knock out entries with missing values
152+
function lhsmissings!(LHS, ncon, miss)
153+
nrow = size(LHS, 1)
154+
nfun = nrow - ncon
155+
@inbounds for j in miss, i in 1:nfun
156+
LHS[i, j] = 0
157+
end
158+
@inbounds for j in 1:nfun, i in miss
159+
LHS[i, j] = 0
160+
end
161+
162+
nothing
163+
end
164+
125165
#-----------------
126166
# PREDICTION STEP
127167
#-----------------
@@ -159,18 +199,22 @@ function krigmean(fitted::FittedKriging, weights::KrigingWeights, vars)
159199
sum(1:n) do p
160200
λₚ = @view λ[p:k:end, j]
161201
zₚ = Tables.getcolumn(cols, vars[p])
162-
sum(i -> λₚ[i] * zₚ[i], eachindex(λₚ, zₚ))
202+
sum(i -> λₚ[i] ⦿ zₚ[i], eachindex(λₚ, zₚ))
163203
end
164204
end
165-
elseif k == 1
205+
else # k == 1
166206
@inbounds map(1:n) do p
167207
λₚ = @view λ[:, 1]
168208
zₚ = Tables.getcolumn(cols, vars[p])
169-
sum(i -> λₚ[i] * zₚ[i], eachindex(λₚ, zₚ))
209+
sum(i -> λₚ[i] ⦿ zₚ[i], eachindex(λₚ, zₚ))
170210
end
171211
end
172212
end
173213

214+
# handle missing values in linear combination
215+
⦿(λ, z) = λ * z
216+
⦿(λ, z::Missing) = 0
217+
174218
function predictvar(fitted::FittedKriging, weights::KrigingWeights, gₒ)
175219
RHS = fitted.state.RHS
176220
fun = fitted.model.fun
@@ -244,6 +288,7 @@ function weights(fitted::FittedKriging, gₒ)
244288
LHS = fitted.state.LHS
245289
RHS = fitted.state.RHS
246290
ncon = fitted.state.ncon
291+
miss = fitted.state.miss
247292
dom = domain(fitted.state.data)
248293
fun = fitted.model.fun
249294

@@ -259,6 +304,9 @@ function weights(fitted::FittedKriging, gₒ)
259304
# set blocks of constraints
260305
rhsconstraints!(fitted, gₒ′)
261306

307+
# knock out entries with missing values
308+
rhsmissings!(RHS, miss)
309+
262310
# solve Kriging system
263311
W = LHS \ RHS
264312

@@ -295,6 +343,16 @@ end
295343
# no adjustments in case of general geostatistical functions
296344
rhsadjustments!(RHS, fun, dom) = nothing
297345

346+
# knock out entries with missing values
347+
function rhsmissings!(RHS, miss)
348+
nvar = size(RHS, 2)
349+
@inbounds for j in 1:nvar, i in miss
350+
RHS[i, j] = 0
351+
end
352+
353+
nothing
354+
end
355+
298356
# the following functions are implemented by
299357
# all variants of Kriging (e.g., SimpleKriging)
300358
function nconstraints end

src/krig/simple.jl

+3-3
Original file line numberDiff line numberDiff line change
@@ -48,14 +48,14 @@ function krigmean(fitted::FittedKriging{<:SimpleKriging}, weights::KrigingWeight
4848
sum(1:n) do p
4949
λₚ = @view λ[p:k:end, j]
5050
zₚ = Tables.getcolumn(cols, vars[p])
51-
μ[p] + sum(i -> λₚ[i] * (zₚ[i] - μ[p]), eachindex(λₚ, zₚ))
51+
μ[p] + sum(i -> λₚ[i] ⦿ (zₚ[i] - μ[p]), eachindex(λₚ, zₚ))
5252
end
5353
end
54-
elseif k == 1
54+
else # k == 1
5555
@inbounds map(1:n) do p
5656
λₚ = @view λ[:, 1]
5757
zₚ = Tables.getcolumn(cols, vars[p])
58-
μ[p] + sum(i -> λₚ[i] * (zₚ[i] - μ[p]), eachindex(λₚ, zₚ))
58+
μ[p] + sum(i -> λₚ[i] ⦿ (zₚ[i] - μ[p]), eachindex(λₚ, zₚ))
5959
end
6060
end
6161
end

0 commit comments

Comments
 (0)