From f27a2057590dd806eee3a68825139f2ca777f2d4 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 17 Aug 2026 18:24:05 +0000 Subject: [PATCH] refactor: Rename normalization flags to `normalized` (#2788) `power_centrality(rescale = )`, `hits_scores(scale = )` and `tk_coords(norm = )` unify on `normalized`, matching the nine functions that already use that name. Legacy spellings are soft-deprecated. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01RTPj4qNv2FWui6etixZeuR --- R/centrality.R | 49 ++++++++++++++++++++--------- R/tkplot.R | 20 +++++++----- man/bonpow.Rd | 4 +-- man/hits_scores.Rd | 13 ++++++-- man/power_centrality.Rd | 4 +-- man/tkplot.Rd | 4 +-- man/tkplot.getcoords.Rd | 3 +- tests/testthat/_snaps/centrality.md | 18 +++++++++++ tests/testthat/test-centrality.R | 34 +++++++++++++++++++- tests/testthat/test-tkplot.R | 12 +++++-- tools/migrations/centrality.R | 4 +-- tools/migrations/plotting.R | 4 +-- 12 files changed, 131 insertions(+), 38 deletions(-) diff --git a/R/centrality.R b/R/centrality.R index b95ea2ea3fd..1851503a30b 100644 --- a/R/centrality.R +++ b/R/centrality.R @@ -233,6 +233,8 @@ edge.betweenness <- function( #' `bonpow()` was renamed to [power_centrality()] to create a more #' consistent API. #' @inheritParams power_centrality +#' @param rescale `r lifecycle::badge("deprecated")` Use `normalized` in +#' [power_centrality()] instead. #' @keywords internal #' @export bonpow <- function( @@ -251,7 +253,7 @@ bonpow <- function( nodes = nodes, loops = loops, exponent = exponent, - rescale = rescale, + normalized = rescale, tol = tol, sparse = sparse ) @@ -1719,7 +1721,7 @@ diversity <- function( #' scores are the same as authority scores. #' #' @param graph The input graph. -#' @param scale Logical, whether to scale the result to have a maximum +#' @param normalized Logical, whether to scale the result to have a maximum #' score of one. If no scaling is used then the result vector has unit length #' in the Euclidean norm. #' @param weights Optional positive weight vector for calculating weighted @@ -1729,6 +1731,7 @@ diversity <- function( #' edges are effectively added up. #' @param options A named list, to override some ARPACK options. See #' [arpack()] for details. The default `NULL` uses [arpack_defaults()]. +#' @param scale `r lifecycle::badge("deprecated")` Use `normalized` instead. #' @inheritParams rlang::args_dots_empty #' @return A named list with members: #' \describe{ @@ -1766,19 +1769,35 @@ diversity <- function( hits_scores <- function( graph, ..., - scale = TRUE, + normalized = TRUE, weights = NULL, - options = NULL + options = NULL, + scale = deprecated() ) { rlang::check_dots_empty() + if (lifecycle::is_present(scale)) { + if (!missing(normalized)) { + cli::cli_abort(c( + "Argument {.arg normalized} of {.fn hits_scores} was supplied more than once.", + i = "It was also supplied via its legacy name {.arg scale}." + )) + } + lifecycle::deprecate_soft( + "3.0.0", + "hits_scores(scale = )", + "hits_scores(normalized = )" + ) + normalized <- scale + } + if (is.null(options)) { options <- arpack_defaults() } hub_and_authority_scores_impl( graph = graph, - scale = scale, + scale = normalized, weights = weights, options = options ) @@ -1810,7 +1829,7 @@ authority_score <- function( scores <- hits_scores( graph = graph, - scale = scale, + normalized = scale, weights = weights, options = options ) @@ -1855,7 +1874,7 @@ hub_score <- function( scores <- hits_scores( graph = graph, - scale = scale, + normalized = scale, weights = weights, options = options ) @@ -2266,7 +2285,7 @@ bonpow.sparse <- function( #' loops. `loops` is `FALSE` by default. #' @param exponent exponent (decay rate) for the Bonacich power centrality #' score; can be negative -#' @param rescale if true, centrality scores are rescaled such that they sum to +#' @param normalized if true, centrality scores are rescaled such that they sum to #' 1. #' @param tol tolerance for near-singularities during matrix inversion (see #' [solve()]) @@ -2325,7 +2344,7 @@ power_centrality <- function( ..., loops = FALSE, exponent = 1, - rescale = FALSE, + normalized = FALSE, tol = 1e-7, sparse = TRUE, weights = NULL @@ -2333,6 +2352,8 @@ power_centrality <- function( # BEGIN GENERATED ARG_HANDLE: power_centrality, do not edit, see tools/generate-migrations.R # fmt: skip if (...length() > 0L) { + .arg_forbidden <- base::intersect(base::names(base::sys.call()), base::c("n", "no")) + if (base::length(.arg_forbidden) > 0L) cli::cli_abort(base::c("Argument {.arg {(.arg_forbidden)}} matches multiple formal arguments of {.fn power_centrality}.", i = "Spell out the full argument name.")) # Pre-3.0.0 signature: power_centrality(graph, nodes, loops, exponent, rescale, tol, sparse, weights) .old_signature <- function(loops, exponent, rescale, tol, sparse, weights, ...) { if (...length() > 0L) { @@ -2344,7 +2365,7 @@ power_centrality <- function( base::c( if (!base::missing(loops)) base::list(loops = loops), if (!base::missing(exponent)) base::list(exponent = exponent), - if (!base::missing(rescale)) base::list(rescale = rescale), + if (!base::missing(rescale)) base::list(normalized = rescale), if (!base::missing(tol)) base::list(tol = tol), if (!base::missing(sparse)) base::list(sparse = sparse), if (!base::missing(weights)) base::list(weights = weights) @@ -2356,7 +2377,7 @@ power_centrality <- function( .arg_conflict <- base::intersect(.arg_names, base::c( if (!base::missing(loops)) "loops", if (!base::missing(exponent)) "exponent", - if (!base::missing(rescale)) "rescale", + if (!base::missing(normalized)) "normalized", if (!base::missing(tol)) "tol", if (!base::missing(sparse)) "sparse", if (!base::missing(weights)) "weights" @@ -2367,7 +2388,7 @@ power_centrality <- function( "3.0.0", what = base::I("Calling `power_centrality()` with positional or abbreviated arguments"), details = base::c( - i = base::paste0("Detected call: power_centrality(", base::paste(base::c("graph", "nodes", .arg_names), collapse = ", "), ")"), + i = base::paste0("Detected call: power_centrality(", base::paste(base::c("graph", "nodes", base::c(loops = "loops", exponent = "exponent", normalized = "rescale", tol = "tol", sparse = "sparse", weights = "weights")[.arg_names]), collapse = ", "), ")"), i = base::paste0("Use instead: power_centrality(", base::paste(base::c("graph", "nodes", base::paste0(.arg_names, " = ")), collapse = ", "), ")") ) ) @@ -2386,7 +2407,7 @@ power_centrality <- function( nodes, loops, exponent, - rescale, + normalized, tol, weights = weights ) @@ -2396,7 +2417,7 @@ power_centrality <- function( nodes, loops, exponent, - rescale, + normalized, tol, weights = weights ) diff --git a/R/tkplot.R b/R/tkplot.R index 9342a284939..11c81bb35e6 100644 --- a/R/tkplot.R +++ b/R/tkplot.R @@ -71,12 +71,14 @@ tkplot.off <- function() { #' `tkplot.getcoords()` was renamed to [tk_coords()] to create a more #' consistent API. #' @inheritParams tk_coords +#' @param norm `r lifecycle::badge("deprecated")` Use `normalized` in +#' [tk_coords()] instead. #' @keywords internal #' @export tkplot.getcoords <- function(tkp.id, norm = FALSE) { # nocov start lifecycle::deprecate_warn("2.0.0", "tkplot.getcoords()", "tk_coords()") - tk_coords(tkp.id = tkp.id, norm = norm) + tk_coords(tkp.id = tkp.id, normalized = norm) } # nocov end #' Interactive plotting of graphs @@ -271,7 +273,7 @@ assign(".next", 1, .tkplot.env) #' @param width The width of the rectangle for generating new coordinates. #' @param height The height of the rectangle for generating new coordinates. #' @param newlayout The new layout, see the `layout` parameter of tkplot. -#' @param norm Logical, should we norm the coordinates. +#' @param normalized Logical, should we norm the coordinates. #' @param coords Two-column numeric matrix, the new coordinates of the #' vertices, in absolute coordinates. #' @param degree The degree to rotate the plot. @@ -734,11 +736,13 @@ tk_postscript <- function(tkp.id) { tk_coords <- function( tkp.id, ..., - norm = FALSE + normalized = FALSE ) { # BEGIN GENERATED ARG_HANDLE: tk_coords, do not edit, see tools/generate-migrations.R # fmt: skip if (...length() > 0L) { + .arg_ambiguous <- base::intersect(base::names(base::substitute(...())), base::c("n", "no", "nor")) + if (base::length(.arg_ambiguous) > 0L) cli::cli_abort("Argument {.arg {(.arg_ambiguous[[1L]])}} matches multiple arguments of {.fn tk_coords}.") # Pre-3.0.0 signature: tk_coords(tkp.id, norm) .old_signature <- function(norm, ...) { if (...length() > 0L) { @@ -748,14 +752,14 @@ tk_coords <- function( cli::cli_abort(base::c("Unexpected argument passed to {.fn tk_coords}: {.arg {(.arg_extra)}}.", i = "Arguments after {.arg ...} must be spelled out in full."), call = base::parent.frame()) } base::c( - if (!base::missing(norm)) base::list(norm = norm) + if (!base::missing(norm)) base::list(normalized = norm) ) } .arg_handle <- .old_signature(...) if (base::length(.arg_handle) > 0L) { .arg_names <- base::names(.arg_handle) .arg_conflict <- base::intersect(.arg_names, base::c( - if (!base::missing(norm)) "norm" + if (!base::missing(normalized)) "normalized" )) if (base::length(.arg_conflict) > 0L) cli::cli_abort(base::c("Argument {.arg {(.arg_conflict)}} of {.fn tk_coords} was supplied more than once.", i = "Pass it exactly once, by its new name {.arg {(.arg_conflict)}}.")) base::list2env(.arg_handle, base::environment()) @@ -763,7 +767,7 @@ tk_coords <- function( "3.0.0", what = base::I("Calling `tk_coords()` with positional or abbreviated arguments"), details = base::c( - i = base::paste0("Detected call: tk_coords(", base::paste(base::c("tkp.id", .arg_names), collapse = ", "), ")"), + i = base::paste0("Detected call: tk_coords(", base::paste(base::c("tkp.id", base::c(normalized = "norm")[.arg_names]), collapse = ", "), ")"), i = base::paste0("Use instead: tk_coords(", base::paste(base::c("tkp.id", base::paste0(.arg_names, " = ")), collapse = ", "), ")") ) ) @@ -774,7 +778,7 @@ tk_coords <- function( # nocov start coords <- .tkplot.get(tkp.id, "coords") coords[, 2] <- max(coords[, 2]) - coords[, 2] - if (norm) { + if (normalized) { # Shift coords[, 1] <- coords[, 1] - min(coords[, 1]) coords[, 2] <- coords[, 2] - min(coords[, 2]) @@ -1845,7 +1849,7 @@ tk_canvas <- function(tkp.id) { layout$params[[i]]$type == "initial" && params[[i]] ) { - realparams[[i]] <- tk_coords(tkp.id, norm = TRUE) + realparams[[i]] <- tk_coords(tkp.id, normalized = TRUE) } } if (as.logical(tcltk::tclvalue(save.default))) { diff --git a/man/bonpow.Rd b/man/bonpow.Rd index f3f76251b8f..338399252dc 100644 --- a/man/bonpow.Rd +++ b/man/bonpow.Rd @@ -27,8 +27,8 @@ loops. \code{loops} is \code{FALSE} by default.} \item{exponent}{exponent (decay rate) for the Bonacich power centrality score; can be negative} -\item{rescale}{if true, centrality scores are rescaled such that they sum to -1.} +\item{rescale}{\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} Use \code{normalized} in +\code{\link[=power_centrality]{power_centrality()}} instead.} \item{tol}{tolerance for near-singularities during matrix inversion (see \code{\link[=solve]{solve()}})} diff --git a/man/hits_scores.Rd b/man/hits_scores.Rd index ee1440b68c5..d9f1562f766 100644 --- a/man/hits_scores.Rd +++ b/man/hits_scores.Rd @@ -4,14 +4,21 @@ \alias{hits_scores} \title{Kleinberg's hub and authority centrality scores.} \usage{ -hits_scores(graph, ..., scale = TRUE, weights = NULL, options = NULL) +hits_scores( + graph, + ..., + normalized = TRUE, + weights = NULL, + options = NULL, + scale = deprecated() +) } \arguments{ \item{graph}{The input graph.} \item{...}{These dots are for future extensions and must be empty.} -\item{scale}{Logical, whether to scale the result to have a maximum +\item{normalized}{Logical, whether to scale the result to have a maximum score of one. If no scaling is used then the result vector has unit length in the Euclidean norm.} @@ -23,6 +30,8 @@ edges are effectively added up.} \item{options}{A named list, to override some ARPACK options. See \code{\link[=arpack]{arpack()}} for details. The default \code{NULL} uses \code{\link[=arpack_defaults]{arpack_defaults()}}.} + +\item{scale}{\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} Use \code{normalized} instead.} } \value{ A named list with members: diff --git a/man/power_centrality.Rd b/man/power_centrality.Rd index b96699c774f..49d80273525 100644 --- a/man/power_centrality.Rd +++ b/man/power_centrality.Rd @@ -10,7 +10,7 @@ power_centrality( ..., loops = FALSE, exponent = 1, - rescale = FALSE, + normalized = FALSE, tol = 1e-07, sparse = TRUE, weights = NULL @@ -31,7 +31,7 @@ loops. \code{loops} is \code{FALSE} by default.} \item{exponent}{exponent (decay rate) for the Bonacich power centrality score; can be negative} -\item{rescale}{if true, centrality scores are rescaled such that they sum to +\item{normalized}{if true, centrality scores are rescaled such that they sum to 1.} \item{tol}{tolerance for near-singularities during matrix inversion (see diff --git a/man/tkplot.Rd b/man/tkplot.Rd index f601f105f74..1013f77d1dd 100644 --- a/man/tkplot.Rd +++ b/man/tkplot.Rd @@ -28,7 +28,7 @@ tk_reshape(tkp.id, newlayout, ..., params) tk_postscript(tkp.id) -tk_coords(tkp.id, ..., norm = FALSE) +tk_coords(tkp.id, ..., normalized = FALSE) tk_set_coords(tkp.id, coords) @@ -56,7 +56,7 @@ the complete list.} \item{params}{Extra parameters in a list, to pass to the layout function.} -\item{norm}{Logical, should we norm the coordinates.} +\item{normalized}{Logical, should we norm the coordinates.} \item{coords}{Two-column numeric matrix, the new coordinates of the vertices, in absolute coordinates.} diff --git a/man/tkplot.getcoords.Rd b/man/tkplot.getcoords.Rd index e764d774690..0b21a844598 100644 --- a/man/tkplot.getcoords.Rd +++ b/man/tkplot.getcoords.Rd @@ -9,7 +9,8 @@ tkplot.getcoords(tkp.id, norm = FALSE) \arguments{ \item{tkp.id}{The ID of the tkplot window to close/reshape/etc.} -\item{norm}{Logical, should we norm the coordinates.} +\item{norm}{\ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} Use \code{normalized} in +\code{\link[=tk_coords]{tk_coords()}} instead.} } \description{ \ifelse{html}{\href{https://lifecycle.r-lib.org/articles/stages.html#deprecated}{\figure{lifecycle-deprecated.svg}{options: alt='[Deprecated]'}}}{\strong{[Deprecated]}} diff --git a/tests/testthat/_snaps/centrality.md b/tests/testthat/_snaps/centrality.md index fd6a1913e6f..ab3d6b88bdc 100644 --- a/tests/testthat/_snaps/centrality.md +++ b/tests/testthat/_snaps/centrality.md @@ -24,6 +24,24 @@ i Please use `arpack_defaults()` instead. i So the function arpack_defaults(), not an object called arpack_defaults. +# hits_scores(scale = ) is deprecated but still works + + Code + res_legacy <- hits_scores(g, scale = FALSE) + Condition + Warning: + The `scale` argument of `hits_scores()` is deprecated as of igraph 3.0.0. + i Please use the `normalized` argument instead. + +--- + + Code + hits_scores(g, normalized = FALSE, scale = FALSE) + Condition + Error in `hits_scores()`: + ! Argument `normalized` of `hits_scores()` was supplied more than once. + i It was also supplied via its legacy name `scale`. + # eigen_centrality() deprecated scale argument Code diff --git a/tests/testthat/test-centrality.R b/tests/testthat/test-centrality.R index 240ac2ab849..94b24ca791c 100644 --- a/tests/testthat/test-centrality.R +++ b/tests/testthat/test-centrality.R @@ -173,6 +173,25 @@ test_that("authority_score survives stress test", { } }) +test_that("hits_scores(scale = ) is deprecated but still works", { + rlang::local_options(lifecycle_verbosity = "warning") + g <- make_star(10) + + expect_snapshot( + res_legacy <- hits_scores(g, scale = FALSE) + ) + # ARPACK starts from a random vector, so compare with a tolerance. + expect_equal( + res_legacy[c("hub", "authority")], + hits_scores(g, normalized = FALSE)[c("hub", "authority")] + ) + + expect_snapshot( + error = TRUE, + hits_scores(g, normalized = FALSE, scale = FALSE) + ) +}) + test_that("`hits_score()` works -- authority", { igraph_local_seed(42) mscale <- function(x) { @@ -1199,7 +1218,7 @@ test_that("power_centrality() covers migrated tail args and positional recovery" nodes = V(ring)[1:3], loops = FALSE, exponent = 0.2, - rescale = TRUE, + normalized = TRUE, tol = 1e-10, sparse = FALSE, weights = rep(1, 5) @@ -1207,6 +1226,19 @@ test_that("power_centrality() covers migrated tail args and positional recovery" # With unit weights the ring is symmetric and rescaled scores sum to one. expect_equal(res, rep(1 / 5, 3)) + # The legacy `rescale` name is recovered as `normalized`. + lifecycle::expect_deprecated( + res_legacy <- power_centrality( + ring, + nodes = V(ring)[1:3], + exponent = 0.2, + rescale = TRUE, + sparse = FALSE, + weights = rep(1, 5) + ) + ) + expect_equal(res_legacy, res) + # `loops` toggles the adjacency diagonal of a path with a loop on vertex 2. looped <- make_graph(c(1, 2, 2, 3, 2, 2), directed = FALSE) expect_false(isTRUE(all.equal( diff --git a/tests/testthat/test-tkplot.R b/tests/testthat/test-tkplot.R index b86df647d9c..1050285f03e 100644 --- a/tests/testthat/test-tkplot.R +++ b/tests/testthat/test-tkplot.R @@ -32,8 +32,8 @@ test_that("tk_fit() recovers positional `width`/`height` with a deprecation", { ) }) -test_that("tk_coords() covers `norm` by name", { - expect_error(tk_coords(9999, norm = TRUE), "not found") +test_that("tk_coords() covers `normalized` by name", { + expect_error(tk_coords(9999, normalized = TRUE), "not found") }) test_that("tk_coords() recovers a positional `norm` with a deprecation", { @@ -44,6 +44,14 @@ test_that("tk_coords() recovers a positional `norm` with a deprecation", { ) }) +test_that("tk_coords() recovers the legacy `norm` name with a deprecation", { + rlang::local_options(lifecycle_verbosity = "warning") + + lifecycle::expect_deprecated( + expect_error(tk_coords(9999, norm = TRUE), "not found") + ) +}) + test_that("tk_rotate() covers `degree` and `rad` by name", { expect_error(tk_rotate(9999, degree = 90), "not found") expect_error(tk_rotate(9999, rad = pi / 2), "not found") diff --git a/tools/migrations/centrality.R b/tools/migrations/centrality.R index 09b62a0fcb4..44d94eece58 100644 --- a/tools/migrations/centrality.R +++ b/tools/migrations/centrality.R @@ -116,7 +116,7 @@ migrations <- list( nodes, loops, exponent, - rescale, + rescale = normalized, tol, sparse, weights @@ -127,7 +127,7 @@ migrations <- list( ..., loops = FALSE, exponent = 1, - rescale = FALSE, + normalized = FALSE, tol = 1e-7, sparse = TRUE, weights = NULL diff --git a/tools/migrations/plotting.R b/tools/migrations/plotting.R index 22036c7f6a6..756ee2689a2 100644 --- a/tools/migrations/plotting.R +++ b/tools/migrations/plotting.R @@ -46,11 +46,11 @@ migrations <- list( ), tk_coords = list( - old = function(tkp.id, norm) {}, + old = function(tkp.id, norm = normalized) {}, new = function( tkp.id, ..., - norm = FALSE + normalized = FALSE ) {}, when = "3.0.0" ),