24  ppplist from solist,anylist

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

library(groupedHyperframe)

Function spatstat.geom::solist() returns an R object of S3 class 'ppplist', if all of the input objects are two-dimensional point-pattern objects (ppp.object, Chapter 23). The S3 class 'ppplist' inherits from the classes 'solist' (Chapter 25), 'anylist' (Chapter 11), 'listof' and 'list'. In addition to the existing S3 method dispatches spatstat.geom::*.ppplist (v3.6.0.3, Listing 24.1) and spatstat.explore::*.ppplist (v3.5.3.3, Listing 24.2),

Listing 24.1: Existing S3 method dispatches spatstat.geom::*.ppplist
Code
suppressPackageStartupMessages(library(spatstat.geom))
methods(class = 'ppplist', all.names = TRUE) |> 
  attr(which = 'info', exact = TRUE) |>
  subset.data.frame(subset = from == 'spatstat.geom')
#                       visible          from       generic  isS4
# as.data.frame.ppplist    TRUE spatstat.geom as.data.frame FALSE
# superimpose.ppplist      TRUE spatstat.geom   superimpose FALSE
Listing 24.2: Existing S3 method dispatches spatstat.explore::*.ppplist
Code
suppressPackageStartupMessages(library(spatstat.explore))
methods(class = 'ppplist', all.names = TRUE) |> 
  attr(which = 'info', exact = TRUE) |>
  subset.data.frame(subset = from == 'spatstat.explore')
#                               visible             from               generic  isS4
# density.ppplist                  TRUE spatstat.explore               density FALSE
# densityAdaptiveKernel.ppplist    TRUE spatstat.explore densityAdaptiveKernel FALSE

Package groupedHyperframe (v0.3.0.20251020) implements more S3 method dispatches to the class 'ppplist' (Listing 24.3, Table 24.1),

Listing 24.3: Table: S3 method dispatches groupedHyperframe::*.ppplist
Code
methods2kable(class = 'ppplist', package = 'groupedHyperframe', all.names = TRUE)
Table 24.1: S3 method dispatches groupedHyperframe::*.ppplist (v0.3.0.20251020)
visible from generic isS4
.kmeans.ppplist TRUE groupedHyperframe groupedHyperframe::.kmeans FALSE
.rmax.ppplist TRUE groupedHyperframe groupedHyperframe::.rmax FALSE
aggregate_marks.ppplist TRUE groupedHyperframe groupedHyperframe::aggregate_marks FALSE
density_marks.ppplist TRUE groupedHyperframe groupedHyperframe::density_marks FALSE
Emark_.ppplist TRUE groupedHyperframe groupedHyperframe::Emark_ FALSE
Gcross_.ppplist TRUE groupedHyperframe groupedHyperframe::Gcross_ FALSE
Jcross_.ppplist TRUE groupedHyperframe groupedHyperframe::Jcross_ FALSE
Kcross_.ppplist TRUE groupedHyperframe groupedHyperframe::Kcross_ FALSE
kerndens.ppplist TRUE groupedHyperframe groupedHyperframe::kerndens FALSE
Kmark_.ppplist TRUE groupedHyperframe groupedHyperframe::Kmark_ FALSE
Lcross_.ppplist TRUE groupedHyperframe groupedHyperframe::Lcross_ FALSE
markconnect_.ppplist TRUE groupedHyperframe groupedHyperframe::markconnect_ FALSE
markcorr_.ppplist TRUE groupedHyperframe groupedHyperframe::markcorr_ FALSE
markvario_.ppplist TRUE groupedHyperframe groupedHyperframe::markvario_ FALSE
nncross_.ppplist TRUE groupedHyperframe groupedHyperframe::nncross_ FALSE
pairwise_cor_spatial.ppplist TRUE groupedHyperframe groupedHyperframe::pairwise_cor_spatial FALSE
quantile.ppplist TRUE groupedHyperframe stats::quantile FALSE
Vmark_.ppplist TRUE groupedHyperframe groupedHyperframe::Vmark_ FALSE
Table 24.2: S3 method dispatches currently not planned for class 'ppplist'
Table 24.2: S3 method dispatches currently not planned for class 'ppplist'
Not Planned Explained in
unmark.ppplist() Section 29.1

