15  fv

Function spatstat.explore::fv() creates a function-value-table (fv.object), i.e., an R object of S3 class 'fv'.

Listing 15.1 summarizes the S3 methods for the class 'fv' in the spatstat.* family of packages,

Listing 15.1: Existing S3 methods spatstat.explore::*.fv
Code
suppressPackageStartupMessages(library(spatstat.explore))
.S3methods(class = 'fv', all.names = TRUE) |> 
  attr(which = 'info', exact = TRUE) |>
  subset.data.frame(subset = from == 'spatstat.explore')
#                  visible             from       generic  isS4
# [.fv                TRUE spatstat.explore             [ FALSE
# [<-.fv              TRUE spatstat.explore           [<- FALSE
# $<-.fv              TRUE spatstat.explore           $<- FALSE
# as.data.frame.fv    TRUE spatstat.explore as.data.frame FALSE
# as.function.fv      TRUE spatstat.explore   as.function FALSE
# as.fv.fv            TRUE spatstat.explore         as.fv FALSE
# cbind.fv            TRUE spatstat.explore         cbind FALSE
# collapse.fv         TRUE spatstat.explore      collapse FALSE
# compatible.fv       TRUE spatstat.explore    compatible FALSE
# Complex.fv          TRUE spatstat.explore       Complex FALSE
# deriv.fv            TRUE spatstat.explore         deriv FALSE
# formula.fv          TRUE spatstat.explore       formula FALSE
# formula<-.fv        TRUE spatstat.explore     formula<- FALSE
# harmonise.fv        TRUE spatstat.explore     harmonise FALSE
# harmonize.fv        TRUE spatstat.explore     harmonize FALSE
# integral.fv         TRUE spatstat.explore      integral FALSE
# Math.fv             TRUE spatstat.explore          Math FALSE
# names<-.fv          TRUE spatstat.explore       names<- FALSE
# Ops.fv              TRUE spatstat.explore           Ops FALSE
# pcf.fv              TRUE spatstat.explore           pcf FALSE
# plot.fv             TRUE spatstat.explore          plot FALSE
# pool.fv             TRUE spatstat.explore          pool FALSE
# print.fv            TRUE spatstat.explore         print FALSE
# rose.fv             TRUE spatstat.explore          rose FALSE
# Smooth.fv           TRUE spatstat.explore        Smooth FALSE
# StieltjesCalc.fv    TRUE spatstat.explore StieltjesCalc FALSE
# Summary.fv          TRUE spatstat.explore       Summary FALSE
# with.fv             TRUE spatstat.explore          with FALSE

The examples in Chapter 15 require that the search path contains the following namespaces,

library(groupedHyperframe)

Package groupedHyperframe (v0.3.2) implements more S3 methods to the class 'fv' (Table 15.1),

Table 15.1: S3 methods groupedHyperframe::*.fv (v0.3.2)
visible generic isS4
.disrecommend2theo.fv TRUE groupedHyperframe::.disrecommend2theo FALSE
.illegal2theo.fv TRUE groupedHyperframe::.illegal2theo FALSE
.rmax.fv TRUE groupedHyperframe::.rmax FALSE
cumvtrapz.fv TRUE groupedHyperframe::cumvtrapz FALSE
keyval.fv TRUE groupedHyperframe::keyval FALSE
visualize_vtrapz.fv TRUE groupedHyperframe::visualize_vtrapz FALSE

15.1 Function Value

Listing 15.2 creates a function-value-table spruces_k, which is the mark correlation of the point-pattern spruces (Section 10.17).

Listing 15.2: Data: function-value-table spruces_k
spruces_k = spatstat.data::spruces |> 
  spatstat.explore::markcorr()

Listing 15.3 visualizes the recommended function values of spruces_k (Listing 15.2) as a black-solid-curve (Figure 15.1) by calling the S3 method spatstat.explore::plot.fv().

