--- title: "Finding your way around geosmooth" output: rmarkdown::html_vignette: toc: true vignette: > %\VignetteIndexEntry{Finding your way around geosmooth} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r standalone-width, echo=FALSE, results='asis'} # Let pkgdown own the site layout; widen only the standalone installed vignette. if (!identical(Sys.getenv("IN_PKGDOWN"), "true")) { cat("") } ``` ```{r setup, include=FALSE} knitr::opts_chunk$set(collapse = TRUE, comment = "#>") read.recipe <- getExportedValue("knitr", "read_chunk") read.recipe(system.file("doc-tools/first-fit.R", package = "geosmooth")) read.recipe(system.file("doc-tools/graph-workflow.R", package = "geosmooth")) ``` `geosmooth` estimates a smooth response, or a probability mass, from data given as a matrix of coordinates or as a weighted graph. This guide is organized by task. The table below names the function to start with for each task, the next section walks through a first fit, and the catalog that follows lists every exported function with a link to its help page. Many of the exported functions build operators or generate synthetic data. You do not need any of them to fit your first model. ## Choose a starting point | Your task | Start with | What to know before choosing | |---|---|---| | Regress a response on coordinates | [`fit.lps()`](https://pgajer.github.io/geosmooth/reference/fit.lps.html) | Local polynomial smoothing; selects neighborhood size, degree, and kernel by cross-validation. | | Average predictions from overlapping local models | [`fit.malps()`](https://pgajer.github.io/geosmooth/reference/fit.malps.html) | Model-averaged local polynomial smoothing; supports coordinate and graph-geodesic neighborhoods. | | Couple local polynomial predictions across overlaps | [`fit.ps.lps()`](https://pgajer.github.io/geosmooth/reference/fit.ps.lps.html) | Prediction-synchronized local polynomial smoothing; experimental R reference implementation. | | Fit a locally adaptive trend with a sparse difference penalty | [`fit.lpl.tf()`](https://pgajer.github.io/geosmooth/reference/fit.lpl.tf.html) | Local polynomial lifting trend filtering; requires optional package `genlasso`. | | Fit a one-dimensional continuous response | `fit.gaussian.trend.filtering()` | Order-2 Gaussian trend filtering with squared-error loss, explicit penalties and convergence diagnostics. | | Fit a one-dimensional binary or binomial response | `fit.logistic.trend.filtering()` | Order-2 logistic trend filtering with a native C++ solver, explicit penalties, optional covariates/offsets, and convergence diagnostics. | | Fit adaptive curves along fixed or learned projections | `fit.projection.trend.filtering()` | Gaussian or binomial additive projection models; mean-loss penalties and separate joint-fit and search diagnostics. | | Add synchronization to a lifting trend filter | [`fit.slpl.tf()`](https://pgajer.github.io/geosmooth/reference/fit.slpl.tf.html) | Synchronized local polynomial lifting trend filtering; adds a second, quadratic penalty. | | Smooth a response already attached to graph vertices | [`fit.metric.graph.lowpass()`](https://pgajer.github.io/geosmooth/reference/fit.metric.graph.lowpass.html) or [`fit.graph.trend.filtering()`](https://pgajer.github.io/geosmooth/reference/fit.graph.trend.filtering.html) | Choose spectral low-pass smoothing or a graph-difference penalty. Edge-weight meanings differ; see below. | | Penalize local second derivatives on a point cloud | [`fit.ssrhe.hessian.regression()`](https://pgajer.github.io/geosmooth/reference/fit.ssrhe.hessian.regression.html) | Hessian-energy regression, following semi-supervised regression using Hessian energy (SSRHE); use `lambda.selection = "cv"` or `"gcv"` to select penalties. | | Use an absolute-value Hessian penalty | [`fit.ssrhe.hessian.l1.regression()`](https://pgajer.github.io/geosmooth/reference/fit.ssrhe.hessian.l1.regression.html) | Different regularization from quadratic Hessian energy; has its own solver and tuning controls. | | Interpolate graph values while fixing a region boundary | [`perform.harmonic.smoothing()`](https://pgajer.github.io/geosmooth/reference/perform.harmonic.smoothing.html) | Iterative harmonic relaxation; [`harmonic.smoother()`](https://pgajer.github.io/geosmooth/reference/harmonic.smoother.html) also records extrema and topology diagnostics. | | Estimate probability mass over a fixed support | [`fit.density()`](https://pgajer.github.io/geosmooth/reference/fit.density.html) | Takes nonnegative counts or masses; returns mass over the rows of `X`. | | Estimate where one subject visits that support | [`fit.subject.od()`](https://pgajer.github.io/geosmooth/reference/fit.subject.od.html) | Takes visit indices, including repeats; handles occupation-density fitting and optional visit-level validation. | | Build reproducible synthetic examples | [`synthetic.spec()`](https://pgajer.github.io/geosmooth/reference/synthetic.spec.html) then [`materialize.synthetic()`](https://pgajer.github.io/geosmooth/reference/materialize.synthetic.html) | Combine geometry, sampling, truth, and response noise. | The rows of this table are different estimators, listed in no particular order of predictive performance. Two experimental methods, parallel-transport trend filtering and transported graph Hessians, are described in their own [section](#experimental-transport-methods). ## A first regression fit `fit.lps()` takes a matrix `X` with one observation per row and a response `y` with one value per row. By default it works in the coordinates you supply. It can also fit each local polynomial in a *local chart*, a coordinate system computed near the point of interest, usually by principal component analysis (PCA). For a first fit, keep the supplied coordinates and the default numerical settings. ```{r first-fit-model} ``` ```{r first-fit-plot, echo=FALSE, fig.width=7.2, fig.height=4, out.width="100%", fig.cap="The points are noisy observations. The solid blue line is the selected fit; the dashed orange line is known only because these data are synthetic.", fig.alt="Noisy observations along a sine curve, with a fitted solid curve close to the dashed synthetic truth."} ``` Here CV selected `r fit$selected$support.size`-point neighborhoods, degree `r fit$selected$degree`, and the Gaussian kernel, with a tuning RMSE of `r format(fit$selected$cv.rmse.observed, digits = 3)` response units. A larger neighborhood averages over more observations, and the degree determines the shape that is fitted within a neighborhood. Because this RMSE was used to choose the settings, it is not an independent estimate of prediction error. ```{r first-predictions} head(fitted(fit)) new.X <- matrix(seq(0.1, 0.9, length.out = 5), ncol = 1) predict(fit, newdata = new.X) ``` Some terms used throughout the package: cross-validation (CV) chooses tuning parameters by predicting responses that were held out of the fit. A *support* is the neighborhood used for one local fit, and a *grid* is a list of candidate values. The support size and the polynomial degree together determine whether a local fit has enough observations. For grouped observations, use [`lps.grouped.foldid()`](https://pgajer.github.io/geosmooth/reference/lps.grouped.foldid.html) to keep each group in one fold. Use [`lps.nested.cv()`](https://pgajer.github.io/geosmooth/reference/lps.nested.cv.html) when you need an outer assessment of the whole tuning procedure. ### Prediction and reusable fits | Fitted object | How to get predictions | Scope | |---|---|---| | Local polynomial smoother | `predict(fit)` or `predict(fit, newdata = Xnew)` | Existing evaluation points or new coordinate rows. | | Model-averaged local polynomial smoother | `predict(fit)` or `predict(fit, newdata = Xnew)` | New coordinates are supported for coordinate-distance fits; graph-geodesic fits currently reject new targets. | | Either lifting trend filter | `predict(fit)` | Training points only; `newdata` must be `NULL`. | | Prediction-synchronized, chart-kernel, local-likelihood, graph low-pass, graph trend-filtering, Hessian, or parallel-transport fit | `fitted(fit)` | No registered `predict()` method for these classes in this version. Chart-kernel and local-likelihood fits can receive `X.eval` when fitting. | | Density or occupation-density result | `fit$rho` | Probability mass on the evaluation support. | | Tracked harmonic result (`harmonic.smoother()`) | `fitted(fit)` | Values on the original graph vertices. | | Basic harmonic result (`perform.harmonic.smoothing()`) | `fit$harmonic_predictions` | An unclassed list; retains the basic function's original contract. | `summary(fit)` shows the selected settings and the diagnostics stored in the fit, and says so when no convergence information is available. `fitted(fit)` returns the fitted values as a vector or a matrix, as stored by the fit. `residuals(fit)` returns the response minus the fitted values, with `NA` where the response is missing. Residuals exist only at the training points, so a fit that was evaluated at other coordinates has none. They are on the scale of the response, also for binomial fits; they are not Pearson or deviance residuals. Probability mass is a different quantity and is read from `fit$rho`. See `help("model-values", package = "geosmooth")` and `help("model-summary", package = "geosmooth")` for details. Three further terms: a *refit* fits a new response while reusing the neighborhoods or the operator of an existing fit. It does not repeat the model selection of the original fit; the help page of each refit method says whether the penalties stay fixed or can be selected again. An *operator* is the matrix of differences or derivatives that a method penalizes; it is not a fitted response. A *spectral basis* is a stored set of graph eigenvectors, which allows many responses to be smoothed without recomputing them. ### Refit and extract smoother matrices `refit(fit, y = new.y, ...)` works for the six kinds of model in the table below. R chooses the method from the class of the fitted object. The arguments named in the last column belong to that method only. | Fitted model | Accepted response | What is reused and what can be changed | |---|---|---| | MALPS | Vector; omitted or `NULL` reuses the stored response | Supports and averaging weights stay fixed. Optional `weights` affect local coefficient estimation; robust residual weights are recomputed. | | Lifting trend filter | Vector required | Reuses the residual operator and stored penalty, or accepts a fixed `lambda`. | | Synchronized lifting trend filter | Vector required | Reuses both operators and stored penalties, or accepts fixed `lambda1` and `lambda2`. | | Metric graph low-pass | Vector or matrix required | Reuses the spectral basis and smoothing parameter; `per.column.gcv = TRUE` selects a parameter for each response column. | | Quadratic Hessian-energy regression | Vector or matrix; omitted or `NULL` reuses the stored response | Reuses the operator and fixed penalties; accepts `lambda1`, `lambda2`, `weights`, and `ridge`. CV/GCV fits dispatch here and retain their selected penalties by default. | | Hessian L1 regression | One response; omitted or `NULL` reuses the stored response | Reuses the derivative operator; accepts `lambda.grid`, fixed or CV `lambda.selection`, and solver controls. | A refit of a metric graph low-pass fit or of a quadratic Hessian fit returns a summary that cannot itself be refitted, so for further responses keep calling `refit()` on the original fit. The other four kinds return fitted objects that can be refitted again. `smoother.matrix(fit, ...)` returns the matrix that maps responses to predictions, for LPS and MALPS fits. For LPS, the fit must have a single candidate configuration; the matrix maps the training responses to the predictions at the evaluation points, and `check.tol` sets the tolerance of an internal check. For MALPS, the matrix maps training responses to predictions at the training points, given the stored supports and weights; `max.n` limits the size of the matrix. For a robust MALPS fit, `allow.robust = TRUE` returns the matrix for the final robust weights held fixed. It does not reflect how those weights depend on the response. ```{r shared-generics} malps.fit <- fit.malps( X, y, degree = 1L, support.type = "knn", support.size = 12L ) new.y <- cos(2 * pi * X[, 1]) malps.refit <- refit(malps.fit, y = new.y) S <- smoother.matrix(malps.fit) max(abs(as.vector(S %*% new.y) - predict(malps.refit))) ``` If you are updating code written for version 0.1.0: the six `refit.*()` functions and the two `*.smoother.matrix()` functions are no longer exported. Call `refit()` and `smoother.matrix()` instead, and in refit calls replace `fitted.model =` with `object =` and `y.new =` with `y =`. The other arguments and the classes of the results are unchanged. `help("refit", package = "geosmooth")` and `help("smoother.matrix", package = "geosmooth")` link to the arguments of each method, and the old help names still work. ## Nearest-neighbor probabilities for a binary outcome `fit.neighbor.probability()` estimates the probability of a binary outcome at a point from the outcomes of its nearest neighbors. It offers three estimators, selected by `method`. `"knn"` is the ordinary proportion among the `k` nearest people. `"case_raw"` and `"case_corrected"` instead extend the neighborhood until it contains `k` cases; the second corrects the proportion for this stopping rule. So `k` counts people for `"knn"` and cases for the other two. Give `k` directly, or give fold labels in `foldid` to select `k` by the Brier score. The function is designed for data in which a participant can have several records: * `group` identifies the participant of each record. A participant is represented by the closest of their records that has an outcome, and all records of a participant are left out together when predicting for that participant. * `id` identifies the records and is used to break ties in distance, so that results do not depend on row order. * A record with a missing outcome is never counted as a control. * `predict(fit)` leaves each participant out of their own prediction. For new points that belong to a participant in the training data, pass `query.group`. * If too few neighbors can be reached, the prediction is `NA`; no overall rate is substituted. Three settings control how neighbors are found. With Euclidean coordinates, `search = "auto"` uses an exact tree search (ANN with `eps = 0`), and `search = "brute"` uses an exhaustive search that serves as a reference. `search = "ann"` with, for example, `eps = 0.1` allows approximate neighbors. The tolerance `eps` bounds the error in distance, not the error in the estimated probability, and an approximate search can miss participants or tied records, so the diagnostics for ties are complete only with an exact search. Chebyshev, intrinsic simplex, and precomputed distances always use the exhaustive search; asking for ANN or a positive `eps` with them is an error. For compositional data normalized so that the largest coordinate is one, `metric = "linf.simplex"` with `normalization = "max"` measures distance along the surface of the simplex, using the solver in `dgraphs`. Paths may cross intermediate faces. Distances are computed for one query point at a time, so neither a matrix of all pairwise distances nor a graph of the samples is needed. With `normalization = "none"`, the rows must already be max-normalized to within `sqrt(.Machine$double.eps)`, and `normalization = "sum"` is not allowed for this metric. Note that max normalization combined with `metric = "euclidean"` measures straight-line distance instead, which can give different neighbors. The search structures are reused within one prediction call or cross-validation split and rebuilt on the next call, so a fitted object can be saved and reloaded. ```{r neighbor-probability, eval=FALSE} fit <- fit.neighbor.probability(X, y, method = "case_corrected", k = 3, id = record_id, group = participant_id, normalization = "max", metric = "linf.simplex") predict(fit, newdata = query_X, query.group = query_participant_id, type = "diagnostics") ``` When the neighborhood stops at a number of cases, it depends on the outcomes, so the estimator is not a linear smoother and `smoother.matrix()` is not available for it. To assess a model whose `k` was tuned, use a separate set of outer folds, grouped by participant. ## Function catalog The tables in this section list every function exported by `geosmooth`, each with a one-line description. To open the help page of a function in R, use, for example, `help("fit.lps", package = "geosmooth")`. The last two tables list related functions that belong to the `dgraphs` package. They are not exported by `geosmooth`, and their help pages are in `dgraphs`. ### Shared model operations | Function | Purpose | |---|---| | [`refit()`](https://pgajer.github.io/geosmooth/reference/refit.html) | Refit a supported model with a new response using stored geometry and method-specific tuning controls. | | [`smoother.matrix()`](https://pgajer.github.io/geosmooth/reference/smoother.matrix.html) | Extract the conditional linear map for a supported LPS or MALPS fit. | ### Local regression and prediction synchronization | Function | Purpose | |---|---| | `fit.neighbor.probability()` | Fit C++ ordinary kNN, raw nearest-case or corrected nearest-case binary estimators, with deterministic ties and grouped tuning. | | [`fit.lps()`](https://pgajer.github.io/geosmooth/reference/fit.lps.html) | Fit and tune local polynomial regression, including supported Gaussian and binary response modes. | | [`fit.malps()`](https://pgajer.github.io/geosmooth/reference/fit.malps.html) | Fit and optionally tune a model-averaged collection of local polynomial models. | | [`fit.ps.lps()`](https://pgajer.github.io/geosmooth/reference/fit.ps.lps.html) | Fit experimental local polynomial models whose predictions are synchronized on overlaps. | | [`fit.chart.kernel()`](https://pgajer.github.io/geosmooth/reference/fit.chart.kernel.html) | Fit a chart-kernel field, with optional reference-measure quadrature weights. | | [`fit.local.likelihood()`](https://pgajer.github.io/geosmooth/reference/fit.local.likelihood.html) | Fit local density or Bernoulli likelihood models; returns a fitted field. | | `unfold.simplex.chart()` | Certify and unfold a local one/two-face simplex neighborhood before constructing charts. | | `fit.masked.graph.field()` | Fit a graph field from partially observed responses. | | `support.masked.graph.field()` | Identify graph components supported by observed responses. | | `cv.masked.graph.field()` | Assess masked fields using participant-grouped nested cross-validation. | | `compare.masked.graph.fields()` | Compare candidate graph fields with independent workers. | | `select.masked.graph.field()` | Assemble nested graph-selection predictions from inner-fold choices. | ### Model assessment and uncertainty | Function | Purpose | |---|---| | `assess.logistic.trend.filtering()` | Test constant binary probability by retuned outcome permutations and measure nested-CV improvement over a training-only constant. Summarize absolute probability deviation and total variation; optionally test them using the same fits, with Holm adjustment. Requires independent unit-weight binary observations without covariates or offsets. | | `assess.gaussian.trend.filtering()` | Assess continuous outcomes with retuned squared-error permutation tests, optional absolute-deviation/total-variation tests and nested-CV MSE/MAE improvement. Requires independent unit-weight observations without covariates or offsets, exchangeable under a common-distribution null. | | [`lps.grouped.foldid()`](https://pgajer.github.io/geosmooth/reference/lps.grouped.foldid.html) | Assign whole groups of observations to validation folds. | | [`lps.nested.cv()`](https://pgajer.github.io/geosmooth/reference/lps.nested.cv.html) | Assess a tuned local polynomial smoother using outer and inner validation folds. | | [`lps.backend.diagnostics()`](https://pgajer.github.io/geosmooth/reference/lps.backend.diagnostics.html) | Inspect the computational backend, chart dimensions, and selected controls. | | [`lps.pointwise.band()`](https://pgajer.github.io/geosmooth/reference/lps.pointwise.band.html) | Calculate pointwise uncertainty for a supported fixed-configuration Gaussian fit. | | [`malps.gcv()`](https://pgajer.github.io/geosmooth/reference/malps.gcv.html) | Calculate generalized cross-validation and optional leave-one-out diagnostics for a stored model-averaged fit. | | [`bootstrap.malps()`](https://pgajer.github.io/geosmooth/reference/bootstrap.malps.html) | Measure refit variability by bootstrap case weights while holding the selected geometry fixed. | Generalized cross-validation (GCV) estimates prediction error from the effective degrees of freedom of a smoother, without fitting a separate model for each left-out observation. It is a different criterion from cross-validation with folds, and the two need not select the same settings. The uncertainty functions for LPS and for MALPS rest on different assumptions. `lps.pointwise.band()` requires a Gaussian fit made with the R backend and the orthogonal polynomial basis, with a single candidate configuration and a fixed chart dimension, and evaluated at the training points. `malps.gcv()` and `bootstrap.malps()` treat the selected supports and weights as fixed and do not repeat the model selection. Neither gives simultaneous coverage, and neither accounts for the uncertainty of choosing the settings from the data. ### Lifting trend filters | Function | Purpose | |---|---| | [`lpl.tf.operator()`](https://pgajer.github.io/geosmooth/reference/lpl.tf.operator.html) | Build a local polynomial residual operator. | | [`fit.lpl.tf()`](https://pgajer.github.io/geosmooth/reference/fit.lpl.tf.html) | Fit a trend filter with an absolute-value penalty on those residuals. | | [`slpl.tf.operator()`](https://pgajer.github.io/geosmooth/reference/slpl.tf.operator.html) | Build the residual operator together with an overlap synchronization operator. | | [`fit.slpl.tf()`](https://pgajer.github.io/geosmooth/reference/fit.slpl.tf.html) | Fit the residual penalty plus quadratic synchronization. | Both fitting functions accept an operator that was built beforehand, or build one themselves. For a single fit, call the fitting function directly. Build the operator yourself when you want to fit several responses, examine the neighborhoods, or avoid recomputing an expensive operator. Both fitting functions require the optional package `genlasso`, and both predict only at the training points. ### Graph spectral smoothing and graph trend filtering | Function | Purpose | |---|---| | [`fit.metric.graph.lowpass()`](https://pgajer.github.io/geosmooth/reference/fit.metric.graph.lowpass.html) | Select a graph-spectral smoothing parameter by GCV and fit a response. | | [`metric.graph.lowpass.operator()`](https://pgajer.github.io/geosmooth/reference/metric.graph.lowpass.operator.html) | Construct a graph Laplacian from edge lengths or explicit conductances. | | [`metric.graph.lowpass.basis()`](https://pgajer.github.io/geosmooth/reference/metric.graph.lowpass.basis.html) | Construct and retain a partial or complete graph spectrum for repeated smoothing. | | [`apply.metric.graph.lowpass.path()`](https://pgajer.github.io/geosmooth/reference/apply.metric.graph.lowpass.path.html) | Apply a supplied basis over a parameter grid; retain the candidate fits. | | [`metric.graph.heat.eta.grid()`](https://pgajer.github.io/geosmooth/reference/metric.graph.heat.eta.grid.html) | Choose heat-time candidates using spectral information. | | [`metric.graph.heat.extend.lower()`](https://pgajer.github.io/geosmooth/reference/metric.graph.heat.extend.lower.html) | Propose a smaller heat time for an external search, subject to resolution safeguards. | | [`graph.trend.filtering.operator()`](https://pgajer.github.io/geosmooth/reference/graph.trend.filtering.operator.html) | Construct graph differences or experimental path divided differences. | | [`fit.graph.trend.filtering()`](https://pgajer.github.io/geosmooth/reference/fit.graph.trend.filtering.html) | Fit an absolute-value graph-difference penalty; requires `genlasso`. | `geosmooth` does not construct graphs or compute shortest paths; the `dgraphs` package does. A graph is passed as an adjacency list, in which element `i` holds the indices (starting from 1) of the neighbors of vertex `i`, and a weight list of the same shape that holds a value for each edge. **The meaning of the edge values depends on the method.** A *length* is a distance between two vertices. A *conductance* is the strength of their connection, and larger values mean a stronger connection. Metric low-pass smoothing expects lengths by default and converts them to conductances; pass `weight.type = "conductance"` if your values are already conductances. Graph trend filtering with the default recursive operator treats the values as conductances, whereas its path divided-difference operator treats them as lengths. The heat time `eta` controls the amount of smoothing in graph low-pass filtering. A basis that contains only part of the spectrum may be unable to represent the filter for small positive heat times. If you build or extend a grid of heat times yourself, consult the spectral diagnostics returned with the basis. With a complete spectrum, heat smoothing also accepts missing responses, as long as every connected component of the graph has at least one observed response. The [observed-data heat guide](verified-heat-fallback.html) describes the criterion that is minimized in this case and the optional numerical cross-checks requested with `numerical.policy = "verified.fallback"`. Under that policy, a heat time is not selected automatically when the GCV scores cannot distinguish the candidates, either because the scores are constant by construction or because their differences are smaller than the numerical accuracy. The resolution bands reported there describe numerical accuracy; they are not confidence intervals. #### From a constructed graph to a fitted response This example constructs a graph with `dgraphs` and smooths a response on it. The 20 locations lie on a line, and the radius used to construct the graph joins only adjacent locations, so the graph is a chain. Vertex *i*, row *i* of the coordinates, and entry *i* of the response must refer to the same observation. The smoother cannot detect a response that is in the wrong order, so keep your identifiers in a table and match the response to the vertices by identifier before fitting. ```{r graph-workflow} ``` With `conductance.alpha = 1`, an edge of length 2 receives the conductance `1 / (2 + 1e-8)`, about 0.5, so longer edges connect their endpoints less strongly. The conversion from lengths to conductances affects the result of the smoothing. The example computes all eigenvectors of the graph Laplacian, so nothing is lost by truncating the spectrum. For a large graph you would keep only some of them; `help("metric.graph.lowpass.basis")` describes the diagnostics to consult in that case. The second response is smoothed with the same eigenvectors and the heat time already selected. The GCV score that is printed was used for tuning and is not an estimate of held-out prediction error. | Where the values are used | What they mean | Consequence | |---|---|---| | Coordinate neighborhoods | Distances in the supplied coordinate units | Rescaling coordinates changes supports. | | Graph-geodesic neighborhoods | Sums of edge lengths along paths | Unreachable vertices have infinite distance; support rules must handle connectivity. | | Metric low-pass, `weight.type = "length"` (default) | Positive lengths transformed to conductances | Reciprocal edges and matching lengths are required; zero lengths are rejected. | | Metric low-pass, `weight.type = "conductance"` | Supplied nonnegative connection strengths | Reciprocal weights must agree; zero-weight edges are removed and positive weights are used unchanged. | | Recursive graph differences | Conductance-like weights | Greater weight penalizes differences more strongly. | | Path divided differences | Edge lengths | The operator uses metric spacing, not conductance. | For example, `metric.graph.lowpass.operator(list(2L, integer()), list(1, numeric()))` is rejected, because the edge from vertex 1 to vertex 2 is not also listed from vertex 2 to vertex 1. If your input has directed edges or edges of length zero, decide what they should mean before you construct an undirected graph. A conductance of zero means that there is no connection. A length of zero is not allowed, because it cannot be converted to a conductance. ### One-dimensional Gaussian and logistic trend filtering | Function | Purpose | |---|---| | `fit.logistic.trend.filtering()` | Fit a univariate piecewise-quadratic logit curve using the native C++ backend. | | `fit.gaussian.trend.filtering()` | Fit a univariate piecewise-quadratic mean curve with squared-error loss using the same order-2 geometry and native C++ solvers. | | `fit.projection.trend.filtering()` | Fit centered order-2 curves on fixed directions or learn directions by projection pursuit; supports Gaussian and binomial losses. | | `cv.projection.trend.filtering()` | Select component counts and mean-loss penalties using supplied, optionally grouped folds; repeat all direction learning within each training split. | For a continuous outcome, fit the model over a list of penalties that you supply, and then assess both the association with the predictor and the held-out prediction: ```{r gaussian-trend-filtering-assessment} x.continuous <- seq(0, 1, length.out = 40) y.continuous <- sin(2*pi*x.continuous) + 0.2*cos(seq_along(x.continuous)) gaussian.fit <- fit.gaussian.trend.filtering( x.continuous, y.continuous, lambda = c(Inf, 0.01), backend = "banded") # Nine permutations keep this API example short; use more for inference. gaussian.assessment <- assess.gaussian.trend.filtering( gaussian.fit, permutations = 9, inner.folds = 3, outer.folds = 3, seed = 42, additional.tests = c("absolute.deviation", "total.variation")) gaussian.assessment$tests gaussian.assessment$prediction$scores ``` The test statistic is the reduction in the residual sum of squares relative to the sample mean. The tuning is repeated for every permutation. The test assumes that the outcomes are exchangeable under the null hypothesis; equal means with variances that depend on the predictor are not enough. The nested cross-validation is a separate assessment. It compares the smoother with the mean of the outer training data, using MSE and MAE on the same held-out rows. The curve summaries are in the units of the response. Note that `lambda = Inf` gives a quadratic curve, whereas the baseline of the assessment is a constant. The example shows how the functions are used; with nine permutations it says nothing about the calibration or the power of the test. The [logistic trend-filtering vignette](logistic-trend-filtering.html) walks through synthetic probabilities, trial weights and held-out Pima predictions. `fit.logistic.trend.filtering()` accepts binary observations, or proportions together with the numbers of trials as weights. You must supply the penalties in `lambda`. They refer to the predictor after it has been rescaled to the range of the training data, not to its original units. Observations with exactly equal predictor values share one value of the curve but keep their own covariates and offsets. To select a penalty by cross-validation, supply `foldid`; give repeated observations of one subject the same fold label. Always inspect `fit$diagnostics`. A fit that reached the iteration limit, detected separation, or failed numerically is not a successful fit. `predict()` applies the transformations learned from the training data and holds the curve constant outside the training range. No ridge penalty is added unless you request one. Two solvers are available. `backend = "dense"` is the reference solver for small problems. The banded solver does not form dense matrices over all knots, yet its stopping checks still examine every knot coefficient. `backend = "auto"` chooses the banded solver when there are more than 256 distinct predictor values or when the dense solver would need too much memory. Both solvers check an estimate of their memory use before allocating. Cross-validation for Gaussian, logistic, local polynomial lifting, and graph trend filtering uses `cv.penalty.scale = "training_mass"` by default. A fold is fitted to less data than the full fit, so its penalty is multiplied by the ratio of the training weight to the full weight. This keeps the penalty in the same proportion to the mean loss. Set `cv.penalty.scale = "none"` to use the same numerical penalty in every fold, as earlier versions did. The one-dimensional fits scale explicit ridge penalties in the same way and record the settings used in each fold. The assessment functions apply the same rule to their outer and inner training sets. Fits saved by earlier versions keep the earlier convention, and fits with a fixed penalty are not affected. ### Hessian-energy regression | Function | Purpose | |---|---| | [`ssrhe.hessian.operator()`](https://pgajer.github.io/geosmooth/reference/ssrhe.hessian.operator.html) | Estimate local derivative operators and Hessian-energy matrices from point-cloud neighborhoods. | | [`fit.ssrhe.hessian.regression()`](https://pgajer.github.io/geosmooth/reference/fit.ssrhe.hessian.regression.html) | Fit quadratic Hessian-energy regression using fixed penalties, label CV, or GCV; optionally select supports. | | [`fit.ssrhe.hessian.l1.regression()`](https://pgajer.github.io/geosmooth/reference/fit.ssrhe.hessian.l1.regression.html) | Fit an absolute-value derivative penalty with fixed or CV-selected regularization. | | [`ssrhe.support.grid()`](https://pgajer.github.io/geosmooth/reference/ssrhe.support.grid.html) | Build candidate adaptive-radius support profiles for tuning. | The Hessian of a function is its matrix of second derivatives. These methods estimate it locally from the points near each observation and penalize its size. A quadratic penalty reduces curvature everywhere by a moderate amount, whereas an absolute-value penalty can set some second derivatives exactly to zero. The two penalties therefore give different estimates. Cross-validation works when some points have no observed response (semi-supervised regression). GCV requires a single response vector with no missing values and strictly positive observation weights. The trace needed for GCV can be computed exactly or estimated with random probes; the exact computation costs more, and the estimate depends on a random seed. For the absolute-value penalty, `solver = "admm"` uses the built-in alternating direction method of multipliers, and the `genlasso` solver requires that optional package. To select the penalties of the quadratic method, call the same fitting function with `lambda.selection = "cv"` or `"gcv"`, and put the settings of the selection in `cv.control` or `gcv.control`. A single penalty value and a grid of candidates cannot be given together. ```{r hessian-selection} hessian.fit <- fit.ssrhe.hessian.regression( X, y, k = 12L, tangent.dim = 1L, lambda.selection = "cv", lambda1.grid = c(0.01, 0.1), cv.control = list(foldid = rep(1:5, length.out = nrow(X))) ) hessian.fit$lambda ``` The separate `.cv()` and `.gcv()` fitting functions of version 0.1.0 have been removed. Their old help names now open the help page of `fit.ssrhe.hessian.regression()`, which explains how to update a call. Calls with fixed `lambda1` and optional `lambda2` work as before. ### Harmonic interpolation | Function | Purpose | |---|---| | [`perform.harmonic.smoothing()`](https://pgajer.github.io/geosmooth/reference/perform.harmonic.smoothing.html) | Relax interior values while holding the region boundary fixed; return convergence diagnostics. | | [`harmonic.smoother()`](https://pgajer.github.io/geosmooth/reference/harmonic.smoother.html) | Run relaxation while recording extrema, prediction history, and topology stability. | | [`get.region.boundary()`](https://pgajer.github.io/geosmooth/reference/get.region.boundary.html) | Find region vertices with a neighbor outside the region. | Harmonic smoothing replaces the values inside a region of the graph by values that are, at each vertex, the weighted average of the neighbors' values, while the values on the boundary of the region stay fixed. A boundary vertex is a vertex of the region that has a neighbor outside it. A vertex of degree one is therefore not a boundary vertex when the region is the whole graph. The two functions behave differently when the region has no boundary, so they are not interchangeable. `perform.harmonic.smoothing()` returns its input unchanged with `status = "no_boundary"`, whereas `harmonic.smoother()` still runs the iteration. Both report numerical convergence only when the change between iterations and the harmonic residual are both small. `harmonic.smoother()` returns a `harmonic_smoother` object with `print()`, `summary()`, and `plot()` methods. It also reports whether the local extrema of the values stopped changing: `stability_detected` says whether a stable stretch of iterations was found, and `stable_iteration` identifies it, or is `NA` if none was found. This is a separate diagnostic from convergence. For example, on a path whose values alternate, recording every second iteration can look stable while the values are still oscillating. Check `converged` and `max_residual`, and do not take stable extrema as a sign that the amount of smoothing is right. ### Probability mass and subject occupation | Function | Purpose | |---|---| | [`fit.density()`](https://pgajer.github.io/geosmooth/reference/fit.density.html) | Fit empirical or graph-random-walk mass estimates from nonnegative weights on a support. | | [`normalize.density()`](https://pgajer.github.io/geosmooth/reference/normalize.density.html) | Convert a numeric field or supported fitted object to probability mass, with normalization diagnostics. | | [`fit.subject.od()`](https://pgajer.github.io/geosmooth/reference/fit.subject.od.html) | Convert repeated visit indices into counts and fit a subject-occupation distribution. | ```{r occupation} support <- matrix(seq(0, 1, length.out = 6), ncol = 1) occupation <- fit.subject.od( support, subject.index = c(1L, 3L, 3L, 6L), method = "empirical" ) occupation$rho sum(occupation$rho) ``` In this example the third support point is visited twice and the first and sixth once each. `rho` is a vector of probability **masses**, one for each support point, that sums to one. It is not a density per unit volume. [`normalize.density()`](https://pgajer.github.io/geosmooth/reference/normalize.density.html) converts a fitted field to such a vector; it does not fit anything. If the field has negative values, they are set to zero before normalizing, and the result reports how much was changed. [`fit.subject.od()`](https://pgajer.github.io/geosmooth/reference/fit.subject.od.html) offers more methods than [`fit.density()`](https://pgajer.github.io/geosmooth/reference/fit.density.html), because it can also fit one of the regression smoothers and normalize the result. Two kinds of cross-validation occur here and should not be confused. Visit-level cross-validation holds out visits and takes its folds from `visit.foldid`. The cross-validation of a regression smoother holds out rows of the support and takes its folds from `foldid`, or from the argument of that name in the chosen method. ### Experimental transport methods | Function | Purpose | |---|---| | [`pttf.geometry()`](https://pgajer.github.io/geosmooth/reference/pttf.geometry.html) | Build local tangent frames and their transport across a graph. | | [`pttf.operator()`](https://pgajer.github.io/geosmooth/reference/pttf.operator.html) | Construct parallel-transport trend-filtering differences from that geometry. | | [`pttf.operator.filter.rows()`](https://pgajer.github.io/geosmooth/reference/pttf.operator.filter.rows.html) | Select operator rows while maintaining provenance and related operator data. | | [`fit.pttf.trend.filtering()`](https://pgajer.github.io/geosmooth/reference/fit.pttf.trend.filtering.html) | Fit a parallel-transport trend filter with an absolute-value or quadratic penalty. | | [`transported.graph.hessian.operator()`](https://pgajer.github.io/geosmooth/reference/transported.graph.hessian.operator.html) | Build a separate experimental graph Hessian diagnostic with several direction-matching rules. | Derivatives estimated at neighboring points are expressed in different local coordinate frames. Parallel transport aligns the frames so that the derivatives can be compared. The functions in this table are experimental, and they return detailed diagnostics about the frames, the operator, and the solver. `transported.graph.hessian.operator()` serves a different purpose from [`pttf.operator()`](https://pgajer.github.io/geosmooth/reference/pttf.operator.html): it builds a diagnostic operator and does not lead to a fitted response. ### Synthetic datasets: specifications, responses, and reproducibility | Function | Purpose | |---|---| | [`synthetic.spec()`](https://pgajer.github.io/geosmooth/reference/synthetic.spec.html) | Combine geometry, sampling, truth, and response descriptors without drawing data. | | [`materialize.synthetic()`](https://pgajer.github.io/geosmooth/reference/materialize.synthetic.html) | Draw a dataset from a specification with an explicit seed and random-number policy. | | [`materialize.synthetic.instance()`](https://pgajer.github.io/geosmooth/reference/materialize.synthetic.instance.html) | Reproduce a registered frozen instance and optionally validate its checksum. | | [`synthetic.registry.ids()`](https://pgajer.github.io/geosmooth/reference/synthetic.registry.ids.html) | List maintained recipe identifiers. | | [`synthetic.registry.spec()`](https://pgajer.github.io/geosmooth/reference/synthetic.registry.spec.html) | Look up a recipe specification; some recipe families allow parameter overrides. | | [`synthetic.registry.seed()`](https://pgajer.github.io/geosmooth/reference/synthetic.registry.seed.html) | Reproduce legacy SSRHE recipe seeds; this is not a seed generator for every registry family. | | [`validate.synthetic.dataset()`](https://pgajer.github.io/geosmooth/reference/validate.synthetic.dataset.html) | Check the structure and consistency of a materialized dataset. | | [`compare.synthetic.dataset()`](https://pgajer.github.io/geosmooth/reference/compare.synthetic.dataset.html) | Compare two datasets with explicit numerical tolerances. | | [`synthetic.dataset.checksum()`](https://pgajer.github.io/geosmooth/reference/synthetic.dataset.checksum.html) | Compute a content checksum for reproducibility checks. | | [`synthetic.sampling.stratified()`](https://pgajer.github.io/geosmooth/reference/synthetic.sampling.stratified.html) | Describe allocation between the supported synthetic strata. | | [`synthetic.truth.named()`](https://pgajer.github.io/geosmooth/reference/synthetic.truth.named.html) | Select a named truth specification with optional parameters. | | [`synthetic.truth.polynomial()`](https://pgajer.github.io/geosmooth/reference/synthetic.truth.polynomial.html) | Specify polynomial truth in the chosen evaluation coordinates. | | [`synthetic.truth.gaussian.mixture()`](https://pgajer.github.io/geosmooth/reference/synthetic.truth.gaussian.mixture.html) | Specify Gaussian-mixture truth using centers, scales, and weights. | | [`synthetic.truth.occupation.mixture()`](https://pgajer.github.io/geosmooth/reference/synthetic.truth.occupation.mixture.html) | Specify occupation-mixture truth with covariance and probability controls. | | [`synthetic.truth.logit()`](https://pgajer.github.io/geosmooth/reference/synthetic.truth.logit.html) | Specify a logit-based truth with prevalence and clipping controls. | | [`synthetic.response.gaussian()`](https://pgajer.github.io/geosmooth/reference/synthetic.response.gaussian.html) | Specify Gaussian response noise with a supplied standard deviation. | | [`synthetic.response.heteroskedastic.gaussian()`](https://pgajer.github.io/geosmooth/reference/synthetic.response.heteroskedastic.gaussian.html) | Specify Gaussian noise whose scale depends on truth. | | [`synthetic.response.clustered.gaussian()`](https://pgajer.github.io/geosmooth/reference/synthetic.response.clustered.gaussian.html) | Specify within-cluster correlated Gaussian noise. | | [`synthetic.response.laplace.outlier()`](https://pgajer.github.io/geosmooth/reference/synthetic.response.laplace.outlier.html) | Specify Laplace noise and an outlier component. | | [`synthetic.response.bernoulli()`](https://pgajer.github.io/geosmooth/reference/synthetic.response.bernoulli.html) | Specify binary response draws and optional positive-count requirements. | The functions whose names begin with `synthetic.` only describe a data set; no random numbers are drawn until you call [`materialize.synthetic()`](https://pgajer.github.io/geosmooth/reference/materialize.synthetic.html). The result is a `synthetic_dataset` object with `print()`, `plot()`, and `as.data.frame()` methods. `vignette("synthetic-datasets", package = "geosmooth")` gives complete examples. For new work, use the random-number policy `"named.stream.v1"`. The other seed and algorithm settings exist so that data sets generated by earlier versions can be reproduced exactly; leave them at their defaults unless that is your aim. ### Geometry constructors and helpers supplied by dgraphs The following functions were exported by `geosmooth` 0.1.0 and are now provided by `dgraphs`. In existing code, replace `geosmooth::` with `dgraphs::`. Their help pages are in `dgraphs`; see, for example, `help("synthetic.circle", package = "dgraphs")`. | Function | Purpose | |---|---| | `dgraphs::synthetic.circle()` | Describe a circle or circular arc and its embedding. | | `dgraphs::synthetic.helix()` | Describe a helical curve. | | `dgraphs::synthetic.trefoil()` | Describe a trefoil curve. | | `dgraphs::synthetic.torus.patch()` | Describe a patch of a torus. | | `dgraphs::synthetic.sphere.cap()` | Describe a spherical cap. | | `dgraphs::synthetic.quadform()` | Describe a flat or quadratic geometry with intrinsic and ambient dimensions. | | `dgraphs::synthetic.simplex()` | Describe a simplex with a given number of parts. | | `dgraphs::synthetic.stratified()` | Combine strata and optional junctions. | | `dgraphs::synthetic.stratum.point()` | Describe a point stratum. | | `dgraphs::synthetic.stratum.segment()` | Describe a line-segment stratum. | | `dgraphs::synthetic.stratum.rectangle()` | Describe a rectangular stratum. | | `dgraphs::synthetic.point.line.junction()` | Describe a point-and-line junction geometry. | | `dgraphs::embed.synthetic.geometry()` | Map latent coordinates to the ambient space. | | `dgraphs::edge.lengths.synthetic.geometry()` | Evaluate geometry-aware lengths between latent endpoints. | | `dgraphs::quadform.gradient()` | Evaluate the gradient of a quadratic geometry. | | `dgraphs::quadform.metric()` | Evaluate its induced metric. | ### Sampling constructors supplied by dgraphs | Function | Purpose | |---|---| | `dgraphs::synthetic.sampling.uniform.interval()` | Describe uniform draws on an interval. | | `dgraphs::synthetic.sampling.uniform.box()` | Describe uniform draws in a box. | | `dgraphs::synthetic.sampling.uniform.rectangle()` | Describe uniform draws in a rectangle with its specified draw algorithm. | | `dgraphs::synthetic.sampling.uniform.disk()` | Describe uniform draws in a disk. | | `dgraphs::synthetic.sampling.grid.interval()` | Describe an interval grid with an endpoint convention. | | `dgraphs::synthetic.sampling.gapped.uniform()` | Describe sampling across separated intervals. | | `dgraphs::synthetic.sampling.truncated.normal()` | Describe bounded Gaussian draws. | | `dgraphs::synthetic.sampling.clustered()` | Describe clustered observations around random centers. | | `dgraphs::synthetic.sampling.dirichlet.zeros()` | Describe Dirichlet sampling with structural zeros. | Two sampling descriptions of similar regions do not necessarily draw the same random numbers, because each description also fixes the order of the draws and the algorithm. ## Finding detailed help The function links in this guide open the online reference. The same help pages are installed with the package and need no internet connection: use `help("fit.lps", package = "geosmooth")`, with the name of the function you want. `help("geosmooth-package", package = "geosmooth")` lists the main functions by task, and `browseVignettes("geosmooth")` lists the installed guides. The [synthetic datasets guide](synthetic-datasets.html) develops reproducible geometry, sampling, truth, and response specifications in more detail. ```{r help-navigation, eval=FALSE} help(package = "geosmooth") help("geosmooth-package", package = "geosmooth") help("fit.lps", package = "geosmooth") help("predict.lps", package = "geosmooth") vignette("function-guide", package = "geosmooth") vignette("synthetic-datasets", package = "geosmooth") ``` The help pages give the exact arguments, defaults, return values, and examples. Use this guide to find the right function, and read its help page before changing its more specialized settings. ## Tuning and numerical settings The help page of each main fitting function groups its arguments into those needed for a first fit and those that control model selection, the neighborhoods, the numerical solver, and the diagnostics that are returned. Start by tuning over a small grid of the statistical settings. Tightening numerical tolerances is not a substitute for that. | Method | Arguments for folds and selection | What is selected, and the main cost | |---|---|---| | LPS / MALPS | `foldid`, `cv.folds`, support/degree/kernel grids | Held-out response selection; candidate combinations multiply fitting work. | | Quadratic Hessian | `lambda.selection`, `cv.control`, `gcv.control` | Label CV or GCV; exact trace calculation is more expensive than sampled traces. | | Hessian L1 | `fold.id`, `nfolds`, `lambda.grid`, `admm.*` | CV excludes unconverged solves; each candidate and fold needs a solve. | | Graph low-pass | `eta.grid`, `n.eigenpairs` | GCV over heat times; a full spectral basis uses quadratic storage. | The argument names differ among the methods because the selection criteria differ; giving them the same names would not make the criteria equivalent. `smoother.matrix()` returns a dense matrix with one row per evaluation point and one column per training point. Stored candidate fits and solver histories also take memory, so request them only when you intend to examine them. The catalog above covers every function exported by `geosmooth` 0.2.0. The geometry and sampling functions belong to `dgraphs`. Methods for generics such as `print()`, `predict()`, and `summary()` are not listed individually; the package index, `help(package = "geosmooth")`, lists them all. For intrinsic simplex charts, weighted PCA/PLS and their limits, see the [local simplex charts guide](local-simplex-charts.html). For worked synthetic comparisons, held-out prediction figures and reproducible PCA/PLS examples, see [PCA and PLS charts](pca-pls-chart-comparison.html).