24.1 Kernel Density of numeric-marks

The S3 generic function density_marks() has been introduced in Section 23.4.

The S3 method dispatch density_marks.ppplist() is a batch process of the S3 method dispatch density_marks.ppp() (@sec-densitymarks_ppp), which finds the kerneldensitys of allnumeric-marksin eachppp.objectin the inputppplist`.

Listing 24.4 finds the kernel densitys of all numeric-marks of the split-ted betacells (Section 8.4).

Listing 24.4: Example: function density_marks.ppplist()
Code
spatstat.data::betacells |>
  spatstat.geom::split.ppp(f = 'type') |>
  density_marks()
# $off
# $off$area
# 
# Call:
#   density.default(x = `$area`)
# 
# Data: $area (70 obs.);    Bandwidth 'bw' = 11.44
# 
#        x               y            
#  Min.   :134.0   Min.   :6.323e-06  
#  1st Qu.:199.1   1st Qu.:9.940e-04  
#  Median :264.2   Median :3.102e-03  
#  Mean   :264.2   Mean   :3.832e-03  
#  3rd Qu.:329.3   3rd Qu.:5.410e-03  
#  Max.   :394.4   Max.   :1.184e-02  
# 
# 
# $on
# $on$area
# 
# Call:
#   density.default(x = `$area`)
# 
# Data: $area (65 obs.);    Bandwidth 'bw' = 23.46
# 
#        x               y            
#  Min.   :137.5   Min.   :2.919e-06  
#  1st Qu.:249.3   1st Qu.:2.427e-04  
#  Median :361.1   Median :1.414e-03  
#  Mean   :361.1   Mean   :2.231e-03  
#  3rd Qu.:473.0   3rd Qu.:4.368e-03  
#  Max.   :584.8   Max.   :6.075e-03

Listing 24.5 showcases the exception handling, as the input ppplist btb.extra (Section 8.6) does not contain numeric-mark.

Listing 24.5: Exception: function density_marks.ppplist(), no numeric-marks
Code
spatstat.data::btb.extra |> 
  density_marks()
# $full
# named list()
# 
# $standard
# named list()

As explained in Section 23.4, the S3 method dispatch density_marks.ppplist() is different from the S3 method dispatch spatstat.explore::density.ppplist(), which uses the \(x\)- and \(y\)-coords only thus provides identical return with the marks removed from the input ppplist.

Review: function spatstat.explore::density.ppplist() (Baddeley, Rubak, and Turner 2015)
a1 = spatstat.data::btb.extra |>
  spatstat.explore::density.ppplist()
a0 = spatstat.data::btb.extra |>
  spatstat.geom::solapply(FUN = spatstat.geom::unmark.ppp) |>
  spatstat.explore::density.ppplist()
stopifnot(identical(a1, a0))

24.2 Kernel Density Estimates of numeric-marks

The S3 generic function kerndens() has been introduced in Section 23.5.

The S3 method dispatch kerndens.ppplist() finds the kernel density estimates of all numeric-marks in all ppp.objects in the input ppplist.

Example: function kerndens.ppplist()
spatstat.data::betacells |>
  spatstat.geom::split.ppp(f = 'type') |>
  kerndens(n = 8L)
# $area
# $area$off
# [1] 6.860440e-06 1.241176e-03 4.217601e-03 1.094990e-02 6.417054e-03 3.925757e-03 1.168156e-03 6.322900e-06
# 
# $area$on
# [1] 3.516447e-06 9.294044e-04 4.454117e-03 6.067291e-03 3.010829e-03 7.952900e-04 2.538680e-04 2.918730e-06
Exception: function kerndens.ppplist(), no numeric-marks
spatstat.data::btb.extra |> 
  kerndens() |>
  is.null()
# [1] TRUE

24.3 Quantile of numeric-marks

The S3 method dispatch quantile.ppplist() finds the quantiles of all numeric-marks in all ppp.objects (Section 23.6).

Example: function quantile.ppplist()
spatstat.data::betacells |>
  spatstat.geom::split.ppp(f = 'type') |>
  quantile()
# $area
# $area$off
#     0%    25%    50%    75%   100% 
# 168.30 239.40 257.35 279.25 360.10 
# 
# $area$on
#    0%   25%   50%   75%  100% 
# 207.9 281.7 321.3 362.2 514.4
Example: function quantile.ppplist(), no numeric-marks
spatstat.data::btb.extra |> 
  quantile()
# named list()

24.4 Aggregation of marks

The S3 generic function aggregate_marks() has been introduced in Section 23.8. The S3 method dispatch aggregate_marks.ppplist()

  • aggregates and vectorizes the marks of each ppp member of the input (Section 23.8);
  • returns a numeric-`vectorlist (Chapter 27).