Listing 15.3: Review: function spatstat.explore::plot.fv() (Baddeley, Rubak, and Turner 2015)
Code
par(mar = c(4, 4, 1, 1))
spruces_k |>
  spatstat.explore::plot.fv(main = NULL)
Figure 15.1: Mark Correlation of spruces

Listing 15.4 finds the columns in a function-value-table that are (Table 15.2),

Table 15.2: Function spatstat.explore::fvnames() (v3.6.0.1)
Abbreviation Finds
a = '.x' the function argument
a = '.y' the recommended function value
Listing 15.4: Review: function spatstat.explore::fvnames() (Baddeley, Rubak, and Turner 2015)
list(
  x = spruces_k |>
    spatstat.explore::fvnames(a = '.x'),
  y = spruces_k |>
    spatstat.explore::fvnames(a = '.y')
)
# $x
# [1] "r"
# 
# $y
# [1] "iso"

The S3 generic function keyval() finds various function values (default being the recommended) in a function-value-table, or an R object containing one or more function-value-tables. Package groupedHyperframe (v0.3.2) implements the following S3 methods (Table 15.3),

Table 15.3: S3 methods groupedHyperframe::keyval.* (v0.3.2)
visible isS4
keyval.fv TRUE FALSE
keyval.fvlist TRUE FALSE
keyval.hyperframe TRUE FALSE

The S3 method keyval.fv() finds various function values (default being the recommended) in the function-value-table, with the corresponding function argument as the vector names. Listing 15.5 finds the recommended function value in the function-value-table spruces_k (Listing 15.2).

Listing 15.5: Example: function keyval.fv()
spruces_k |>
  keyval() |>
  head(n = 3L)
#            0 0.0185546875  0.037109375 
#    0.8091085    0.8109143    0.8128058

Listing 15.6 finds the theoretical function value in the function-value-table spruces_k (Listing 15.2).

Listing 15.6: Example: function keyval.fv(., key = 'theo')
spruces_k |>
  keyval(key = 'theo') |>
  head(n = 3L)
#            0 0.0185546875  0.037109375 
#            1            1            1

15.2 Cumulative Average Vertical Height of Trapzoidal Integration

The S3 generic function cumvtrapz() has been introduced in Section 11.1 (Table 11.1). The S3 method cumvtrapz.fv() calculate the cumulative average vertical height of the trapezoidal integration (Section 11.1) under the recommended function values (Figure 15.2).

Listing 15.7: Figure: Visualize cumvtrapz of markcorr()
Code
spruces_k |>
  visualize_vtrapz(draw.rect = FALSE) + 
  ggplot2::theme_minimal()
Figure 15.2: Visualize cumvtrapz of markcorr()

15.3 \(r_\text{max}\)

The S3 generic function .rmax() has been introduced in Section 27.10 (Table 27.9). The S3 method .rmax.fv() (Listing 15.8), often used as an internal utility function, simply grabs the maximum value of the \(r\)-vector in a function-value-table.

Listing 15.8: Example: function .rmax.fv()
Code
sprucesK_r1 = spatstat.data::spruces |>
  spatstat.explore::Emark() |>
  .rmax.fv()
sprucesK_r2 = spatstat.data::spruces |>
  spatstat.explore::Emark() |>
  spatstat.explore::with.fv(expr = r) |>
  max()
stopifnot(identical(sprucesK_r1, sprucesK_r2))

15.4 Legal \(r_\text{max}\)

Function spatstat.explore::markcorr() is the workhorse inside functions Emark(), Vmark() and markvario() (v3.6.0.1). Function markcorr() relies on the un-exported workhorse function spatstat.explore:::sewsmod(), whose default method = "density" contains a ratio of two kernel density estimates. Due to the floating-point precision of R (Listing 15.9), such density ratios may have exceptional/illegal returns of

  • 0 from \(0/\delta\), or Inf from \(\delta/0\), with a real number \(\delta\geq\) (approximately) 2.6e-324
  • NaN from \(0/\varepsilon\) or \(\varepsilon/0\), with a real number \(\varepsilon\leq\) (approximately) 2.5e-324
