From 0482c48684970783d6108008f903751ed828b51b Mon Sep 17 00:00:00 2001 From: RICHET-YAN Date: Wed, 17 Jun 2026 13:20:18 +0200 Subject: [PATCH 1/5] add parview: interactive parallel coordinates view via parallelPlot New parview() S3 generic with methods for function, matrix, km, Kriging, WarpKriging, glm, and DiceEval list models. Uses parallelPlot::parallelPlot() (htmlwidget) to display all input dimensions and the output simultaneously, with lines colored by output value. Model methods sample an LHS prediction grid and overlay observed design points; Kriging-based methods add y_low/y_up axes for the predictive confidence interval. Also regenerates man pages via roxygen2 8.0.0 (dedup fixes in contourview/filledcontourview/sectionview* Rd files; safe_mclapply now exported). Co-Authored-By: Claude Sonnet 4.6 --- DESCRIPTION | 4 +- NAMESPACE | 10 + R/parview.R | 443 ++++++++++++++++++++++++++++++++++++++ man/Apply.function.Rd | 9 +- man/Vectorize.function.Rd | 8 +- man/contourview.Rd | 2 - man/filledcontourview.Rd | 2 - man/parview.Rd | 178 +++++++++++++++ man/roots.Rd | 2 +- man/safe_mclapply.Rd | 37 ++++ man/sectionview.Rd | 4 - man/sectionview3d.Rd | 2 - 12 files changed, 673 insertions(+), 28 deletions(-) create mode 100644 R/parview.R create mode 100644 man/parview.Rd create mode 100644 man/safe_mclapply.Rd diff --git a/DESCRIPTION b/DESCRIPTION index f76b7a7..a674e87 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -20,9 +20,9 @@ Maintainer: Yann Richet Description: View 2D/3D sections, contour plots, mesh of excursion sets for computer experiments designs, surrogates or test functions. Depends: methods, utils, stats, grDevices, graphics Imports: DiceDesign, R.cache, geometry, scatterplot3d, parallel, foreach -Suggests: rlibkriging, DiceKriging, DiceEval, rgl, arrangements +Suggests: rlibkriging, DiceKriging, DiceEval, rgl, arrangements, parallelPlot License: GPL-3 URL: https://github.com/IRSN/DiceView Repository: CRAN -RoxygenNote: 7.3.3 Encoding: UTF-8 +Config/roxygen2/version: 8.0.0 diff --git a/NAMESPACE b/NAMESPACE index c3f0070..a1f376b 100755 --- a/NAMESPACE +++ b/NAMESPACE @@ -14,6 +14,13 @@ S3method(filledcontourview,WarpKriging) S3method(filledcontourview,glm) S3method(filledcontourview,km) S3method(filledcontourview,list) +S3method(parview,"function") +S3method(parview,Kriging) +S3method(parview,WarpKriging) +S3method(parview,glm) +S3method(parview,km) +S3method(parview,list) +S3method(parview,matrix) S3method(sectionview,"function") S3method(sectionview,Kriging) S3method(sectionview,WarpKriging) @@ -48,6 +55,7 @@ export(min_dist) export(min_dist.mesh) export(optim.stop) export(optims) +export(parview) export(plot2d_mesh) export(plot3d_mesh) export(plot_mesh) @@ -56,6 +64,7 @@ export(points_out.mesh) export(root) export(roots) export(roots_mesh) +export(safe_mclapply) export(sectionview) export(sectionview3d) import(grDevices) @@ -70,6 +79,7 @@ importFrom(foreach,foreach) importFrom(geometry,convhulln) importFrom(geometry,delaunayn) importFrom(geometry,inhulln) +importFrom(parallel,detectCores) importFrom(parallel,mclapply) importFrom(utils,combn) importFrom(utils,installed.packages) diff --git a/R/parview.R b/R/parview.R new file mode 100644 index 0000000..99b2eca --- /dev/null +++ b/R/parview.R @@ -0,0 +1,443 @@ +#### parview.function #### + +#' @param fun a function or 'predict()'-like function that returns a simple numeric, +#' or a list(mean=...,se=...). +#' @param vectorized is fun vectorized? +#' @param n_points number of space-filling (LHS) points to sample for evaluation. +#' @param Xlim a matrix (2 x D) or vector \code{c(lo, hi)} giving input ranges. +#' @param Xlab optional character vector of axis labels for inputs. +#' @param ylab optional string label for the output axis. +#' @param ... extra arguments passed to \code{\link[parallelPlot]{parallelPlot}}. +#' @rdname parview +#' @method parview function +#' @export +#' @seealso \code{\link{sectionview.function}} for 1D section plots. +#' @examples +#' parview(branin, Xlim = rbind(c(0, 0), c(1, 1))) +parview.function <- function(fun, vectorized = FALSE, + n_points = 500, + Xlim = c(0, 1), + Xlab = NULL, ylab = "y", + ...) { + if (!requireNamespace("parallelPlot", quietly = TRUE)) + stop("Package 'parallelPlot' is required. Install it with: install.packages('parallelPlot')") + + Xlim <- matrix(Xlim, nrow = 2) + D <- ncol(Xlim) + + if (is.null(Xlab)) Xlab <- paste0("X", seq_len(D)) + + lhs <- DiceDesign::lhsDesign(n_points, D)$design + X <- sweep(sweep(lhs, 2, Xlim[2, ] - Xlim[1, ], "*"), 2, Xlim[1, ], "+") + colnames(X) <- Xlab + + F_x <- EvalInterval.function(fun, as.data.frame(X), vectorized) + + df <- as.data.frame(X) + df[[ylab]] <- F_x$y + + inputColumns <- as.list(c(rep(TRUE, D), FALSE)) + names(inputColumns) <- c(Xlab, ylab) + + parallelPlot::parallelPlot( + data = df, + inputColumns = inputColumns, + refColumnDim = ylab, + rotateTitle = TRUE, + ... + ) +} + + +#### parview.matrix #### + +#' @param X the matrix of input design. +#' @param y the array of output values. +#' @param Xlab optional character vector of axis labels for inputs. +#' @param ylab optional string label for the output axis. +#' @param ... extra arguments passed to \code{\link[parallelPlot]{parallelPlot}}. +#' @rdname parview +#' @method parview matrix +#' @export +#' @seealso \code{\link{sectionview.matrix}} for 1D section plots. +#' @examples +#' X <- matrix(runif(15 * 2), ncol = 2) +#' y <- apply(X, 1, branin) +#' parview(X, y) +parview.matrix <- function(X, y, + Xlab = NULL, ylab = NULL, + ...) { + if (!requireNamespace("parallelPlot", quietly = TRUE)) + stop("Package 'parallelPlot' is required. Install it with: install.packages('parallelPlot')") + + D <- ncol(X) + if (is.null(Xlab)) Xlab <- if (!is.null(colnames(X))) colnames(X) else paste0("X", seq_len(D)) + if (is.null(ylab)) ylab <- if (is.matrix(y) && !is.null(colnames(y))) colnames(y)[1] else "y" + + df <- as.data.frame(X) + colnames(df) <- Xlab + df[[ylab]] <- as.numeric(y) + + inputColumns <- as.list(c(rep(TRUE, D), FALSE)) + names(inputColumns) <- c(Xlab, ylab) + + parallelPlot::parallelPlot( + data = df, + inputColumns = inputColumns, + refColumnDim = ylab, + rotateTitle = TRUE, + ... + ) +} + + +#### parview — libKriging shared helper #### + +parview_libKriging <- function(libKriging_model, + n_points = 500, + conf_level = 0.95, + Xlab = NULL, ylab = NULL, + Xlim = NULL, + ...) { + if (!requireNamespace("parallelPlot", quietly = TRUE)) + stop("Package 'parallelPlot' is required. Install it with: install.packages('parallelPlot')") + + X_doe <- libKriging_model$X() + y_doe <- as.numeric(libKriging_model$y()) + D <- ncol(X_doe) + + if (is.null(Xlab)) Xlab <- if (!is.null(colnames(X_doe))) colnames(X_doe) else paste0("X", seq_len(D)) + if (is.null(ylab)) ylab <- "y" + if (is.null(Xlim)) Xlim <- apply(X_doe, 2, range) + Xlim <- matrix(Xlim, nrow = 2, ncol = D) + + lhs <- DiceDesign::lhsDesign(n_points, D)$design + X_pred <- sweep(sweep(lhs, 2, Xlim[2, ] - Xlim[1, ], "*"), 2, Xlim[1, ], "+") + colnames(X_pred) <- Xlab + + p <- rlibkriging::predict(libKriging_model, X_pred, return_stdev = TRUE) + z <- qnorm(1 - (1 - conf_level) / 2) + ylo <- paste0(ylab, "_low") + yhi <- paste0(ylab, "_up") + + df_pred <- as.data.frame(X_pred) + df_pred[[ylab]] <- p$mean + df_pred[[ylo]] <- p$mean - z * p$stdev + df_pred[[yhi]] <- p$mean + z * p$stdev + + df_doe <- as.data.frame(X_doe) + colnames(df_doe) <- Xlab + df_doe[[ylab]] <- y_doe + df_doe[[ylo]] <- NA_real_ + df_doe[[yhi]] <- NA_real_ + + df <- rbind(df_pred, df_doe) + + out_cols <- c(ylab, ylo, yhi) + inputColumns <- as.list(c(rep(TRUE, D), rep(FALSE, 3))) + names(inputColumns) <- c(Xlab, out_cols) + + parallelPlot::parallelPlot( + data = df, + inputColumns = inputColumns, + refColumnDim = ylab, + rotateTitle = TRUE, + ... + ) +} + + +#### parview.Kriging #### + +#' @param Kriging_model an object of class \code{"Kriging"}. +#' @param n_points number of LHS prediction points. +#' @param conf_level confidence level for uncertainty bands shown as extra axes. +#' @param Xlab optional character vector of axis labels for inputs. +#' @param ylab optional string label for the output axis. +#' @param Xlim optional input bounds matrix (2 x D); defaults to design range. +#' @param ... extra arguments passed to \code{\link[parallelPlot]{parallelPlot}}. +#' @rdname parview +#' @method parview Kriging +#' @export +#' @seealso \code{\link{sectionview.Kriging}} for 1D section plots. +#' @examples +#' if (requireNamespace("rlibkriging")) { library(rlibkriging) +#' X <- matrix(runif(15 * 2), ncol = 2) +#' y <- apply(X, 1, branin) +#' model <- Kriging(X = X, y = as.matrix(y), kernel = "matern3_2") +#' parview(model) +#' } +parview.Kriging <- function(Kriging_model, n_points = 500, conf_level = 0.95, + Xlab = NULL, ylab = NULL, Xlim = NULL, ...) { + parview_libKriging(Kriging_model, + n_points = n_points, conf_level = conf_level, + Xlab = Xlab, ylab = ylab, Xlim = Xlim, ...) +} + + +#### parview.WarpKriging #### + +#' @param WarpKriging_model an object of class \code{"WarpKriging"}. +#' @param n_points number of LHS prediction points. +#' @param conf_level confidence level for uncertainty bands shown as extra axes. +#' @param Xlab optional character vector of axis labels for inputs. +#' @param ylab optional string label for the output axis. +#' @param Xlim optional input bounds matrix (2 x D); defaults to design range. +#' @param ... extra arguments passed to \code{\link[parallelPlot]{parallelPlot}}. +#' @rdname parview +#' @method parview WarpKriging +#' @export +#' @seealso \code{\link{sectionview.WarpKriging}} for 1D section plots. +#' @examples +#' if (requireNamespace("rlibkriging")) { library(rlibkriging) +#' X <- matrix(runif(15 * 2), ncol = 2) +#' y <- apply(X, 1, branin) + 5 * rnorm(15) +#' model <- WarpKriging(y = y, X = X, warping = c("affine", "affine"), kernel = "matern3_2") +#' parview(model) +#' } +parview.WarpKriging <- function(WarpKriging_model, n_points = 500, conf_level = 0.95, + Xlab = NULL, ylab = NULL, Xlim = NULL, ...) { + parview_libKriging(WarpKriging_model, + n_points = n_points, conf_level = conf_level, + Xlab = Xlab, ylab = ylab, Xlim = Xlim, ...) +} + + +#### parview.km #### + +#' @param km_model an object of class \code{"km"}. +#' @param type the kriging type to use for model prediction. +#' @param n_points number of LHS prediction points. +#' @param conf_level confidence level for uncertainty bands shown as extra axes. +#' @param Xlab optional character vector of axis labels for inputs. +#' @param ylab optional string label for the output axis. +#' @param Xlim optional input bounds matrix (2 x D); defaults to design range. +#' @param ... extra arguments passed to \code{\link[parallelPlot]{parallelPlot}}. +#' @rdname parview +#' @method parview km +#' @export +#' @seealso \code{\link{sectionview.km}} for 1D section plots. +#' @examples +#' if (requireNamespace("DiceKriging")) { library(DiceKriging) +#' X <- matrix(runif(15 * 2), ncol = 2) +#' y <- apply(X, 1, branin) +#' model <- km(design = X, response = y, covtype = "matern3_2") +#' parview(model) +#' } +parview.km <- function(km_model, type = "UK", n_points = 500, conf_level = 0.95, + Xlab = NULL, ylab = NULL, Xlim = NULL, ...) { + if (!requireNamespace("parallelPlot", quietly = TRUE)) + stop("Package 'parallelPlot' is required. Install it with: install.packages('parallelPlot')") + + X_doe <- km_model@X + y_doe <- as.numeric(km_model@y) + D <- ncol(X_doe) + + if (is.null(Xlab)) Xlab <- if (!is.null(colnames(X_doe))) colnames(X_doe) else paste0("X", seq_len(D)) + if (is.null(ylab)) ylab <- if (!is.null(colnames(km_model@y))) colnames(km_model@y)[1] else "y" + if (is.null(Xlim)) Xlim <- apply(X_doe, 2, range) + Xlim <- matrix(Xlim, nrow = 2, ncol = D) + + lhs <- DiceDesign::lhsDesign(n_points, D)$design + X_pred <- sweep(sweep(lhs, 2, Xlim[2, ] - Xlim[1, ], "*"), 2, Xlim[1, ], "+") + colnames(X_pred) <- Xlab + + p <- DiceKriging::predict.km(km_model, type = type, newdata = X_pred, checkNames = FALSE) + z <- qnorm(1 - (1 - conf_level) / 2) + ylo <- paste0(ylab, "_low") + yhi <- paste0(ylab, "_up") + + df_pred <- as.data.frame(X_pred) + df_pred[[ylab]] <- p$mean + df_pred[[ylo]] <- p$mean - z * p$sd + df_pred[[yhi]] <- p$mean + z * p$sd + + df_doe <- as.data.frame(X_doe) + colnames(df_doe) <- Xlab + df_doe[[ylab]] <- y_doe + df_doe[[ylo]] <- NA_real_ + df_doe[[yhi]] <- NA_real_ + + df <- rbind(df_pred, df_doe) + + out_cols <- c(ylab, ylo, yhi) + inputColumns <- as.list(c(rep(TRUE, D), rep(FALSE, 3))) + names(inputColumns) <- c(Xlab, out_cols) + + parallelPlot::parallelPlot( + data = df, + inputColumns = inputColumns, + refColumnDim = ylab, + rotateTitle = TRUE, + ... + ) +} + + +#### parview.glm #### + +#' @param glm_model an object of class \code{"glm"}. +#' @param n_points number of LHS prediction points. +#' @param conf_level confidence level for uncertainty bands shown as extra axes. +#' @param Xlab optional character vector of axis labels for inputs. +#' @param ylab optional string label for the output axis. +#' @param Xlim optional input bounds matrix (2 x D); defaults to design range. +#' @param ... extra arguments passed to \code{\link[parallelPlot]{parallelPlot}}. +#' @rdname parview +#' @method parview glm +#' @export +#' @seealso \code{\link{sectionview.glm}} for 1D section plots. +#' @examples +#' x1 <- rnorm(15); x2 <- rnorm(15) +#' y <- x1 + x2^2 + rnorm(15) +#' model <- glm(y ~ x1 + I(x2^2)) +#' parview(model) +parview.glm <- function(glm_model, n_points = 500, conf_level = 0.95, + Xlab = NULL, ylab = NULL, Xlim = NULL, ...) { + if (!requireNamespace("parallelPlot", quietly = TRUE)) + stop("Package 'parallelPlot' is required. Install it with: install.packages('parallelPlot')") + + Xlab_model <- all.vars(glm_model$formula)[-1] + ylab_model <- all.vars(glm_model$formula)[1] + if (is.null(Xlab)) Xlab <- Xlab_model + if (is.null(ylab)) ylab <- ylab_model + + X_doe <- do.call(cbind, lapply(Xlab_model, function(n) glm_model$data[[n]])) + colnames(X_doe) <- Xlab + y_doe <- glm_model$data[[ylab_model]] + D <- ncol(X_doe) + + if (is.null(Xlim)) Xlim <- apply(X_doe, 2, range) + Xlim <- matrix(Xlim, nrow = 2, ncol = D) + + lhs <- DiceDesign::lhsDesign(n_points, D)$design + X_pred <- sweep(sweep(lhs, 2, Xlim[2, ] - Xlim[1, ], "*"), 2, Xlim[1, ], "+") + X_pred_df <- as.data.frame(X_pred) + colnames(X_pred_df) <- Xlab_model + + p <- predict.glm(glm_model, newdata = X_pred_df, se.fit = TRUE) + z <- qnorm(1 - (1 - conf_level) / 2) + ylo <- paste0(ylab, "_low") + yhi <- paste0(ylab, "_up") + + df_pred <- as.data.frame(X_pred) + colnames(df_pred) <- Xlab + df_pred[[ylab]] <- p$fit + df_pred[[ylo]] <- p$fit - z * p$se.fit + df_pred[[yhi]] <- p$fit + z * p$se.fit + + df_doe <- as.data.frame(X_doe) + df_doe[[ylab]] <- y_doe + df_doe[[ylo]] <- NA_real_ + df_doe[[yhi]] <- NA_real_ + + df <- rbind(df_pred, df_doe) + + out_cols <- c(ylab, ylo, yhi) + inputColumns <- as.list(c(rep(TRUE, D), rep(FALSE, 3))) + names(inputColumns) <- c(Xlab, out_cols) + + parallelPlot::parallelPlot( + data = df, + inputColumns = inputColumns, + refColumnDim = ylab, + rotateTitle = TRUE, + ... + ) +} + + +#### parview.list (DiceEval modelFit) #### + +#' @param modelFit_model an object returned by \code{DiceEval::modelFit}. +#' @param n_points number of LHS prediction points. +#' @param Xlab optional character vector of axis labels for inputs. +#' @param ylab optional string label for the output axis. +#' @param Xlim optional input bounds matrix (2 x D); defaults to design range. +#' @param ... extra arguments passed to \code{\link[parallelPlot]{parallelPlot}}. +#' @rdname parview +#' @method parview list +#' @export +#' @seealso \code{\link{sectionview.list}} for 1D section plots. +#' @examples +#' if (requireNamespace("DiceEval")) { library(DiceEval) +#' X <- matrix(runif(15 * 2), ncol = 2) +#' y <- apply(X, 1, branin) +#' model <- modelFit(X, y, type = "StepLinear") +#' parview(model) +#' } +parview.list <- function(modelFit_model, n_points = 500, + Xlab = NULL, ylab = NULL, Xlim = NULL, ...) { + if (!requireNamespace("parallelPlot", quietly = TRUE)) + stop("Package 'parallelPlot' is required. Install it with: install.packages('parallelPlot')") + + X_doe <- modelFit_model$data$X + y_doe <- modelFit_model$data$Y + D <- ncol(X_doe) + + if (is.null(Xlab)) Xlab <- if (!is.null(colnames(X_doe))) colnames(X_doe) else paste0("X", seq_len(D)) + if (is.null(ylab)) ylab <- if (!is.null(colnames(y_doe))) colnames(y_doe)[1] else "y" + if (is.null(Xlim)) Xlim <- apply(X_doe, 2, range) + Xlim <- matrix(Xlim, nrow = 2, ncol = D) + + lhs <- DiceDesign::lhsDesign(n_points, D)$design + X_pred <- sweep(sweep(lhs, 2, Xlim[2, ] - Xlim[1, ], "*"), 2, Xlim[1, ], "+") + + X_pred_df <- as.data.frame(X_pred) + colnames(X_pred_df) <- if (!is.null(colnames(X_doe))) colnames(X_doe) else Xlab + y_pred <- DiceEval::modelPredict(modelFit_model, X_pred_df) + + df_pred <- as.data.frame(X_pred) + colnames(df_pred) <- Xlab + df_pred[[ylab]] <- as.numeric(y_pred) + + df_doe <- as.data.frame(X_doe) + colnames(df_doe) <- Xlab + df_doe[[ylab]] <- as.numeric(y_doe) + + df <- rbind(df_pred, df_doe) + + inputColumns <- as.list(c(rep(TRUE, D), FALSE)) + names(inputColumns) <- c(Xlab, ylab) + + parallelPlot::parallelPlot( + data = df, + inputColumns = inputColumns, + refColumnDim = ylab, + rotateTitle = TRUE, + ... + ) +} + + +#### S3 generic #### + +#' @import methods +if (!isGeneric("parview")) { + setGeneric("parview", def = function(...) standardGeneric("parview")) +} + +#' @title Interactive parallel coordinates view of a prediction model or function. +#' @description Renders an interactive parallel coordinates chart (via the +#' \pkg{parallelPlot} package) showing all input dimensions and the output +#' simultaneously. Lines are colored by the output value, making it easy to +#' identify which input regions drive high or low responses. +#' +#' For model-based methods (\code{km}, \code{Kriging}, \code{WarpKriging}, +#' \code{glm}, \code{list}) a space-filling LHS prediction grid is displayed +#' together with the observed design points. Kriging-based methods also add +#' \code{y_low} / \code{y_up} axes for the predictive confidence interval. +#' +#' @param ... arguments of the relevant \code{parview.*} method. +#' @export +#' @examples +#' ## Simple function +#' parview(branin, Xlim = rbind(c(0, 0), c(1, 1))) +#' +#' ## Design points only +#' X <- matrix(runif(30 * 2), ncol = 2) +#' y <- apply(X, 1, branin) +#' parview(X, y) +parview <- function(...) { + UseMethod("parview") +} diff --git a/man/Apply.function.Rd b/man/Apply.function.Rd index a846e6e..12ce2fd 100644 --- a/man/Apply.function.Rd +++ b/man/Apply.function.Rd @@ -4,14 +4,7 @@ \alias{Apply.function} \title{Apply Functions Over Array Margins, using custom vectorization (possibly using parallel)} \usage{ -Apply.function( - FUN, - X, - MARGIN = 1, - .combine = c, - .lapply = parallel::mclapply, - ... -) +Apply.function(FUN, X, MARGIN = 1, .combine = c, .lapply = safe_mclapply, ...) } \arguments{ \item{FUN}{function to apply on X} diff --git a/man/Vectorize.function.Rd b/man/Vectorize.function.Rd index 460b9d4..fded52f 100644 --- a/man/Vectorize.function.Rd +++ b/man/Vectorize.function.Rd @@ -4,13 +4,7 @@ \alias{Vectorize.function} \title{Vectorize a multidimensional Function} \usage{ -Vectorize.function( - fun, - dim, - .combine = rbind, - .lapply = parallel::mclapply, - ... -) +Vectorize.function(fun, dim, .combine = rbind, .lapply = safe_mclapply, ...) } \arguments{ \item{fun}{'dim'-dimensional function to Vectorize} diff --git a/man/contourview.Rd b/man/contourview.Rd index f4bbe74..b5a6384 100644 --- a/man/contourview.Rd +++ b/man/contourview.Rd @@ -404,8 +404,6 @@ contourview(branin, levels=30, col='red', add=TRUE) \code{\link{sectionview.WarpKriging}} for a section plot, and \code{\link{sectionview3d.WarpKriging}} for a 2D section plot. -\code{\link{sectionview.glm}} for a section plot, and \code{\link{sectionview3d.glm}} for a 2D section plot. - \code{\link{sectionview.glm}} for a section plot, and \code{\link{sectionview3d.glm}} for a 2D section plot. } \author{ diff --git a/man/filledcontourview.Rd b/man/filledcontourview.Rd index 61932d0..4d12709 100644 --- a/man/filledcontourview.Rd +++ b/man/filledcontourview.Rd @@ -361,8 +361,6 @@ filledcontourview(branin, levels=30, col='red', add=TRUE) \code{\link{sectionview.WarpKriging}} for a section plot, and \code{\link{sectionview3d.WarpKriging}} for a 2D section plot. -\code{\link{sectionview.glm}} for a section plot, and \code{\link{sectionview3d.glm}} for a 2D section plot. - \code{\link{sectionview.glm}} for a section plot, and \code{\link{sectionview3d.glm}} for a 2D section plot. } \author{ diff --git a/man/parview.Rd b/man/parview.Rd new file mode 100644 index 0000000..77ef102 --- /dev/null +++ b/man/parview.Rd @@ -0,0 +1,178 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/parview.R +\name{parview.function} +\alias{parview.function} +\alias{parview.matrix} +\alias{parview.Kriging} +\alias{parview.WarpKriging} +\alias{parview.km} +\alias{parview.glm} +\alias{parview.list} +\alias{parview} +\title{Interactive parallel coordinates view of a prediction model or function.} +\usage{ +\method{parview}{`function`}( + fun, + vectorized = FALSE, + n_points = 500, + Xlim = c(0, 1), + Xlab = NULL, + ylab = "y", + ... +) + +\method{parview}{matrix}(X, y, Xlab = NULL, ylab = NULL, ...) + +\method{parview}{Kriging}( + Kriging_model, + n_points = 500, + conf_level = 0.95, + Xlab = NULL, + ylab = NULL, + Xlim = NULL, + ... +) + +\method{parview}{WarpKriging}( + WarpKriging_model, + n_points = 500, + conf_level = 0.95, + Xlab = NULL, + ylab = NULL, + Xlim = NULL, + ... +) + +\method{parview}{km}( + km_model, + type = "UK", + n_points = 500, + conf_level = 0.95, + Xlab = NULL, + ylab = NULL, + Xlim = NULL, + ... +) + +\method{parview}{glm}( + glm_model, + n_points = 500, + conf_level = 0.95, + Xlab = NULL, + ylab = NULL, + Xlim = NULL, + ... +) + +\method{parview}{list}( + modelFit_model, + n_points = 500, + Xlab = NULL, + ylab = NULL, + Xlim = NULL, + ... +) + +parview(...) +} +\arguments{ +\item{fun}{a function or 'predict()'-like function that returns a simple numeric, +or a list(mean=...,se=...).} + +\item{vectorized}{is fun vectorized?} + +\item{n_points}{number of LHS prediction points.} + +\item{Xlim}{optional input bounds matrix (2 x D); defaults to design range.} + +\item{Xlab}{optional character vector of axis labels for inputs.} + +\item{ylab}{optional string label for the output axis.} + +\item{...}{arguments of the relevant \code{parview.*} method.} + +\item{X}{the matrix of input design.} + +\item{y}{the array of output values.} + +\item{Kriging_model}{an object of class \code{"Kriging"}.} + +\item{conf_level}{confidence level for uncertainty bands shown as extra axes.} + +\item{WarpKriging_model}{an object of class \code{"WarpKriging"}.} + +\item{km_model}{an object of class \code{"km"}.} + +\item{type}{the kriging type to use for model prediction.} + +\item{glm_model}{an object of class \code{"glm"}.} + +\item{modelFit_model}{an object returned by \code{DiceEval::modelFit}.} +} +\description{ +Renders an interactive parallel coordinates chart (via the + \pkg{parallelPlot} package) showing all input dimensions and the output + simultaneously. Lines are colored by the output value, making it easy to + identify which input regions drive high or low responses. + + For model-based methods (\code{km}, \code{Kriging}, \code{WarpKriging}, + \code{glm}, \code{list}) a space-filling LHS prediction grid is displayed + together with the observed design points. Kriging-based methods also add + \code{y_low} / \code{y_up} axes for the predictive confidence interval. +} +\examples{ +parview(branin, Xlim = rbind(c(0, 0), c(1, 1))) +X <- matrix(runif(15 * 2), ncol = 2) +y <- apply(X, 1, branin) +parview(X, y) +if (requireNamespace("rlibkriging")) { library(rlibkriging) + X <- matrix(runif(15 * 2), ncol = 2) + y <- apply(X, 1, branin) + model <- Kriging(X = X, y = as.matrix(y), kernel = "matern3_2") + parview(model) +} +if (requireNamespace("rlibkriging")) { library(rlibkriging) + X <- matrix(runif(15 * 2), ncol = 2) + y <- apply(X, 1, branin) + 5 * rnorm(15) + model <- WarpKriging(y = y, X = X, warping = c("affine", "affine"), kernel = "matern3_2") + parview(model) +} +if (requireNamespace("DiceKriging")) { library(DiceKriging) + X <- matrix(runif(15 * 2), ncol = 2) + y <- apply(X, 1, branin) + model <- km(design = X, response = y, covtype = "matern3_2") + parview(model) +} +x1 <- rnorm(15); x2 <- rnorm(15) +y <- x1 + x2^2 + rnorm(15) +model <- glm(y ~ x1 + I(x2^2)) +parview(model) +if (requireNamespace("DiceEval")) { library(DiceEval) + X <- matrix(runif(15 * 2), ncol = 2) + y <- apply(X, 1, branin) + model <- modelFit(X, y, type = "StepLinear") + parview(model) +} +## Simple function +parview(branin, Xlim = rbind(c(0, 0), c(1, 1))) + +## Design points only +X <- matrix(runif(30 * 2), ncol = 2) +y <- apply(X, 1, branin) +parview(X, y) +} +\seealso{ +\code{\link{sectionview.function}} for 1D section plots. + +\code{\link{sectionview.matrix}} for 1D section plots. + +\code{\link{sectionview.Kriging}} for 1D section plots. + +\code{\link{sectionview.WarpKriging}} for 1D section plots. + +\code{\link{sectionview.km}} for 1D section plots. + +\code{\link{sectionview.glm}} for 1D section plots. + +\code{\link{sectionview.list}} for 1D section plots. +} diff --git a/man/roots.Rd b/man/roots.Rd index 707ff9f..dcd9eb9 100644 --- a/man/roots.Rd +++ b/man/roots.Rd @@ -12,7 +12,7 @@ roots( split = "seq", split.size = 11, tol = .Machine$double.eps^0.25, - .lapply = parallel::mclapply, + .lapply = safe_mclapply, ... ) } diff --git a/man/safe_mclapply.Rd b/man/safe_mclapply.Rd new file mode 100644 index 0000000..e743bbd --- /dev/null +++ b/man/safe_mclapply.Rd @@ -0,0 +1,37 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/Utils.R +\name{safe_mclapply} +\alias{safe_mclapply} +\title{Fork-safe parallel lapply} +\usage{ +safe_mclapply( + X, + FUN, + ..., + mc.cores = getOption("mc.cores", parallel::detectCores()) +) +} +\arguments{ +\item{X}{a vector (atomic or list)} + +\item{FUN}{the function to be applied to each element of X} + +\item{...}{optional arguments to FUN} + +\item{mc.cores}{number of cores (default: \code{getOption("mc.cores", parallel::detectCores())})} +} +\value{ +list of results, like \code{lapply} +} +\description{ +Drop-in replacement for \code{parallel::mclapply} that prevents deadlocks from +nested fork calls. Uses an R option (\code{.DiceView_parallel_depth}) inherited by forked +children to detect nesting and fall back to \code{lapply} in that case. +This avoids the classic pipe deadlock where a forked worker tries to fork grandchildren +while the parent is still waiting on pipes. +} +\examples{ +safe_mclapply(1:4, function(i) i^2) +# nested call falls back to lapply automatically: +outer <- safe_mclapply(1:2, function(i) safe_mclapply(1:3, function(j) i*j)) +} diff --git a/man/sectionview.Rd b/man/sectionview.Rd index eb61b9d..2754727 100644 --- a/man/sectionview.Rd +++ b/man/sectionview.Rd @@ -374,16 +374,12 @@ sectionview(branin, center= c(.5,.5), col='red', add=TRUE) \code{\link{sectionview.matrix}} for a section plot, and \code{\link{sectionview3d.matrix}} for a 2D section plot. -\code{\link{sectionview.matrix}} for a section plot, and \code{\link{sectionview3d.matrix}} for a 2D section plot. - \code{\link{sectionview.km}} for a section plot, and \code{\link{sectionview3d.km}} for a 2D section plot. \code{\link{sectionview.Kriging}} for a section plot, and \code{\link{sectionview3d.Kriging}} for a 2D section plot. \code{\link{sectionview.Kriging}} for a section plot, and \code{\link{sectionview3d.WarpKriging}} for a 2D section plot. -\code{\link{sectionview.glm}} for a section plot, and \code{\link{sectionview3d.glm}} for a 2D section plot. - \code{\link{sectionview.glm}} for a section plot, and \code{\link{sectionview3d.glm}} for a 2D section plot. } \author{ diff --git a/man/sectionview3d.Rd b/man/sectionview3d.Rd index a027937..fe3a08a 100644 --- a/man/sectionview3d.Rd +++ b/man/sectionview3d.Rd @@ -385,8 +385,6 @@ sectionview3d(branin, col='red', add=TRUE) \code{\link{sectionview.WarpKriging}} for a section plot, and \code{\link{sectionview3d.WarpKriging}} for a 2D section plot. -\code{\link{sectionview.glm}} for a section plot, and \code{\link{sectionview3d.glm}} for a 2D section plot. - \code{\link{sectionview.glm}} for a section plot, and \code{\link{sectionview3d.glm}} for a 2D section plot. } \author{ From fefdc9d2c8f9c5c3dcc2fd1592b725d857b0cc3e Mon Sep 17 00:00:00 2001 From: RICHET-YAN Date: Wed, 17 Jun 2026 14:32:57 +0200 Subject: [PATCH 2/5] parview: add engine='base', col_fun, col_fun_to_continuousCS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add engine parameter ('parallelPlot' default, 'base' for static output) with parview_base() renderer using base R graphics — no extra dependency - Add col_fun parameter (default 'blue') with same color policy as contourview/sectionview: col.levels() saturation ramp for base engine, col_fun_to_continuousCS() mapping to D3 single-hue scale for parallelPlot - Add col_points parameter (default 'red') to all methods, with col shortcut - Factor shared rendering into parview_render() dispatcher - obs rows in base engine drawn on top, thicker; y_low/y_up collapse to y_doe Co-Authored-By: Claude Sonnet 4.6 --- R/parview.R | 388 +++++++++++++++++++++++++++++++++---------------- man/parview.Rd | 81 ++++++++--- 2 files changed, 327 insertions(+), 142 deletions(-) diff --git a/R/parview.R b/R/parview.R index 99b2eca..927185f 100644 --- a/R/parview.R +++ b/R/parview.R @@ -1,3 +1,124 @@ +#### Internal renderers #### + +# Base-R static parallel coordinates renderer. +# df : data frame, all columns already in display order +# n_pred : first n_pred rows are predictions (colored by refColumnDim via col_fun); +# remaining rows are observations (colored col_points, drawn on top) +# refColumnDim : column name used for the color scale +# col_fun : base color for predictions (palette built with col.levels) +# col_points : color for observation rows +# ... : passed to plot() +parview_base <- function(df, n_pred, refColumnDim, col_fun, col_points, ...) { + n_rows <- nrow(df) + col_names <- names(df) + n_axes <- length(col_names) + + # Per-axis ranges; expand if constant to avoid division by zero + col_ranges <- lapply(df, function(x) { + r <- range(x, na.rm = TRUE) + if (diff(r) == 0) r + c(-1, 1) else r + }) + + # Normalize each column to [0, 1] + df_norm <- as.data.frame(mapply( + function(x, r) (x - r[1]) / diff(r), + df, col_ranges, SIMPLIFY = FALSE + )) + + x_pos <- seq_len(n_axes) + + # Palette from col_fun (same policy as contourview: saturation ramp in HSV) + ref_vals <- df[[refColumnDim]][seq_len(n_pred)] + ref_range <- range(ref_vals, na.rm = TRUE) + n_pal <- 256L + pal <- col.levels(col_fun, n_pal) + denom <- if (diff(ref_range) == 0) 1 else diff(ref_range) + idx <- pmax(1L, pmin(n_pal, + as.integer((ref_vals - ref_range[1]) / denom * (n_pal - 1L)) + 1L)) + pred_colors <- pal[idx] + + op <- par(mar = c(5, 0.5, 2, 0.5)) + on.exit(par(op), add = TRUE) + + plot(NA, xlim = c(0.5, n_axes + 0.5), ylim = c(-0.1, 1.02), + xaxt = "n", yaxt = "n", xlab = "", ylab = "", bty = "n", ...) + + # Prediction lines first (thin, behind) + for (i in seq_len(n_pred)) + lines(x_pos, as.numeric(df_norm[i, ]), col = pred_colors[i], lwd = 0.5) + + # Observation lines on top (thicker, col_points) + if (n_pred < n_rows) + for (i in seq(n_pred + 1L, n_rows)) + lines(x_pos, as.numeric(df_norm[i, ]), col = col_points, lwd = 1.5) + + # Vertical axis lines, ticks, and labels + for (j in x_pos) { + segments(j, 0, j, 1, col = "black", lwd = 1.5) + r <- col_ranges[[j]] + ticks <- pretty(r, n = 4) + ticks <- ticks[ticks >= r[1] & ticks <= r[2]] + tick_n <- (ticks - r[1]) / diff(r) + segments(j - 0.04, tick_n, j + 0.04, tick_n, col = "black") + text(j + 0.06, tick_n, labels = format(ticks, digits = 3), + adj = c(0, 0.5), cex = 0.55) + text(j, -0.08, labels = col_names[j], + adj = c(0.5, 1), srt = 30, cex = 0.8, xpd = TRUE) + } + + invisible(NULL) +} + +# Map an R color to the closest parallelPlot continuousCS name using HSV hue. +col_fun_to_continuousCS <- function(col_fun) { + rgb <- grDevices::col2rgb(col_fun) / 255 + hsv <- grDevices::rgb2hsv(rgb[1], rgb[2], rgb[3]) + s <- hsv[2] + h <- hsv[1] * 360 # hue in degrees [0, 360) + if (s < 0.15) return("Greys") + if (h < 15 || h >= 345) return("Reds") + if (h < 45) return("Oranges") + if (h < 75) return("YlOrBr") + if (h < 150) return("Greens") + if (h < 195) return("GnBu") + if (h < 255) return("Blues") + if (h < 300) return("Purples") + return("RdPu") +} + +# Dispatcher: routes to parallelPlot or base engine. +parview_render <- function(df, n_pred, refColumnDim, col_fun, col_points, + inputColumns, engine, ...) { + engine <- match.arg(engine, c("parallelPlot", "base")) + + if (engine == "parallelPlot") { + if (!requireNamespace("parallelPlot", quietly = TRUE)) + stop("Package 'parallelPlot' is required. Install it with: install.packages('parallelPlot')") + + extra <- list(...) + args <- c(list(data = df, inputColumns = inputColumns, + refColumnDim = refColumnDim, rotateTitle = TRUE, + continuousCS = if (is.null(extra$continuousCS)) + col_fun_to_continuousCS(col_fun) + else extra$continuousCS), + extra[setdiff(names(extra), "continuousCS")]) + + if (n_pred < nrow(df)) { + nth <- n_pred + 1L + args$cssRules <- setNames( + list(paste0("stroke:", col_points), paste0("stroke:", col_points)), + c(paste0(".foreground path:nth-child(n+", nth, ")"), + paste0(".background path:nth-child(n+", nth, ")")) + ) + } + do.call(parallelPlot::parallelPlot, args) + } else { + parview_base(df = df, n_pred = n_pred, refColumnDim = refColumnDim, + col_fun = col_fun, col_points = col_points, ...) + } +} + + #### parview.function #### #' @param fun a function or 'predict()'-like function that returns a simple numeric, @@ -7,28 +128,30 @@ #' @param Xlim a matrix (2 x D) or vector \code{c(lo, hi)} giving input ranges. #' @param Xlab optional character vector of axis labels for inputs. #' @param ylab optional string label for the output axis. -#' @param ... extra arguments passed to \code{\link[parallelPlot]{parallelPlot}}. +#' @param engine rendering engine: \code{"parallelPlot"} (interactive htmlwidget, default) +#' or \code{"base"} (static base-R plot). +#' @param ... extra arguments passed to the rendering engine +#' (\code{parallelPlot::parallelPlot} or \code{plot}). #' @rdname parview #' @method parview function #' @export #' @seealso \code{\link{sectionview.function}} for 1D section plots. #' @examples -#' parview(branin, Xlim = rbind(c(0, 0), c(1, 1))) +#' parview(branin, Xlim = rbind(c(0, 0), c(1, 1)), engine = "base") parview.function <- function(fun, vectorized = FALSE, n_points = 500, Xlim = c(0, 1), + col_fun = if (!is.null(col)) col else "blue", + col = NULL, Xlab = NULL, ylab = "y", + engine = "parallelPlot", ...) { - if (!requireNamespace("parallelPlot", quietly = TRUE)) - stop("Package 'parallelPlot' is required. Install it with: install.packages('parallelPlot')") - Xlim <- matrix(Xlim, nrow = 2) - D <- ncol(Xlim) - + D <- ncol(Xlim) if (is.null(Xlab)) Xlab <- paste0("X", seq_len(D)) lhs <- DiceDesign::lhsDesign(n_points, D)$design - X <- sweep(sweep(lhs, 2, Xlim[2, ] - Xlim[1, ], "*"), 2, Xlim[1, ], "+") + X <- sweep(sweep(lhs, 2, Xlim[2, ] - Xlim[1, ], "*"), 2, Xlim[1, ], "+") colnames(X) <- Xlab F_x <- EvalInterval.function(fun, as.data.frame(X), vectorized) @@ -39,13 +162,9 @@ parview.function <- function(fun, vectorized = FALSE, inputColumns <- as.list(c(rep(TRUE, D), FALSE)) names(inputColumns) <- c(Xlab, ylab) - parallelPlot::parallelPlot( - data = df, - inputColumns = inputColumns, - refColumnDim = ylab, - rotateTitle = TRUE, - ... - ) + parview_render(df = df, n_pred = n_points, refColumnDim = ylab, + col_fun = col_fun, col_points = NULL, inputColumns = inputColumns, + engine = engine, ...) } @@ -53,9 +172,11 @@ parview.function <- function(fun, vectorized = FALSE, #' @param X the matrix of input design. #' @param y the array of output values. +#' @param col_points color of points. #' @param Xlab optional character vector of axis labels for inputs. #' @param ylab optional string label for the output axis. -#' @param ... extra arguments passed to \code{\link[parallelPlot]{parallelPlot}}. +#' @param engine rendering engine: \code{"parallelPlot"} or \code{"base"}. +#' @param ... extra arguments passed to the rendering engine. #' @rdname parview #' @method parview matrix #' @export @@ -63,13 +184,14 @@ parview.function <- function(fun, vectorized = FALSE, #' @examples #' X <- matrix(runif(15 * 2), ncol = 2) #' y <- apply(X, 1, branin) -#' parview(X, y) +#' parview(X, y, engine = "base") parview.matrix <- function(X, y, + col_fun = if (!is.null(col)) col else "blue", + col_points = if (!is.null(col)) col else "red", + col = NULL, Xlab = NULL, ylab = NULL, + engine = "parallelPlot", ...) { - if (!requireNamespace("parallelPlot", quietly = TRUE)) - stop("Package 'parallelPlot' is required. Install it with: install.packages('parallelPlot')") - D <- ncol(X) if (is.null(Xlab)) Xlab <- if (!is.null(colnames(X))) colnames(X) else paste0("X", seq_len(D)) if (is.null(ylab)) ylab <- if (is.matrix(y) && !is.null(colnames(y))) colnames(y)[1] else "y" @@ -81,42 +203,39 @@ parview.matrix <- function(X, y, inputColumns <- as.list(c(rep(TRUE, D), FALSE)) names(inputColumns) <- c(Xlab, ylab) - parallelPlot::parallelPlot( - data = df, - inputColumns = inputColumns, - refColumnDim = ylab, - rotateTitle = TRUE, - ... - ) + # All rows are observations — color all by y (n_pred = nrow) + parview_render(df = df, n_pred = nrow(df), refColumnDim = ylab, + col_fun = col_fun, col_points = col_points, inputColumns = inputColumns, + engine = engine, ...) } #### parview — libKriging shared helper #### parview_libKriging <- function(libKriging_model, - n_points = 500, + n_points = 500, + col_fun = "blue", + col_points = "red", conf_level = 0.95, Xlab = NULL, ylab = NULL, Xlim = NULL, + engine = "parallelPlot", ...) { - if (!requireNamespace("parallelPlot", quietly = TRUE)) - stop("Package 'parallelPlot' is required. Install it with: install.packages('parallelPlot')") - X_doe <- libKriging_model$X() y_doe <- as.numeric(libKriging_model$y()) - D <- ncol(X_doe) + D <- ncol(X_doe) if (is.null(Xlab)) Xlab <- if (!is.null(colnames(X_doe))) colnames(X_doe) else paste0("X", seq_len(D)) if (is.null(ylab)) ylab <- "y" if (is.null(Xlim)) Xlim <- apply(X_doe, 2, range) Xlim <- matrix(Xlim, nrow = 2, ncol = D) - lhs <- DiceDesign::lhsDesign(n_points, D)$design + lhs <- DiceDesign::lhsDesign(n_points, D)$design X_pred <- sweep(sweep(lhs, 2, Xlim[2, ] - Xlim[1, ], "*"), 2, Xlim[1, ], "+") colnames(X_pred) <- Xlab - p <- rlibkriging::predict(libKriging_model, X_pred, return_stdev = TRUE) - z <- qnorm(1 - (1 - conf_level) / 2) + p <- rlibkriging::predict(libKriging_model, X_pred, return_stdev = TRUE) + z <- qnorm(1 - (1 - conf_level) / 2) ylo <- paste0(ylab, "_low") yhi <- paste0(ylab, "_up") @@ -128,22 +247,18 @@ parview_libKriging <- function(libKriging_model, df_doe <- as.data.frame(X_doe) colnames(df_doe) <- Xlab df_doe[[ylab]] <- y_doe - df_doe[[ylo]] <- NA_real_ - df_doe[[yhi]] <- NA_real_ + df_doe[[ylo]] <- y_doe # no CI at conditioning points + df_doe[[yhi]] <- y_doe df <- rbind(df_pred, df_doe) - out_cols <- c(ylab, ylo, yhi) + out_cols <- c(ylab, ylo, yhi) inputColumns <- as.list(c(rep(TRUE, D), rep(FALSE, 3))) names(inputColumns) <- c(Xlab, out_cols) - parallelPlot::parallelPlot( - data = df, - inputColumns = inputColumns, - refColumnDim = ylab, - rotateTitle = TRUE, - ... - ) + parview_render(df = df, n_pred = n_points, refColumnDim = ylab, + col_fun = col_fun, col_points = col_points, inputColumns = inputColumns, + engine = engine, ...) } @@ -151,11 +266,13 @@ parview_libKriging <- function(libKriging_model, #' @param Kriging_model an object of class \code{"Kriging"}. #' @param n_points number of LHS prediction points. +#' @param col_points color of observed design points. #' @param conf_level confidence level for uncertainty bands shown as extra axes. #' @param Xlab optional character vector of axis labels for inputs. #' @param ylab optional string label for the output axis. #' @param Xlim optional input bounds matrix (2 x D); defaults to design range. -#' @param ... extra arguments passed to \code{\link[parallelPlot]{parallelPlot}}. +#' @param engine rendering engine: \code{"parallelPlot"} or \code{"base"}. +#' @param ... extra arguments passed to the rendering engine. #' @rdname parview #' @method parview Kriging #' @export @@ -165,13 +282,20 @@ parview_libKriging <- function(libKriging_model, #' X <- matrix(runif(15 * 2), ncol = 2) #' y <- apply(X, 1, branin) #' model <- Kriging(X = X, y = as.matrix(y), kernel = "matern3_2") -#' parview(model) +#' parview(model, engine = "base") #' } -parview.Kriging <- function(Kriging_model, n_points = 500, conf_level = 0.95, - Xlab = NULL, ylab = NULL, Xlim = NULL, ...) { +parview.Kriging <- function(Kriging_model, n_points = 500, + col_fun = if (!is.null(col)) col else "blue", + col_points = if (!is.null(col)) col else "red", + col = NULL, + conf_level = 0.95, + Xlab = NULL, ylab = NULL, Xlim = NULL, + engine = "parallelPlot", ...) { parview_libKriging(Kriging_model, - n_points = n_points, conf_level = conf_level, - Xlab = Xlab, ylab = ylab, Xlim = Xlim, ...) + n_points = n_points, col_fun = col_fun, col_points = col_points, + conf_level = conf_level, + Xlab = Xlab, ylab = ylab, Xlim = Xlim, + engine = engine, ...) } @@ -179,11 +303,13 @@ parview.Kriging <- function(Kriging_model, n_points = 500, conf_level = 0.95, #' @param WarpKriging_model an object of class \code{"WarpKriging"}. #' @param n_points number of LHS prediction points. +#' @param col_points color of observed design points. #' @param conf_level confidence level for uncertainty bands shown as extra axes. #' @param Xlab optional character vector of axis labels for inputs. #' @param ylab optional string label for the output axis. #' @param Xlim optional input bounds matrix (2 x D); defaults to design range. -#' @param ... extra arguments passed to \code{\link[parallelPlot]{parallelPlot}}. +#' @param engine rendering engine: \code{"parallelPlot"} or \code{"base"}. +#' @param ... extra arguments passed to the rendering engine. #' @rdname parview #' @method parview WarpKriging #' @export @@ -193,13 +319,20 @@ parview.Kriging <- function(Kriging_model, n_points = 500, conf_level = 0.95, #' X <- matrix(runif(15 * 2), ncol = 2) #' y <- apply(X, 1, branin) + 5 * rnorm(15) #' model <- WarpKriging(y = y, X = X, warping = c("affine", "affine"), kernel = "matern3_2") -#' parview(model) +#' parview(model, engine = "base") #' } -parview.WarpKriging <- function(WarpKriging_model, n_points = 500, conf_level = 0.95, - Xlab = NULL, ylab = NULL, Xlim = NULL, ...) { +parview.WarpKriging <- function(WarpKriging_model, n_points = 500, + col_fun = if (!is.null(col)) col else "blue", + col_points = if (!is.null(col)) col else "red", + col = NULL, + conf_level = 0.95, + Xlab = NULL, ylab = NULL, Xlim = NULL, + engine = "parallelPlot", ...) { parview_libKriging(WarpKriging_model, - n_points = n_points, conf_level = conf_level, - Xlab = Xlab, ylab = ylab, Xlim = Xlim, ...) + n_points = n_points, col_fun = col_fun, col_points = col_points, + conf_level = conf_level, + Xlab = Xlab, ylab = ylab, Xlim = Xlim, + engine = engine, ...) } @@ -208,11 +341,13 @@ parview.WarpKriging <- function(WarpKriging_model, n_points = 500, conf_level = #' @param km_model an object of class \code{"km"}. #' @param type the kriging type to use for model prediction. #' @param n_points number of LHS prediction points. +#' @param col_points color of observed design points. #' @param conf_level confidence level for uncertainty bands shown as extra axes. #' @param Xlab optional character vector of axis labels for inputs. #' @param ylab optional string label for the output axis. #' @param Xlim optional input bounds matrix (2 x D); defaults to design range. -#' @param ... extra arguments passed to \code{\link[parallelPlot]{parallelPlot}}. +#' @param engine rendering engine: \code{"parallelPlot"} or \code{"base"}. +#' @param ... extra arguments passed to the rendering engine. #' @rdname parview #' @method parview km #' @export @@ -222,28 +357,30 @@ parview.WarpKriging <- function(WarpKriging_model, n_points = 500, conf_level = #' X <- matrix(runif(15 * 2), ncol = 2) #' y <- apply(X, 1, branin) #' model <- km(design = X, response = y, covtype = "matern3_2") -#' parview(model) +#' parview(model, engine = "base") #' } -parview.km <- function(km_model, type = "UK", n_points = 500, conf_level = 0.95, - Xlab = NULL, ylab = NULL, Xlim = NULL, ...) { - if (!requireNamespace("parallelPlot", quietly = TRUE)) - stop("Package 'parallelPlot' is required. Install it with: install.packages('parallelPlot')") - +parview.km <- function(km_model, type = "UK", n_points = 500, + col_fun = if (!is.null(col)) col else "blue", + col_points = if (!is.null(col)) col else "red", + col = NULL, + conf_level = 0.95, + Xlab = NULL, ylab = NULL, Xlim = NULL, + engine = "parallelPlot", ...) { X_doe <- km_model@X y_doe <- as.numeric(km_model@y) - D <- ncol(X_doe) + D <- ncol(X_doe) if (is.null(Xlab)) Xlab <- if (!is.null(colnames(X_doe))) colnames(X_doe) else paste0("X", seq_len(D)) if (is.null(ylab)) ylab <- if (!is.null(colnames(km_model@y))) colnames(km_model@y)[1] else "y" if (is.null(Xlim)) Xlim <- apply(X_doe, 2, range) Xlim <- matrix(Xlim, nrow = 2, ncol = D) - lhs <- DiceDesign::lhsDesign(n_points, D)$design + lhs <- DiceDesign::lhsDesign(n_points, D)$design X_pred <- sweep(sweep(lhs, 2, Xlim[2, ] - Xlim[1, ], "*"), 2, Xlim[1, ], "+") colnames(X_pred) <- Xlab - p <- DiceKriging::predict.km(km_model, type = type, newdata = X_pred, checkNames = FALSE) - z <- qnorm(1 - (1 - conf_level) / 2) + p <- DiceKriging::predict.km(km_model, type = type, newdata = X_pred, checkNames = FALSE) + z <- qnorm(1 - (1 - conf_level) / 2) ylo <- paste0(ylab, "_low") yhi <- paste0(ylab, "_up") @@ -255,22 +392,18 @@ parview.km <- function(km_model, type = "UK", n_points = 500, conf_level = 0.95, df_doe <- as.data.frame(X_doe) colnames(df_doe) <- Xlab df_doe[[ylab]] <- y_doe - df_doe[[ylo]] <- NA_real_ - df_doe[[yhi]] <- NA_real_ + df_doe[[ylo]] <- y_doe + df_doe[[yhi]] <- y_doe df <- rbind(df_pred, df_doe) - out_cols <- c(ylab, ylo, yhi) + out_cols <- c(ylab, ylo, yhi) inputColumns <- as.list(c(rep(TRUE, D), rep(FALSE, 3))) names(inputColumns) <- c(Xlab, out_cols) - parallelPlot::parallelPlot( - data = df, - inputColumns = inputColumns, - refColumnDim = ylab, - rotateTitle = TRUE, - ... - ) + parview_render(df = df, n_pred = n_points, refColumnDim = ylab, + col_fun = col_fun, col_points = col_points, inputColumns = inputColumns, + engine = engine, ...) } @@ -278,11 +411,13 @@ parview.km <- function(km_model, type = "UK", n_points = 500, conf_level = 0.95, #' @param glm_model an object of class \code{"glm"}. #' @param n_points number of LHS prediction points. +#' @param col_points color of observed design points. #' @param conf_level confidence level for uncertainty bands shown as extra axes. #' @param Xlab optional character vector of axis labels for inputs. #' @param ylab optional string label for the output axis. #' @param Xlim optional input bounds matrix (2 x D); defaults to design range. -#' @param ... extra arguments passed to \code{\link[parallelPlot]{parallelPlot}}. +#' @param engine rendering engine: \code{"parallelPlot"} or \code{"base"}. +#' @param ... extra arguments passed to the rendering engine. #' @rdname parview #' @method parview glm #' @export @@ -291,12 +426,14 @@ parview.km <- function(km_model, type = "UK", n_points = 500, conf_level = 0.95, #' x1 <- rnorm(15); x2 <- rnorm(15) #' y <- x1 + x2^2 + rnorm(15) #' model <- glm(y ~ x1 + I(x2^2)) -#' parview(model) -parview.glm <- function(glm_model, n_points = 500, conf_level = 0.95, - Xlab = NULL, ylab = NULL, Xlim = NULL, ...) { - if (!requireNamespace("parallelPlot", quietly = TRUE)) - stop("Package 'parallelPlot' is required. Install it with: install.packages('parallelPlot')") - +#' parview(model, engine = "base") +parview.glm <- function(glm_model, n_points = 500, + col_fun = if (!is.null(col)) col else "blue", + col_points = if (!is.null(col)) col else "red", + col = NULL, + conf_level = 0.95, + Xlab = NULL, ylab = NULL, Xlim = NULL, + engine = "parallelPlot", ...) { Xlab_model <- all.vars(glm_model$formula)[-1] ylab_model <- all.vars(glm_model$formula)[1] if (is.null(Xlab)) Xlab <- Xlab_model @@ -310,13 +447,13 @@ parview.glm <- function(glm_model, n_points = 500, conf_level = 0.95, if (is.null(Xlim)) Xlim <- apply(X_doe, 2, range) Xlim <- matrix(Xlim, nrow = 2, ncol = D) - lhs <- DiceDesign::lhsDesign(n_points, D)$design - X_pred <- sweep(sweep(lhs, 2, Xlim[2, ] - Xlim[1, ], "*"), 2, Xlim[1, ], "+") + lhs <- DiceDesign::lhsDesign(n_points, D)$design + X_pred <- sweep(sweep(lhs, 2, Xlim[2, ] - Xlim[1, ], "*"), 2, Xlim[1, ], "+") X_pred_df <- as.data.frame(X_pred) colnames(X_pred_df) <- Xlab_model - p <- predict.glm(glm_model, newdata = X_pred_df, se.fit = TRUE) - z <- qnorm(1 - (1 - conf_level) / 2) + p <- predict.glm(glm_model, newdata = X_pred_df, se.fit = TRUE) + z <- qnorm(1 - (1 - conf_level) / 2) ylo <- paste0(ylab, "_low") yhi <- paste0(ylab, "_up") @@ -328,22 +465,18 @@ parview.glm <- function(glm_model, n_points = 500, conf_level = 0.95, df_doe <- as.data.frame(X_doe) df_doe[[ylab]] <- y_doe - df_doe[[ylo]] <- NA_real_ - df_doe[[yhi]] <- NA_real_ + df_doe[[ylo]] <- y_doe + df_doe[[yhi]] <- y_doe df <- rbind(df_pred, df_doe) - out_cols <- c(ylab, ylo, yhi) + out_cols <- c(ylab, ylo, yhi) inputColumns <- as.list(c(rep(TRUE, D), rep(FALSE, 3))) names(inputColumns) <- c(Xlab, out_cols) - parallelPlot::parallelPlot( - data = df, - inputColumns = inputColumns, - refColumnDim = ylab, - rotateTitle = TRUE, - ... - ) + parview_render(df = df, n_pred = n_points, refColumnDim = ylab, + col_fun = col_fun, col_points = col_points, inputColumns = inputColumns, + engine = engine, ...) } @@ -351,10 +484,12 @@ parview.glm <- function(glm_model, n_points = 500, conf_level = 0.95, #' @param modelFit_model an object returned by \code{DiceEval::modelFit}. #' @param n_points number of LHS prediction points. +#' @param col_points color of observed design points. #' @param Xlab optional character vector of axis labels for inputs. #' @param ylab optional string label for the output axis. #' @param Xlim optional input bounds matrix (2 x D); defaults to design range. -#' @param ... extra arguments passed to \code{\link[parallelPlot]{parallelPlot}}. +#' @param engine rendering engine: \code{"parallelPlot"} or \code{"base"}. +#' @param ... extra arguments passed to the rendering engine. #' @rdname parview #' @method parview list #' @export @@ -364,23 +499,24 @@ parview.glm <- function(glm_model, n_points = 500, conf_level = 0.95, #' X <- matrix(runif(15 * 2), ncol = 2) #' y <- apply(X, 1, branin) #' model <- modelFit(X, y, type = "StepLinear") -#' parview(model) +#' parview(model, engine = "base") #' } parview.list <- function(modelFit_model, n_points = 500, - Xlab = NULL, ylab = NULL, Xlim = NULL, ...) { - if (!requireNamespace("parallelPlot", quietly = TRUE)) - stop("Package 'parallelPlot' is required. Install it with: install.packages('parallelPlot')") - + col_fun = if (!is.null(col)) col else "blue", + col_points = if (!is.null(col)) col else "red", + col = NULL, + Xlab = NULL, ylab = NULL, Xlim = NULL, + engine = "parallelPlot", ...) { X_doe <- modelFit_model$data$X y_doe <- modelFit_model$data$Y - D <- ncol(X_doe) + D <- ncol(X_doe) if (is.null(Xlab)) Xlab <- if (!is.null(colnames(X_doe))) colnames(X_doe) else paste0("X", seq_len(D)) if (is.null(ylab)) ylab <- if (!is.null(colnames(y_doe))) colnames(y_doe)[1] else "y" if (is.null(Xlim)) Xlim <- apply(X_doe, 2, range) Xlim <- matrix(Xlim, nrow = 2, ncol = D) - lhs <- DiceDesign::lhsDesign(n_points, D)$design + lhs <- DiceDesign::lhsDesign(n_points, D)$design X_pred <- sweep(sweep(lhs, 2, Xlim[2, ] - Xlim[1, ], "*"), 2, Xlim[1, ], "+") X_pred_df <- as.data.frame(X_pred) @@ -400,13 +536,9 @@ parview.list <- function(modelFit_model, n_points = 500, inputColumns <- as.list(c(rep(TRUE, D), FALSE)) names(inputColumns) <- c(Xlab, ylab) - parallelPlot::parallelPlot( - data = df, - inputColumns = inputColumns, - refColumnDim = ylab, - rotateTitle = TRUE, - ... - ) + parview_render(df = df, n_pred = n_points, refColumnDim = ylab, + col_fun = col_fun, col_points = col_points, inputColumns = inputColumns, + engine = engine, ...) } @@ -417,27 +549,35 @@ if (!isGeneric("parview")) { setGeneric("parview", def = function(...) standardGeneric("parview")) } -#' @title Interactive parallel coordinates view of a prediction model or function. -#' @description Renders an interactive parallel coordinates chart (via the -#' \pkg{parallelPlot} package) showing all input dimensions and the output -#' simultaneously. Lines are colored by the output value, making it easy to -#' identify which input regions drive high or low responses. +#' @title Parallel coordinates view of a prediction model or function. +#' @description Renders a parallel coordinates chart showing all input dimensions +#' and the output simultaneously. Lines are colored by the output value (viridis +#' palette), making it easy to identify which input regions drive high or low +#' responses. #' #' For model-based methods (\code{km}, \code{Kriging}, \code{WarpKriging}, #' \code{glm}, \code{list}) a space-filling LHS prediction grid is displayed -#' together with the observed design points. Kriging-based methods also add -#' \code{y_low} / \code{y_up} axes for the predictive confidence interval. +#' together with the observed design points (in \code{col_points} color). +#' Kriging-based methods also add \code{y_low} / \code{y_up} axes for the +#' predictive confidence interval. +#' +#' Two rendering engines are available via the \code{engine} argument: +#' \describe{ +#' \item{\code{"parallelPlot"}}{Interactive htmlwidget (default), requires the +#' \pkg{parallelPlot} package.} +#' \item{\code{"base"}}{Static base-R plot, no extra dependency.} +#' } #' #' @param ... arguments of the relevant \code{parview.*} method. #' @export #' @examples -#' ## Simple function -#' parview(branin, Xlim = rbind(c(0, 0), c(1, 1))) +#' ## Static base-R plot +#' parview(branin, Xlim = rbind(c(0, 0), c(1, 1)), engine = "base") #' #' ## Design points only #' X <- matrix(runif(30 * 2), ncol = 2) #' y <- apply(X, 1, branin) -#' parview(X, y) +#' parview(X, y, engine = "base") parview <- function(...) { UseMethod("parview") } diff --git a/man/parview.Rd b/man/parview.Rd index 77ef102..be4e88b 100644 --- a/man/parview.Rd +++ b/man/parview.Rd @@ -9,37 +9,58 @@ \alias{parview.glm} \alias{parview.list} \alias{parview} -\title{Interactive parallel coordinates view of a prediction model or function.} +\title{Parallel coordinates view of a prediction model or function.} \usage{ \method{parview}{`function`}( fun, vectorized = FALSE, n_points = 500, Xlim = c(0, 1), + col_fun = if (!is.null(col)) col else "blue", + col = NULL, Xlab = NULL, ylab = "y", + engine = "parallelPlot", ... ) -\method{parview}{matrix}(X, y, Xlab = NULL, ylab = NULL, ...) +\method{parview}{matrix}( + X, + y, + col_fun = if (!is.null(col)) col else "blue", + col_points = if (!is.null(col)) col else "red", + col = NULL, + Xlab = NULL, + ylab = NULL, + engine = "parallelPlot", + ... +) \method{parview}{Kriging}( Kriging_model, n_points = 500, + col_fun = if (!is.null(col)) col else "blue", + col_points = if (!is.null(col)) col else "red", + col = NULL, conf_level = 0.95, Xlab = NULL, ylab = NULL, Xlim = NULL, + engine = "parallelPlot", ... ) \method{parview}{WarpKriging}( WarpKriging_model, n_points = 500, + col_fun = if (!is.null(col)) col else "blue", + col_points = if (!is.null(col)) col else "red", + col = NULL, conf_level = 0.95, Xlab = NULL, ylab = NULL, Xlim = NULL, + engine = "parallelPlot", ... ) @@ -47,29 +68,41 @@ km_model, type = "UK", n_points = 500, + col_fun = if (!is.null(col)) col else "blue", + col_points = if (!is.null(col)) col else "red", + col = NULL, conf_level = 0.95, Xlab = NULL, ylab = NULL, Xlim = NULL, + engine = "parallelPlot", ... ) \method{parview}{glm}( glm_model, n_points = 500, + col_fun = if (!is.null(col)) col else "blue", + col_points = if (!is.null(col)) col else "red", + col = NULL, conf_level = 0.95, Xlab = NULL, ylab = NULL, Xlim = NULL, + engine = "parallelPlot", ... ) \method{parview}{list}( modelFit_model, n_points = 500, + col_fun = if (!is.null(col)) col else "blue", + col_points = if (!is.null(col)) col else "red", + col = NULL, Xlab = NULL, ylab = NULL, Xlim = NULL, + engine = "parallelPlot", ... ) @@ -89,12 +122,16 @@ or a list(mean=...,se=...).} \item{ylab}{optional string label for the output axis.} +\item{engine}{rendering engine: \code{"parallelPlot"} or \code{"base"}.} + \item{...}{arguments of the relevant \code{parview.*} method.} \item{X}{the matrix of input design.} \item{y}{the array of output values.} +\item{col_points}{color of observed design points.} + \item{Kriging_model}{an object of class \code{"Kriging"}.} \item{conf_level}{confidence level for uncertainty bands shown as extra axes.} @@ -110,56 +147,64 @@ or a list(mean=...,se=...).} \item{modelFit_model}{an object returned by \code{DiceEval::modelFit}.} } \description{ -Renders an interactive parallel coordinates chart (via the - \pkg{parallelPlot} package) showing all input dimensions and the output - simultaneously. Lines are colored by the output value, making it easy to - identify which input regions drive high or low responses. +Renders a parallel coordinates chart showing all input dimensions + and the output simultaneously. Lines are colored by the output value (viridis + palette), making it easy to identify which input regions drive high or low + responses. For model-based methods (\code{km}, \code{Kriging}, \code{WarpKriging}, \code{glm}, \code{list}) a space-filling LHS prediction grid is displayed - together with the observed design points. Kriging-based methods also add - \code{y_low} / \code{y_up} axes for the predictive confidence interval. + together with the observed design points (in \code{col_points} color). + Kriging-based methods also add \code{y_low} / \code{y_up} axes for the + predictive confidence interval. + + Two rendering engines are available via the \code{engine} argument: + \describe{ + \item{\code{"parallelPlot"}}{Interactive htmlwidget (default), requires the + \pkg{parallelPlot} package.} + \item{\code{"base"}}{Static base-R plot, no extra dependency.} + } } \examples{ -parview(branin, Xlim = rbind(c(0, 0), c(1, 1))) +parview(branin, Xlim = rbind(c(0, 0), c(1, 1)), engine = "base") X <- matrix(runif(15 * 2), ncol = 2) y <- apply(X, 1, branin) -parview(X, y) +parview(X, y, engine = "base") if (requireNamespace("rlibkriging")) { library(rlibkriging) X <- matrix(runif(15 * 2), ncol = 2) y <- apply(X, 1, branin) model <- Kriging(X = X, y = as.matrix(y), kernel = "matern3_2") - parview(model) + parview(model, engine = "base") } if (requireNamespace("rlibkriging")) { library(rlibkriging) X <- matrix(runif(15 * 2), ncol = 2) y <- apply(X, 1, branin) + 5 * rnorm(15) model <- WarpKriging(y = y, X = X, warping = c("affine", "affine"), kernel = "matern3_2") - parview(model) + parview(model, engine = "base") } if (requireNamespace("DiceKriging")) { library(DiceKriging) X <- matrix(runif(15 * 2), ncol = 2) y <- apply(X, 1, branin) model <- km(design = X, response = y, covtype = "matern3_2") - parview(model) + parview(model, engine = "base") } x1 <- rnorm(15); x2 <- rnorm(15) y <- x1 + x2^2 + rnorm(15) model <- glm(y ~ x1 + I(x2^2)) -parview(model) +parview(model, engine = "base") if (requireNamespace("DiceEval")) { library(DiceEval) X <- matrix(runif(15 * 2), ncol = 2) y <- apply(X, 1, branin) model <- modelFit(X, y, type = "StepLinear") - parview(model) + parview(model, engine = "base") } -## Simple function -parview(branin, Xlim = rbind(c(0, 0), c(1, 1))) +## Static base-R plot +parview(branin, Xlim = rbind(c(0, 0), c(1, 1)), engine = "base") ## Design points only X <- matrix(runif(30 * 2), ncol = 2) y <- apply(X, 1, branin) -parview(X, y) +parview(X, y, engine = "base") } \seealso{ \code{\link{sectionview.function}} for 1D section plots. From 54502af81aa0e607db9d91bd486e27766cdd55fd Mon Sep 17 00:00:00 2001 From: RICHET-YAN Date: Wed, 17 Jun 2026 15:19:56 +0200 Subject: [PATCH 3/5] fix NAMESPACE: remove safe_mclapply export and detectCores import MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These were picked up by roxygen2 from uncommitted R/Utils.R changes and don't belong in this branch — safe_mclapply is not defined in the committed code, causing package install to fail in CI. Co-Authored-By: Claude Sonnet 4.6 --- NAMESPACE | 2 -- 1 file changed, 2 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index a1f376b..231cbbb 100755 --- a/NAMESPACE +++ b/NAMESPACE @@ -64,7 +64,6 @@ export(points_out.mesh) export(root) export(roots) export(roots_mesh) -export(safe_mclapply) export(sectionview) export(sectionview3d) import(grDevices) @@ -79,7 +78,6 @@ importFrom(foreach,foreach) importFrom(geometry,convhulln) importFrom(geometry,delaunayn) importFrom(geometry,inhulln) -importFrom(parallel,detectCores) importFrom(parallel,mclapply) importFrom(utils,combn) importFrom(utils,installed.packages) From da29c3da6ea2bb7c74517ee69b738110705f6be5 Mon Sep 17 00:00:00 2001 From: RICHET-YAN Date: Wed, 17 Jun 2026 15:35:02 +0200 Subject: [PATCH 4/5] fix CI: document col_fun/col params, remove orphan safe_mclapply.Rd - Add @param col_fun and @param col to parview.function roxygen block so parview.Rd no longer has undocumented-arguments warnings - Delete man/safe_mclapply.Rd (generated from uncommitted R/Utils.R, referenced a function not present in committed code) - Restore man/Apply.function.Rd, man/Vectorize.function.Rd, man/roots.Rd to their master state (roxygenise had replaced parallel::mclapply references with safe_mclapply, causing codoc mismatches) Co-Authored-By: Claude Sonnet 4.6 --- R/parview.R | 4 ++++ man/Apply.function.Rd | 9 ++++++++- man/Vectorize.function.Rd | 8 +++++++- man/parview.Rd | 6 ++++++ man/roots.Rd | 2 +- man/safe_mclapply.Rd | 37 ------------------------------------- 6 files changed, 26 insertions(+), 40 deletions(-) delete mode 100644 man/safe_mclapply.Rd diff --git a/R/parview.R b/R/parview.R index 927185f..3a08086 100644 --- a/R/parview.R +++ b/R/parview.R @@ -125,6 +125,10 @@ parview_render <- function(df, n_pred, refColumnDim, col_fun, col_points, #' or a list(mean=...,se=...). #' @param vectorized is fun vectorized? #' @param n_points number of space-filling (LHS) points to sample for evaluation. +#' @param col_fun base color for the prediction color scale (HSV saturation ramp from +#' white to this color, matching the \code{contourview} policy). +#' @param col shorthand alias for both \code{col_fun} and \code{col_points}; overridden +#' by the individual arguments if both are supplied. #' @param Xlim a matrix (2 x D) or vector \code{c(lo, hi)} giving input ranges. #' @param Xlab optional character vector of axis labels for inputs. #' @param ylab optional string label for the output axis. diff --git a/man/Apply.function.Rd b/man/Apply.function.Rd index 12ce2fd..a846e6e 100644 --- a/man/Apply.function.Rd +++ b/man/Apply.function.Rd @@ -4,7 +4,14 @@ \alias{Apply.function} \title{Apply Functions Over Array Margins, using custom vectorization (possibly using parallel)} \usage{ -Apply.function(FUN, X, MARGIN = 1, .combine = c, .lapply = safe_mclapply, ...) +Apply.function( + FUN, + X, + MARGIN = 1, + .combine = c, + .lapply = parallel::mclapply, + ... +) } \arguments{ \item{FUN}{function to apply on X} diff --git a/man/Vectorize.function.Rd b/man/Vectorize.function.Rd index fded52f..460b9d4 100644 --- a/man/Vectorize.function.Rd +++ b/man/Vectorize.function.Rd @@ -4,7 +4,13 @@ \alias{Vectorize.function} \title{Vectorize a multidimensional Function} \usage{ -Vectorize.function(fun, dim, .combine = rbind, .lapply = safe_mclapply, ...) +Vectorize.function( + fun, + dim, + .combine = rbind, + .lapply = parallel::mclapply, + ... +) } \arguments{ \item{fun}{'dim'-dimensional function to Vectorize} diff --git a/man/parview.Rd b/man/parview.Rd index be4e88b..2e3cff6 100644 --- a/man/parview.Rd +++ b/man/parview.Rd @@ -118,6 +118,12 @@ or a list(mean=...,se=...).} \item{Xlim}{optional input bounds matrix (2 x D); defaults to design range.} +\item{col_fun}{base color for the prediction color scale (HSV saturation ramp from +white to this color, matching the \code{contourview} policy).} + +\item{col}{shorthand alias for both \code{col_fun} and \code{col_points}; overridden +by the individual arguments if both are supplied.} + \item{Xlab}{optional character vector of axis labels for inputs.} \item{ylab}{optional string label for the output axis.} diff --git a/man/roots.Rd b/man/roots.Rd index dcd9eb9..707ff9f 100644 --- a/man/roots.Rd +++ b/man/roots.Rd @@ -12,7 +12,7 @@ roots( split = "seq", split.size = 11, tol = .Machine$double.eps^0.25, - .lapply = safe_mclapply, + .lapply = parallel::mclapply, ... ) } diff --git a/man/safe_mclapply.Rd b/man/safe_mclapply.Rd deleted file mode 100644 index e743bbd..0000000 --- a/man/safe_mclapply.Rd +++ /dev/null @@ -1,37 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/Utils.R -\name{safe_mclapply} -\alias{safe_mclapply} -\title{Fork-safe parallel lapply} -\usage{ -safe_mclapply( - X, - FUN, - ..., - mc.cores = getOption("mc.cores", parallel::detectCores()) -) -} -\arguments{ -\item{X}{a vector (atomic or list)} - -\item{FUN}{the function to be applied to each element of X} - -\item{...}{optional arguments to FUN} - -\item{mc.cores}{number of cores (default: \code{getOption("mc.cores", parallel::detectCores())})} -} -\value{ -list of results, like \code{lapply} -} -\description{ -Drop-in replacement for \code{parallel::mclapply} that prevents deadlocks from -nested fork calls. Uses an R option (\code{.DiceView_parallel_depth}) inherited by forked -children to detect nesting and fall back to \code{lapply} in that case. -This avoids the classic pipe deadlock where a forked worker tries to fork grandchildren -while the parent is still waiting on pipes. -} -\examples{ -safe_mclapply(1:4, function(i) i^2) -# nested call falls back to lapply automatically: -outer <- safe_mclapply(1:2, function(i) safe_mclapply(1:3, function(j) i*j)) -} From 41ea27a4b7d5be72e447e27aa2d8c9730d1c3568 Mon Sep 17 00:00:00 2001 From: RICHET-YAN Date: Wed, 17 Jun 2026 15:39:54 +0200 Subject: [PATCH 5/5] exclude .claude memory dir from R package build Co-Authored-By: Claude Sonnet 4.6 --- .Rbuildignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.Rbuildignore b/.Rbuildignore index 07d58b7..98f5fb9 100644 --- a/.Rbuildignore +++ b/.Rbuildignore @@ -4,3 +4,4 @@ ^.github$ ^man-roxygen$ ^doc$ +^\.claude$