Example: function aggregate_marks.ppplist(), for sample mean and sd
spatstat.geom::solist(
  spatstat.data::spruces,
  spatstat.data::spruces
) |>
  aggregate_marks(FUN = mean)
# Component 1:
#      mean 
# 0.2503731 
# 
# Component 2:
#      mean 
# 0.2503731
spatstat.geom::solist(
  spatstat.data::spruces,
  spatstat.data::spruces
) |>
  aggregate_marks(FUN = \(z) c(mean = mean(z), sd = sd(z)))
# Component 1:
#       mean         sd 
# 0.25037313 0.04697474 
# 
# Component 2:
#       mean         sd 
# 0.25037313 0.04697474
Example: function aggregate_marks.ppplist(), for relative frequency
spatstat.geom::solist(
  spatstat.data::ants,
  spatstat.data::ants
) |>
  aggregate_marks(FUN = \(z) table(z)/length(z))
# Component 1:
# Cataglyphis      Messor 
#   0.2989691   0.7010309 
# 
# Component 2:
# Cataglyphis      Messor 
#   0.2989691   0.7010309
Example: function aggregate_marks.ppplist(), for sample mean and sd of area-by-type
spatstat.geom::solist(
  spatstat.data::betacells,
  spatstat.data::betacells
) |>
  aggregate_marks(by = area ~ type, FUN = \(z) c(mean = mean(z), sd = sd(z)))
# Component 1:
# off.area.mean   off.area.sd  on.area.mean    on.area.sd 
#     259.72143      40.86083     325.11692      60.71534 
# 
# Component 2:
# off.area.mean   off.area.sd  on.area.mean    on.area.sd 
#     259.72143      40.86083     325.11692      60.71534
Example: function aggregate_marks.ppplist(), for relative frequency of season-by-group
spatstat.geom::solist(
  spatstat.data::gorillas,
  spatstat.data::gorillas
) |>
  aggregate_marks(by = season ~ group, FUN = \(z) table(z)/length(z))
# Component 1:
#   major.season.dry major.season.rainy   minor.season.dry minor.season.rainy 
#          0.4285714          0.5714286          0.4208754          0.5791246 
# 
# Component 2:
#   major.season.dry major.season.rainy   minor.season.dry minor.season.rainy 
#          0.4285714          0.5714286          0.4208754          0.5791246