Listing 15.9: Review: exceptional/illegal ratio due to floating-point precision (R Core Team 2025)
Code
list(
  0 / c(2.6e-324, 2.5e-324),
  c(2.5e-324, 2.6e-324) / 0
)
# [[1]]
# [1]   0 NaN
# 
# [[2]]
# [1] NaN Inf

Function spatstat.explore::markcorr() provides a default argument of parameter \(r\)-vector (Section 27.10), at which the mark correlation function \(k_f(r)\) are evaluated. The S3 method spatstat.explore::print.fv() (Listing 15.10) prints the recommended range (and available range) of \(r\)-vector.

Listing 15.10: Review: function spatstat.explore::print.fv() (Baddeley, Rubak, and Turner 2015)
Code
spruces_k |>
  spatstat.explore::print.fv()
# Function value object (class 'fv')
# for the function r -> k[mm](r)
# ................................................................................
#       Math.label              Description                                       
# r     r                       distance argument r                               
# theo  {k[mm]^{iid}}(r)        theoretical value (independent marks) for k[mm](r)
# trans {hat(k)[mm]^{trans}}(r) translation-corrected estimate of k[mm](r)        
# iso   {hat(k)[mm]^{iso}}(r)   Ripley isotropic correction estimate of k[mm](r)  
# ................................................................................
# Default plot formula:  .~r
# where "." stands for 'iso', 'trans', 'theo'
# Recommended range of argument r: [0, 9.5]
# Available range of argument r: [0, 9.5]
# Unit of length: 1 metre

Exceptional/illegal values of 0, Inf and/or NaN may appear in the mark correlation function \(k_f(r)\), if the \(r\)-vector goes well beyond the recommended range. The author constructs a malformed function-value-table fv_mal (Listing 15.11, Listing 15.12) to demonstrate various recovery procedures applicable in such cases.

Listing 15.11: Data: a malformed function-value-table fv_mal with \(r\)-vector out-of-range
fv_mal = spatstat.data::spruces |> 
  spatstat.explore::markcorr(r = 0:100)
Listing 15.12: Review: spatstat.explore::plot.fv() on fv_mal
Code
par(mar = c(4, 4, 1, 1))
fv_mal |> 
  spatstat.explore::plot.fv(xlim = c(0, 100), main = NULL)

The helper function lastLegal() (Listing 15.16, Listing 15.17) returns the index of the last consecutive legal values in a double (Listing 15.13) vector, i.e., the first exceptional/illegal value of 0, Inf, or NaN appears at the next index. The term “legal”, as in the function lastLegal(), is defined as

a double scalar being not-NA_real_, not-NaN, not-Inf, and with absolute value greater than .Machine$double.eps (Listing 15.14, Listing 15.15).

Listing 15.13: Advanced: NaN and Inf are double, not integer (R Core Team 2025)
Code
list(Inf, NaN) |> 
  vapply(FUN = typeof, FUN.VALUE = '')
# [1] "double" "double"
Listing 15.14: Advanced: base::is.finite()
Code
c(NA_real_, NaN, Inf) |>
  is.finite()
# [1] FALSE FALSE FALSE
Listing 15.16: Example: function lastLegal(), toy examples
Code
list(
  c(exp(1), pi),
  c(exp(1), pi, NaN),
  c(exp(1), pi, NaN, 1, 0, Inf)
) |> 
  lapply(FUN = lastLegal)
# [[1]]
# [1] 2
# attr(,"value")
# [1] 3.141593
# 
# [[2]]
# [1] 2
# attr(,"value")
# [1] 3.141593
# 
# [[3]]
# [1] 2
# attr(,"value")
# [1] 3.141593
Listing 15.17: Example: lastLegal() of keyval.fv()
spruces_k_lastLegal = fv_mal |>
  keyval.fv() |>
  lastLegal()
spruces_k_lastLegal
# [1] 75
# attr(,"value")
#       74 
# 1.549766

The term Legal \(r_\text{max}\) indicates (the index) of the \(r\)-vector, where the last of the consecutive legal recommended function values appears. In Listing 15.17, the last consecutive legal recommended-function-value of the malformed function-value-table fv_mal (Listing 15.11) of \(k_f(r)=1.550\) appears at the 75-th index of the \(r\)-vector, i.e., \(r=74\).

Legality of the function spatstat.explore::markcorr() returns depends not only on the input point-pattern, but also on the values of the \(r\)-vector. In other words, the creation of a function-value-table by function markcorr() is a numerical procedure. Therefore, the discussion of Legal \(r_\text{max}\) pertains to the function-value-table (fv.object, Chapter 15), instead of to the point-pattern (ppp.object, Chapter 27).

Example: Legality of spatstat.explore::markcorr() return depends on \(r\)-vector
spatstat.data::spruces |> 
  spatstat.explore::markcorr(r = seq.int(from = 0, to = 100, by = .1)) |>
  keyval.fv() |>
  lastLegal()
# [1] 742
# attr(,"value")
#      74.1 
# 0.3191326

15.4.1 Handling Illegal Recommended-Function-Value

The S3 generic functions .illegal2theo() and .disrecommend2theo() are exploratory approaches to remove the illegal recommended function values (Section 15.4) from a function-value-table. These approaches replace the recommended function values with the theoretical values starting at different locations in the function argument (Table 15.2, Listing 15.4), and return an updated function-value-table. Package groupedHyperframe (v0.3.2) implements the following S3 methods (Table 15.4, Table 15.5),

Table 15.4: S3 methods groupedHyperframe::.illegal2theo.* (v0.3.2)
visible isS4
.illegal2theo.fv TRUE FALSE
.illegal2theo.fvlist TRUE FALSE
.illegal2theo.hyperframe TRUE FALSE
Table 15.5: S3 methods groupedHyperframe::.disrecommend2theo.* (v0.3.2)
visible isS4
.disrecommend2theo.fv TRUE FALSE
.disrecommend2theo.fvlist TRUE FALSE
.disrecommend2theo.hyperframe TRUE FALSE

Function .illegal2theo.fv() (Figure 15.3) replaces the recommended function values after the first illegal \(r\) (Section 15.4) of the malformed function-value-table fv_mal (Listing 15.11) with its theoretical values.

Listing 15.18: Advanced: function .illegal2theo.fv()
par(mar = c(4, 4, 1, 1))
fv_mal |> 
  .illegal2theo() |>
  spatstat.explore::plot.fv(xlim = c(0, 100), main = NULL)
# r≥75.0 replaced with theo
Figure 15.3: Replaces with theoretical values after the first illegal \(r\)

Function .disrecommend2theo.fv() (Figure 15.4) replaces the recommended function values outside the recommended range attr(.,'alim')[2L] of the malformed function-value-table fv_mal (Listing 15.11) with its theoretical values.

Listing 15.19: Advanced: function .disrecommend2theo.fv()
par(mar = c(4, 4, 1, 1))
fv_mal |> 
  .disrecommend2theo() |>
  spatstat.explore::plot.fv(xlim = c(0, 100), main = NULL)
# r≥10.0 replaced with theo
Figure 15.4: Replaces with theoretical values outside the recommended range

15.5 Interpolation & Smoothing

The author uses the toy example of a “coarse” and a “fine” function-value-table (Listing 15.20, Listing 15.21) to illustrate various interpolation and smoothing methods of the \(x\)- and \(y\)-values (Table 15.2, Listing 15.4) in a function-value-table.