The S3 method dispatch t.vectorlist() (@sec-t_vectorlist) is the fastest way to extract a "slice" from the returnednumeric-vectorlist`.

Advanced: function t.vectorlist()
spatstat.geom::solist(
  spatstat.data::gorillas,
  spatstat.data::gorillas
) |>
  aggregate_marks(by = season ~ group, FUN = \(z) table(z)/length(z)) |>
  t.vectorlist()
# major.season.dry:
# [1] 0.4285714 0.4285714
# 
# major.season.rainy:
# [1] 0.5714286 0.5714286
# 
# minor.season.dry:
# [1] 0.4208754 0.4208754
# 
# minor.season.rainy:
# [1] 0.5791246 0.5791246

24.5 Default \(r_\text{max}\)

The S3 generic function .rmax() has been introduced in Section 23.11. The S3 method dispatch .rmax.ppplist() obtains the default \(r_\text{max}\) before the (potentially) very slow batch processes.

Advanced: function .rmax.ppplist()
spatstat.data::btb.extra$full |> 
  spatstat.explore::markcorr() |> 
  vapply(FUN = .rmax.fv, FUN.VALUE = NA_real_)
#        year spoligotype 
#    26.96117    26.96117
spatstat.data::btb.extra$standard |> 
  spatstat.explore::markcorr() |> 
  vapply(FUN = .rmax.fv, FUN.VALUE = NA_real_)
#        year spoligotype 
#    26.96117    26.96117
spatstat.data::btb.extra |> 
  .rmax(fun = 'K')
#     full standard 
# 26.96117 26.96117

24.6 \(k\)-Means Clustering

The S3 generic function .kmeans() has been introduced in Section 23.12. The S3 method dispatch .kmeans.ppplist()

  • is a simple iteration of the method dispatch .kmeans.ppp().
  • returns an object of class 'pppkmlist', which inherits from 'ppplist'.

Package groupedHyperframe (v0.3.0.20251020) implements the following S3 method dispatches to the class 'pppkmlist' (Table 24.3),

Table: S3 method dispatches groupedHyperframe::*.pppkmlist
methods2kable(class = 'pppkmlist', package = 'groupedHyperframe', all.names = TRUE)
Table 24.3: S3 method dispatches groupedHyperframe::*.pppkmlist (v0.3.0.20251020)
visible from generic isS4
split.pppkmlist TRUE groupedHyperframe base::split FALSE

24.6.1 Examples

The authors use a subset of the hyper data frame flu (Section 8.10) to illustrate this concept.

Example: function .kmeans.ppplist()
set.seed(14); spatstat.data::flu |>
  spatstat.geom::subset.hyperframe(subset = (stain == 'M2-M1') & (virustype == 'wt')) |>
  spatstat.geom::`$.hyperframe`(name = 'pattern') |>
  .kmeans(formula = ~ x + y, centers = 3L)
# List of point patterns
# 
# wt M2-M1 13:
# Marked planar point pattern: 471 points
# Multitype, with levels = M2, M1 
# window: rectangle = [0, 3331] x [0, 3331] nm
# with k-means clustering of 153, 147, 171 points
# 
# wt M2-M1 22:
# Marked planar point pattern: 217 points
# Multitype, with levels = M2, M1 
# window: rectangle = [0, 3331] x [0, 3331] nm
# with k-means clustering of 64, 85, 68 points
# 
# wt M2-M1 27:
# Marked planar point pattern: 214 points
# Multitype, with levels = M2, M1 
# window: rectangle = [0, 3331] x [0, 3331] nm
# with k-means clustering of 71, 59, 84 points
# 
# wt M2-M1 43:
# Marked planar point pattern: 406 points
# Multitype, with levels = M2, M1 
# window: rectangle = [0, 3331] x [0, 3331] nm
# with k-means clustering of 169, 108, 129 points
# 
# wt M2-M1 49:
# Marked planar point pattern: 417 points
# Multitype, with levels = M2, M1 
# window: rectangle = [0, 3331] x [0, 3331] nm
# with k-means clustering of 125, 195, 97 points
# 
# wt M2-M1 65:
# Marked planar point pattern: 318 points
# Multitype, with levels = M2, M1 
# window: rectangle = [0, 3331] x [0, 3331] nm
# with k-means clustering of 117, 109, 92 points
# 
# wt M2-M1 71:
# Marked planar point pattern: 265 points
# Multitype, with levels = M2, M1 
# window: rectangle = [0, 3331] x [0, 3331] nm
# with k-means clustering of 118, 83, 64 points
# 
# wt M2-M1 84:
# Marked planar point pattern: 509 points
# Multitype, with levels = M2, M1 
# window: rectangle = [0, 3331] x [0, 3331] nm
# with k-means clustering of 155, 156, 198 points

24.6.2 Split by \(k\)-Means Clustering

The S3 method dispatch split.pppkmlist() splits a pppkmlist by the \(k\)-means clustering indices of each 'pppkm' member. The returned object has attributes

  • attr(,'id'), indices of the ppp.objects before splitting.
  • attr(,'cluster'), indices of \(k\)-means clusters, nested in id.

The authors use a subset of the hyper data frame flu (Section 8.10) to illustrate this concept.

Example: function split.pppkmlist()
set.seed(14); spatstat.data::flu |>
  spatstat.geom::subset.hyperframe(subset = (stain == 'M2-M1') & (virustype == 'wt')) |>
  spatstat.geom::`$.hyperframe`(name = 'pattern') |> 
  .kmeans(formula = ~ x + y, centers = 3L) |>
  split()
# $`wt M2-M1 13.1`
# Marked planar point pattern: 153 points
# Multitype, with levels = M2, M1 
# window: rectangle = [0, 3331] x [0, 3331] nm
# 
# $`wt M2-M1 13.2`
# Marked planar point pattern: 147 points
# Multitype, with levels = M2, M1 
# window: rectangle = [0, 3331] x [0, 3331] nm
# 
# $`wt M2-M1 13.3`
# Marked planar point pattern: 171 points
# Multitype, with levels = M2, M1 
# window: rectangle = [0, 3331] x [0, 3331] nm
# 
# $`wt M2-M1 22.1`
# Marked planar point pattern: 64 points
# Multitype, with levels = M2, M1 
# window: rectangle = [0, 3331] x [0, 3331] nm
# 
# $`wt M2-M1 22.2`
# Marked planar point pattern: 85 points
# Multitype, with levels = M2, M1 
# window: rectangle = [0, 3331] x [0, 3331] nm
# 
# $`wt M2-M1 22.3`
# Marked planar point pattern: 68 points
# Multitype, with levels = M2, M1 
# window: rectangle = [0, 3331] x [0, 3331] nm
# 
# $`wt M2-M1 27.1`
# Marked planar point pattern: 71 points
# Multitype, with levels = M2, M1 
# window: rectangle = [0, 3331] x [0, 3331] nm
# 
# $`wt M2-M1 27.2`
# Marked planar point pattern: 59 points
# Multitype, with levels = M2, M1 
# window: rectangle = [0, 3331] x [0, 3331] nm
# 
# $`wt M2-M1 27.3`
# Marked planar point pattern: 84 points
# Multitype, with levels = M2, M1 
# window: rectangle = [0, 3331] x [0, 3331] nm
# 
# $`wt M2-M1 43.1`
# Marked planar point pattern: 169 points
# Multitype, with levels = M2, M1 
# window: rectangle = [0, 3331] x [0, 3331] nm
# 
# $`wt M2-M1 43.2`
# Marked planar point pattern: 108 points
# Multitype, with levels = M2, M1 
# window: rectangle = [0, 3331] x [0, 3331] nm
# 
# $`wt M2-M1 43.3`
# Marked planar point pattern: 129 points
# Multitype, with levels = M2, M1 
# window: rectangle = [0, 3331] x [0, 3331] nm
# 
# $`wt M2-M1 49.1`
# Marked planar point pattern: 125 points
# Multitype, with levels = M2, M1 
# window: rectangle = [0, 3331] x [0, 3331] nm
# 
# $`wt M2-M1 49.2`
# Marked planar point pattern: 195 points
# Multitype, with levels = M2, M1 
# window: rectangle = [0, 3331] x [0, 3331] nm
# 
# $`wt M2-M1 49.3`
# Marked planar point pattern: 97 points
# Multitype, with levels = M2, M1 
# window: rectangle = [0, 3331] x [0, 3331] nm
# 
# $`wt M2-M1 65.1`
# Marked planar point pattern: 117 points
# Multitype, with levels = M2, M1 
# window: rectangle = [0, 3331] x [0, 3331] nm
# 
# $`wt M2-M1 65.2`
# Marked planar point pattern: 109 points
# Multitype, with levels = M2, M1 
# window: rectangle = [0, 3331] x [0, 3331] nm
# 
# $`wt M2-M1 65.3`
# Marked planar point pattern: 92 points
# Multitype, with levels = M2, M1 
# window: rectangle = [0, 3331] x [0, 3331] nm
# 
# $`wt M2-M1 71.1`
# Marked planar point pattern: 118 points
# Multitype, with levels = M2, M1 
# window: rectangle = [0, 3331] x [0, 3331] nm
# 
# $`wt M2-M1 71.2`
# Marked planar point pattern: 83 points
# Multitype, with levels = M2, M1 
# window: rectangle = [0, 3331] x [0, 3331] nm
# 
# $`wt M2-M1 71.3`
# Marked planar point pattern: 64 points
# Multitype, with levels = M2, M1 
# window: rectangle = [0, 3331] x [0, 3331] nm
# 
# $`wt M2-M1 84.1`
# Marked planar point pattern: 155 points
# Multitype, with levels = M2, M1 
# window: rectangle = [0, 3331] x [0, 3331] nm
# 
# $`wt M2-M1 84.2`
# Marked planar point pattern: 156 points
# Multitype, with levels = M2, M1 
# window: rectangle = [0, 3331] x [0, 3331] nm
# 
# $`wt M2-M1 84.3`
# Marked planar point pattern: 198 points
# Multitype, with levels = M2, M1 
# window: rectangle = [0, 3331] x [0, 3331] nm
# 
# attr(,"id")
#  [1] 1 1 1 2 2 2 3 3 3 4 4 4 5 5 5 6 6 6 7 7 7 8 8 8
# attr(,"cluster")
#  [1] 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3 1 2 3

24.7 Batch Process on Eligible marks

The S3 method dispatches Emark_.ppplist(), Vmark_.ppplist(), etc., in Table 24.1,

  • collects the return of the corresponding S3 method dispatch in Table 23.9, for each ppp.object (Chapter 23) in the input ppplist (Chapter 24);
  • organizes these returns into an fvlist (Chapter 14) for each numeric-mark, named in the fashion of <numeric-mark>.<suffix>.

The S3 method dispatches Gcross_.ppplist(), Kcross_.ppplist(), etc., in Table 24.1,

  • collects the return of the corresponding S3 method dispatch in Table 23.10, for each ppp.object (Chapter 23) in the input ppplist (Chapter 24);
  • organizes these returns into an fvlist (Chapter 14) for each multitype-mark, named in the fashion of <multitype-mark>.<suffix>.

The S3 method dispatch nncross_.ppplist() in Table 24.1,

  • collects the return of the corresponding S3 method dispatch in Table 23.11, for each ppp.object (Chapter 23) in the input ppplist (Chapter 24);
  • organizes these returns into an anylist (Chapter 11) for each multitype-mark, named in the fashion of <multitype-mark>.<suffix>.

The low-level utility function op_ppplist(), for batch operation on ppplist, is the underlying mechanism of the batch processes (Section 3.2). Function op_ppplist()

  • applies the operation to each ppp.object (Chapter 23) in the input using package parallel shipped with R version 4.5.1 (2025-06-13), and returns a two-level hierarchical list, the first level corresponds to the individual ppp.objects, and the second level corresponds to the eligible numeric-marks (for operations in Table 23.9) or eligible multitype-marks (foroperations in Table 23.10 and Table 23.11);
  • ‘flips’ the hierarchy such that the first level represents the eligible marks, and the second level corresponds to the individual ppp.objects.
Data: a ppplist object finp_s
finp = spatstat.data::finpines
append_marks(finp) = finp |> 
  spatstat.geom::cut.ppp(z = 'height', breaks = 2L, labels = c('L', 'H')) |>
  spatstat.geom::marks.ppp()
finp_s = finp |> 
  spatstat.geom::split.ppp(f = 'm3') |>
  spatstat.geom::solapply(FUN = spatstat.geom::subset.ppp, select = c('diameter', 'm3'))
finp_s
# List of point patterns
# 
# L:
# Marked planar point pattern: 77 points
# Mark variables: diameter, m3 
# window: rectangle = [-5, 5] x [-8, 2] metres
# 
# H:
# Marked planar point pattern: 49 points
# Mark variables: diameter, m3 
# window: rectangle = [-5, 5] x [-8, 2] metres
Example: function markcorr_.ppplist()
finp_s |>
  markcorr_()
# 
# $diameter.k
# An 'fvlist' of 2 fv.objects k[mm](r)
# Available rmax: 2.5
# Minimum Legal rmax: 2.5
Example: function nncross_.ppplist()
gorillas_group_dist = spatstat.data::gorillas |> 
  spatstat.geom::split.ppp(f = 'group') |>
  nncross_(i = 'dry', j = 'rainy')
# 
gorillas_group_dist |>
  lapply(FUN = \(i) {
    i |> lapply(FUN = summary)
  })
# $season.nncross
# $season.nncross$major
#    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
#    0.00   43.56   67.63   80.18  107.80  263.51 
# 
# $season.nncross$minor
#    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
#   9.117  43.825  74.924  92.316 113.309 361.513
gorillas_group_dist |>
  lapply(FUN = lengths)
# $season.nncross
# major minor 
#   150   125