Listing 15.20: Data: coarse vs. fine \(r\)-vector
r_coarse = 0:9
r_fine = seq.int(from = 0, to = 9, by = .01)
Listing 15.21: Data: coarse vs fine fv.object
sprucesK_coarse = spatstat.data::spruces |>
  spatstat.explore::markcorr(r = r_coarse)
sprucesK_fine = spatstat.data::spruces |>
  spatstat.explore::markcorr(r = r_fine)

Function approxfun.fv() (Listing 15.22) performs a linear interpolation (Figure 15.5). This is a “pseudo” S3 method, as the workhorse function stats::approxfun() is not an S3 generic function.

Listing 15.22: Example: function approxfun.fv()
Code
sprucesK_coarse |>
  approxfun.fv() |>
  visualize_vtrapz(draw.rect = FALSE) +
  ggplot2::theme_minimal()
Figure 15.5: Linear Interpolation

Function splinefun.fv() (Listing 15.23) performs a spline interpolation (Figure 15.6). This is a “pseudo” S3 method, as the workhorse function stats::splinefun() is not an S3 generic function.

Listing 15.23: Example: function splinefun.fv()
Code
sprucesK_coarse |>
  splinefun.fv() |>
  visualize_vtrapz(draw.rect = FALSE) +
  ggplot2::theme_minimal()
Figure 15.6: Spline Interpolation

Function loess.fv() (Listing 15.24) performs a local polynomial regression fit (Figure 15.7). This is a “pseudo” S3 method, as the workhorse function stats::loess() is not an S3 generic function.

Listing 15.24: Example: function loess.fv()
Code
sprucesK_coarse |>
  loess.fv() |>
  visualize_vtrapz(draw.rect = FALSE) +
  ggplot2::theme_minimal()
Figure 15.7: Local Polynomial Regression, or LOESS

An experienced reader may wonder: is it truly advantageous to compute a coarse function-value-table and then perform interpolation and/or smoothing, rather than computing a fine function-value-table to start with? This is an excellent question! In fact, we observe no substantial difference in computation time via package microbenchmark (Mersmann 2024, v1.5.0) even when the grid of the \(r\)-vector is 100 times finer (Listing 15.25), as of package spatstat.explore (v3.6.0.1)! This observation justifies the use of the plain-and-naïve trapezoidal integration (Chapter 11, Section 11.1) on a fine fv.object (Figure 15.8 B), rather than employing more sophisticated numerical integration methods, e.g., the Simpson’s rule pracma::simpson(), the adaptive Simpson quadrature pracma::quad(), etc. on an interpolation and/or smoothing of a coarse fv.object (Figure 15.5, Figure 15.6, Figure 15.7).

Listing 15.25: Advanced: coarse vs fine fv.object, benchmarks
Code
suppressPackageStartupMessages(library(spatstat))
microbenchmark::microbenchmark(
  coarse = Emark(spruces, r = r_coarse),
  fine = Emark(spruces, r = r_fine)
) |>
  suppressWarnings()
# Unit: milliseconds
#    expr      min       lq     mean   median       uq       max neval cld
#  coarse 1.937086 1.954655 2.235100 1.966729 1.983723 20.019726   100   a
#    fine 2.270457 2.313979 2.503519 2.331116 2.362338  5.206918   100   a
Listing 15.26: Figure: Trapezoidal Integration, coarse vs. fine
Code
spruce_fig_coarse = sprucesK_coarse |>
  visualize_vtrapz(draw.rect = FALSE) +
  ggplot2::labs(title = '(A). Coarse')
spruce_fig_fine = sprucesK_fine |>
  visualize_vtrapz(draw.rect = FALSE) +
  ggplot2::labs(title = '(B). Fine')
(spruce_fig_coarse + spruce_fig_fine + patchwork::plot_layout(ncol = 2L)) & 
  ggplot2::theme_minimal()
Figure 15.8: Trapezoidal Integration, coarse vs. fine