diff --git a/NAMESPACE b/NAMESPACE index 00fe445..bdb8ddc 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -65,12 +65,14 @@ export(sort_network) export(st_compatibalize) importFrom(RANN,nn2) importFrom(data.table,as.data.table) +importFrom(data.table,copy) importFrom(data.table,data.table) importFrom(data.table,setnames) importFrom(dplyr,all_of) importFrom(dplyr,any_of) importFrom(dplyr,arrange) importFrom(dplyr,as_tibble) +importFrom(dplyr,between) importFrom(dplyr,bind_cols) importFrom(dplyr,bind_rows) importFrom(dplyr,case_when) @@ -92,6 +94,9 @@ importFrom(dplyr,select) importFrom(dplyr,summarise) importFrom(dplyr,tibble) importFrom(dplyr,ungroup) +importFrom(fastmap,fastqueue) +importFrom(fastmap,faststack) +importFrom(methods,as) importFrom(pbapply,pbapply) importFrom(pbapply,pblapply) importFrom(pbapply,pboptions) @@ -110,11 +115,22 @@ importFrom(sf,st_intersects) importFrom(sf,st_is_longlat) importFrom(sf,st_join) importFrom(sf,st_linestring) +importFrom(sf,st_point) importFrom(sf,st_reverse) importFrom(sf,st_segmentize) importFrom(sf,st_sf) +importFrom(sf,st_sfc) importFrom(sf,st_transform) importFrom(sf,st_zm) +importFrom(stats,na.omit) +importFrom(stats,setNames) importFrom(tidyr,pivot_wider) +importFrom(tidyr,replace_na) +importFrom(tidyr,unnest) +importFrom(units,as_units) +importFrom(units,set_units) +importFrom(utils,adist) importFrom(utils,combn) +importFrom(utils,setTxtProgressBar) importFrom(utils,tail) +importFrom(utils,txtProgressBar) diff --git a/R/00_hydroloom.R b/R/00_hydroloom.R index 210ad92..d3327fd 100644 --- a/R/00_hydroloom.R +++ b/R/00_hydroloom.R @@ -107,7 +107,7 @@ hnd$point_id <- "identifier of hydrologic location point" hnd$offset <- "offset distance from point to line in units of linear reference analysis units" hnd$levelpath_outlet_id <- "id of outlet catchment of a levelpath" -hydroloom_name_definitions <- stats::setNames(as.character(hnd), names(hnd)) +hydroloom_name_definitions <- setNames(as.character(hnd), names(hnd)) class(hydroloom_name_definitions) <- c("hydroloom_names", class(hydroloom_name_definitions)) #' @export @@ -193,14 +193,22 @@ get_outlet_value <- function(x) { } #' @importFrom dplyr filter select left_join right_join all_of any_of bind_rows group_by -#' @importFrom dplyr ungroup n rename row_number arrange desc distinct mutate summarise +#' @importFrom dplyr ungroup n rename row_number between arrange desc distinct mutate summarise #' @importFrom dplyr everything as_tibble pull group_split tibble bind_cols lag case_when +#' @importFrom data.table copy data.table as.data.table setnames +#' @importFrom tidyr unnest replace_na pivot_wider #' @importFrom rlang := #' @importFrom sf "st_geometry<-" st_drop_geometry st_geometry st_geometry_type st_intersects #' @importFrom sf st_cast st_linestring st_is_longlat st_transform st_segmentize st_buffer #' @importFrom sf st_as_sf st_sf st_zm st_coordinates st_crs st_join st_reverse +#' @importFrom sf st_point st_sfc #' @importFrom pbapply pblapply pbsapply pbapply pboptions #' @importFrom RANN nn2 +#' @importFrom stats setNames na.omit +#' @importFrom fastmap fastqueue faststack +#' @importFrom utils txtProgressBar setTxtProgressBar adist combn tail +#' @importFrom units set_units as_units +#' @importFrom methods as .data <- NULL @@ -266,7 +274,7 @@ hy <- function(x, clean = FALSE) { x <- as_tibble(x) } - attr(x, "orig_names") <- stats::setNames(names(x), keep_names) + attr(x, "orig_names") <- setNames(names(x), keep_names) class(x) <- c("hy", class(x)) diff --git a/R/add_divergence.R b/R/add_divergence.R index fa6dc20..3f576c5 100644 --- a/R/add_divergence.R +++ b/R/add_divergence.R @@ -123,7 +123,7 @@ add_divergence.hy <- function(x, coastal_outlet_ids, inland_outlet_ids, x <- make_fromids(make_index_ids(x), return_list = TRUE) - paths <- pbapply::pblapply(term, function(i, net) { + paths <- pblapply(term, function(i, net) { try(navigate_network_dfs(x = net, starts = i, direction = "up", reset = FALSE)) @@ -133,8 +133,8 @@ add_divergence.hy <- function(x, coastal_outlet_ids, inland_outlet_ids, paths = I(unlist(paths, recursive = FALSE))) |> mutate(coastal = id %in% coastal_outlet_ids) |> - tidyr::unnest(cols = c(paths)) |> - tidyr::unnest(cols = c(paths)) |> + unnest(cols = c(paths)) |> + unnest(cols = c(paths)) |> select(all_of(c(id = "paths", "coastal"))) |> distinct() |> group_by(id) |> diff --git a/R/add_levelpaths.R b/R/add_levelpaths.R index 74e9c7d..7ef9c7e 100644 --- a/R/add_levelpaths.R +++ b/R/add_levelpaths.R @@ -156,7 +156,7 @@ add_levelpaths.hy <- function(x, name_attribute, weight_attribute, x$done[pathids$ind] <- rep(TRUE, nrow(pathids)) # grab everything that goes to the path we just followed - outlet_ind <- stats::na.omit(as.numeric(from_ind$froms[, pathids$ind])) + outlet_ind <- na.omit(as.numeric(from_ind$froms[, pathids$ind])) # remove the path we just follwed outlet_ind <- x[outlet_ind[!outlet_ind %in% pathids$ind],] @@ -218,7 +218,7 @@ get_path <- function(x, tailid, from_ind, status, wat) { while(keep_going) { tryCatch({ - next_tails <- x[stats::na.omit(from_ind$froms[,tailid]), ] + next_tails <- x[na.omit(from_ind$froms[,tailid]), ] if(nrow(next_tails) > 1) { diff --git a/R/add_pfafstetter.R b/R/add_pfafstetter.R index a8235be..66abb1a 100644 --- a/R/add_pfafstetter.R +++ b/R/add_pfafstetter.R @@ -92,7 +92,6 @@ add_pfafstetter.hy <- function(x, max_level = 2, status = FALSE) { } #' @noRd -#' @importFrom dplyr arrange left_join get_pfaf_9 <- function(x, mainstem, max_level, pre_pfaf = 0, assigned = NA, status = FALSE) { if(!levelpath_outlet_id %in% names(x)) { @@ -149,7 +148,7 @@ get_pfaf_9 <- function(x, mainstem, max_level, pre_pfaf = 0, assigned = NA, stat out[["pfaf"]] <- out$p_id + pre_pfaf * 10 if(all(sapply(out$members, function(x) all(is.na(x))))) out$members[[1]] <- mainstem$id - out <- unnest(out, "members") + out <- simple_unnest(out, "members") out <- list(out[!is.na(out$members), ]) if(nrow(out[[1]]) == 0 | all(out[[1]]$members %in% mainstem$id)) { @@ -174,8 +173,6 @@ apply_fun <- function(p, p9, x, max_level, status) { } } -#' @importFrom tidyr pivot_wider -#' @importFrom dplyr select #' @noRd cleanup_pfaf <- function(pfaf) { # Add level number @@ -187,8 +184,8 @@ cleanup_pfaf <- function(pfaf) { # Deduplicate problem tributaries remove <- do.call(c, lapply(1:length(unique(pfaf$level)), function(l, pfaf) { check <- pfaf[pfaf$level == l, ] - check <- dplyr::group_by(check, .data$id) - check <- dplyr::filter(check, n() > 1 & .data$pfaf < max(.data$pfaf))$uid + check <- group_by(check, .data$id) + check <- filter(check, n() > 1 & .data$pfaf < max(.data$pfaf))$uid }, pfaf = pfaf)) pfaf <- pivot_wider(select(pfaf[!pfaf$uid %in% remove, ], -"uid"), diff --git a/R/add_toids.R b/R/add_toids.R index a0a7f0b..3ad6a14 100644 --- a/R/add_toids.R +++ b/R/add_toids.R @@ -41,7 +41,6 @@ add_toids.data.frame <- function(x, return_dendritic = TRUE) { } .datatable.aware=TRUE -#' @importFrom data.table data.table #' @name add_toids #' @export add_toids.hy <- function(x, return_dendritic = TRUE) { diff --git a/R/align_names.R b/R/align_names.R index 8e8730b..8f389c0 100644 --- a/R/align_names.R +++ b/R/align_names.R @@ -74,7 +74,7 @@ align_names <- function(x) { } - x <- rename(x, any_of(stats::setNames(names(replace_names), unname(replace_names)))) + x <- rename(x, any_of(setNames(names(replace_names), unname(replace_names)))) switch_back <- !names(x) %in% good_names diff --git a/R/check_hy_graph.R b/R/check_hy_graph.R index 24bf0c1..2422eaf 100644 --- a/R/check_hy_graph.R +++ b/R/check_hy_graph.R @@ -41,7 +41,7 @@ check_hy_graph <- function(x, loop_check = FALSE) { data.table(drop_geometry(x)), by.x = "toid", by.y = "id", all.x = TRUE) - x <- dplyr::as_tibble(x) + x <- as_tibble(x) check <- x$id == x$toid.y @@ -91,11 +91,11 @@ check_hy_graph_internal <- function(g, all_starts) { f <- make_fromids(g) # used to track which path tops we need to go back to - to_visit_queue <- fastmap::fastqueue(missing_default = 0) + to_visit_queue <- fastqueue(missing_default = 0) lapply(all_starts, function(x) to_visit_queue$add(x)) - out_stack <- fastmap::faststack() + out_stack <- faststack() # to track where we've been visited_tracker <- rep(FALSE, ncol(g$to)) @@ -106,7 +106,7 @@ check_hy_graph_internal <- function(g, all_starts) { # trigger for making a new path new_path <- FALSE - pb = utils::txtProgressBar(0, ncol(g$to), style = 3) + pb = txtProgressBar(0, ncol(g$to), style = 3) on.exit(close(pb)) n <- 0 @@ -119,7 +119,7 @@ check_hy_graph_internal <- function(g, all_starts) { visited_tracker[node] <- TRUE if(!n %% 100) - utils::setTxtProgressBar(pb, n) + setTxtProgressBar(pb, n) # now look at what's downtream and add to a queue @@ -185,7 +185,7 @@ check_hy_graph_internal <- function(g, all_starts) { } - utils::setTxtProgressBar(pb, n) + setTxtProgressBar(pb, n) # if we got this far, Cool! unique(g$to_list$id[as.integer(out_stack$as_list())]) @@ -195,7 +195,7 @@ check_hy_graph_internal <- function(g, all_starts) { loop_search_dfs <- function(g, node, visited_tracker) { # stack to track stuff we need to visit - to_visit_stack <- fastmap::faststack(missing_default = 0) + to_visit_stack <- faststack(missing_default = 0) # while we still have nodes to check while(node != 0) { diff --git a/R/disambiguate.R b/R/disambiguate.R index 0a6c428..9a9bdfb 100644 --- a/R/disambiguate.R +++ b/R/disambiguate.R @@ -51,7 +51,7 @@ disambiguate_indexes <- function(indexes, flowpath, hydro_location) { indexes <- align_names(indexes) - renamer <- stats::setNames(names(indexes), orig_index_names) + renamer <- setNames(names(indexes), orig_index_names) call_rename <- FALSE if(!is.hy(flowpath, silent = TRUE)) call_rename <- TRUE @@ -107,7 +107,7 @@ disambiguate_indexes <- function(indexes, flowpath, hydro_location) { } string_score <- function(x) { - raw_score <- as.numeric(utils::adist(x[[1]], x[[2]], ignore.case = TRUE)) + raw_score <- as.numeric(adist(x[[1]], x[[2]], ignore.case = TRUE)) (1 - (raw_score) / max(c(nchar(x[[1]]), nchar(x[[2]])))) } diff --git a/R/get_hydro_location.R b/R/get_hydro_location.R index f9cb119..2aef57c 100644 --- a/R/get_hydro_location.R +++ b/R/get_hydro_location.R @@ -41,7 +41,7 @@ get_hydro_location <- function(indexes, flowpath) { get_hydro_location_single <- function(x) { - coords <- sf::st_coordinates(x[[2]]) |> + coords <- st_coordinates(x[[2]]) |> add_index() |> add_len() @@ -58,15 +58,15 @@ get_hydro_location_single <- function(x) { if(nds == nus) { return( - sf::st_sfc(sf::st_point(c(coords$X[nds], coords$Y[nds])), - crs = sf::st_crs(x[[2]])) + st_sfc(st_point(c(coords$X[nds], coords$Y[nds])), + crs = st_crs(x[[2]])) )} new_m <- rescale_measures(m, coords$id_measure[nds], coords$id_measure[nus]) new <- interp_meas(new_m, coords$X[nds], coords$Y[nds], coords$X[nus], coords$Y[nus]) - return(sf::st_sfc(sf::st_point(c(new[[1]], new[[2]])), crs = sf::st_crs(x[[2]]))) + return(st_sfc(st_point(c(new[[1]], new[[2]])), crs = st_crs(x[[2]]))) } interp_meas <- function(m, x1, y1, x2, y2) { diff --git a/R/index_points_to_lines.R b/R/index_points_to_lines.R index 91278d6..9816d53 100644 --- a/R/index_points_to_lines.R +++ b/R/index_points_to_lines.R @@ -28,7 +28,7 @@ matcher <- function(coords, points, search_radius, max_matches = 1) { # Now limit to max matches per point matched <- group_by(matched, .data$point_id) |> - filter(dplyr::row_number() <= max_matches) |> + filter(row_number() <= max_matches) |> ungroup() |> as.data.frame() @@ -39,18 +39,18 @@ check_search_radius <- function(search_radius, points) { if(is.null(search_radius)) { if(st_is_longlat(points)) { - search_radius <- units::set_units(0.01, "degrees") + search_radius <- set_units(0.01, "degrees") } else { - search_radius <- units::set_units(200, "m") + search_radius <- set_units(200, "m") - units(search_radius) <- units::as_units( + units(search_radius) <- as_units( st_crs(points, parameters = TRUE)$ud_unit) } } if(!inherits(search_radius, "units")) { warning("search_radius units not set, trying units of points.") - units(search_radius) <- units::as_units( + units(search_radius) <- as_units( st_crs(points, parameters = TRUE)$ud_unit) } @@ -89,9 +89,9 @@ add_index <- function(x) { add_len <- function(x) { x |> - mutate(len = sqrt( ( (.data$X - (dplyr::lag(.data$X))) ^ 2) + - ( ( (.data$Y - (dplyr::lag(.data$Y))) ^ 2)))) |> - mutate(len = tidyr::replace_na(.data$len, 0)) |> + mutate(len = sqrt( ( (.data$X - (lag(.data$X))) ^ 2) + + ( ( (.data$Y - (lag(.data$Y))) ^ 2)))) |> + mutate(len = replace_na(.data$len, 0)) |> mutate(len = cumsum(.data$len)) |> mutate(id_measure = 100 - (100 * .data$len / max(.data$len))) } @@ -199,8 +199,8 @@ index_points_to_lines.hy <- function(x, points, point_buffer <- st_buffer(points, search_radius) - if(units(search_radius) == units(units::as_units("degrees"))) { - if(st_is_longlat(in_crs) & search_radius > units::set_units(1, "degree")) { + if(units(search_radius) == units(as_units("degrees"))) { + if(st_is_longlat(in_crs) & search_radius > set_units(1, "degree")) { warning("search radius is large for lat/lon input, are you sure?") } } @@ -223,7 +223,7 @@ index_points_to_lines.hy <- function(x, points, fline_atts <- drop_geometry(x) - if(sf::st_geometry_type(x, by_geometry = FALSE) != "LINESTRING") { + if(st_geometry_type(x, by_geometry = FALSE) != "LINESTRING") { warning("converting to LINESTRING, this may be slow, check results") } @@ -273,7 +273,7 @@ index_points_to_lines.hy <- function(x, points, x <- x |> mutate(index = seq_len(nrow(x))) |> st_cast("LINESTRING", warn = FALSE) |> - st_segmentize(dfMaxLength = units::as_units(precision, "m")) + st_segmentize(dfMaxLength = as_units(precision, "m")) fline_atts <- right_join(fline_atts, select(drop_geometry(x), @@ -354,8 +354,6 @@ index_points_to_lines.hy <- function(x, points, #' \link[units]{set_units}. #' @return data.frame with two columns, COMID, in_wb_COMID, near_wb_COMID, #' near_wb_dist, and outlet_fline_COMID. Distance is in units of provided projection. -#' @importFrom sf st_join st_geometry_type -#' @importFrom dplyr select mutate bind_cols #' @export #' @examples #' @@ -451,7 +449,7 @@ rename_indexed <- function(x, matched) { orig_aggregate_id <- names(attr(x, "orig_names")[attr(x, "orig_names") == aggregate_id]) new_aggregate_measure <- paste0(orig_aggregate_id, "_measure") - rename(matched, any_of(stats::setNames(c(id, aggregate_id, aggregate_id_measure), + rename(matched, any_of(setNames(c(id, aggregate_id, aggregate_id_measure), c(orig_id, orig_aggregate_id, new_aggregate_measure)))) } diff --git a/R/make_attribute_topology.R b/R/make_attribute_topology.R index be77d8b..5d548f2 100644 --- a/R/make_attribute_topology.R +++ b/R/make_attribute_topology.R @@ -44,8 +44,8 @@ make_attribute_topology.hy <- function(x, min_distance) { # first we get start and end nodes nodes <- as.data.frame(cbind( - sf::st_coordinates(hydroloom::get_node(x, "start")), - sf::st_coordinates(hydroloom::get_node(x, "end")))) + st_coordinates(get_node(x, "start")), + st_coordinates(get_node(x, "end")))) # add the id to the nodes nodes$id <- x$id @@ -60,7 +60,7 @@ make_attribute_topology.hy <- function(x, min_distance) { xs <- 1:nrow(nodes) # apply over allnodes - closest <- pbapply::pblapply(xs, function(x, nodes) { + closest <- pblapply(xs, function(x, nodes) { # distance from one node to all other nodes d <- sqrt((nodes$ex[x] - nodes$sx)^2 + (nodes$ey[x] - nodes$sy)^2) @@ -80,7 +80,7 @@ make_attribute_topology.hy <- function(x, min_distance) { # remove row == torow and get group size. nodes <- select(nodes, all_of(c("row", "torow"))) |> - tidyr::unnest(cols = "torow") |> + unnest(cols = "torow") |> filter(.data$row != .data$torow) |> left_join(drop_geometry(x), by = "row") |> left_join(select(drop_geometry(x), row, toid = id), diff --git a/R/make_fromids.R b/R/make_fromids.R index e2b21c9..89c2c27 100644 --- a/R/make_fromids.R +++ b/R/make_fromids.R @@ -7,7 +7,6 @@ #' "froms_list" element containing all from ids in a list form. #' @return list containing a "froms" matrix, "lengths" vector, #' and optionally "froms_list" elements. -#' @importFrom data.table as.data.table setnames #' @export #' @examples #' @@ -20,7 +19,7 @@ #' make_fromids <- function(index_ids, return_list = FALSE) { - index_ids <- unnest(index_ids$to_list, "toindid") + index_ids <- simple_unnest(index_ids$to_list, "toindid") # froms <- left_join(select(index_ids, "indid"), # select(index_ids, indid = "toindid", fromindid = "indid"), diff --git a/R/make_index_ids.R b/R/make_index_ids.R index 2a8ed41..418d952 100644 --- a/R/make_index_ids.R +++ b/R/make_index_ids.R @@ -61,7 +61,7 @@ make_index_ids.hy <- function(x, long_form = FALSE) { out <- data.table(id = unique(x$id), indid = seq(1, length(unique(x$id)))) - out_rename <- data.table::copy(out) + out_rename <- copy(out) setnames(out_rename, old = "indid", new = "toindid") out <- merge(merge(as.data.table(x)[, list(id, toid)], @@ -69,7 +69,7 @@ make_index_ids.hy <- function(x, long_form = FALSE) { out_rename, by.x = "toid", by.y = "id", all.x = TRUE, sort = FALSE) |> as.data.frame() |> - dplyr::as_tibble() + as_tibble() # dplyr method of the above hanges on large datasets # out2 <- data.frame(id = unique(x$id), diff --git a/R/make_node_topology.R b/R/make_node_topology.R index 925ceb9..00588f1 100644 --- a/R/make_node_topology.R +++ b/R/make_node_topology.R @@ -72,7 +72,7 @@ make_node_topology.hy <- function(x, add_div = NULL, add = TRUE) { } else { if(any(is.na(x$toid))) stop("NA toids found -- must be 0") - if(!all(x$toid[x$toid != hydroloom:::get_outlet_value(x)] %in% x$id)) stop("Not all non zero toids are in ids") + if(!all(x$toid[x$toid != get_outlet_value(x)] %in% x$id)) stop("Not all non zero toids are in ids") if(any(c(fromnode, tonode) %in% names(x))) stop("fromnode or tonode already in data") order <- data.frame(id = x$id) diff --git a/R/navigate_connected_paths.R b/R/navigate_connected_paths.R index 73fecc7..43e3012 100644 --- a/R/navigate_connected_paths.R +++ b/R/navigate_connected_paths.R @@ -8,7 +8,6 @@ #' @return data.frame containing the distance between pairs of network outlets #' and a list column containing flowpath identifiers along path that connect outlets. #' For a network with one terminal outlet, the data.frame will have `nrow(x)^2` rows. -#' @importFrom utils combn tail #' @export #' @examples #' x <- sf::read_sf(system.file("extdata", "walker.gpkg", package = "hydroloom")) @@ -50,7 +49,7 @@ navigate_connected_paths <- function(x, outlets, status = FALSE) { if(status) message("Finding all downstream paths.") - all_dn <- pbapply::pblapply(index$indid[id_match], function(indid, toindid) { + all_dn <- pblapply(index$indid[id_match], function(indid, toindid) { out <- get_dwn(indid, toindid) if((lo <- length(out)) > 1) { out[2:lo] # don't want to include the starting flowpath @@ -94,7 +93,7 @@ navigate_connected_paths <- function(x, outlets, status = FALSE) { get_length <- function(p, length_km) sum(length_km$length_km[p[[1]]], length_km$length_km[p[[2]]]) - path_lengths <- pbapply::pblapply(connected_paths, get_length, length_km = length_km) + path_lengths <- pblapply(connected_paths, get_length, length_km = length_km) path_lengths <- cbind(as.data.frame(matrix(id_match[pairs[lengths(paths) > 0,]], ncol = 2)), diff --git a/R/sort_network.R b/R/sort_network.R index 87b439d..a8241dc 100644 --- a/R/sort_network.R +++ b/R/sort_network.R @@ -184,11 +184,11 @@ sort_network.hy <- function(x, split = FALSE, outlets = NULL) { if(split) { # this is only two columns - ids <- methods::as(names(out_list), class(pull(x[1, 1]))) + ids <- as(names(out_list), class(pull(x[1, 1]))) out_list <- data.frame(ids = ids) |> mutate(set = out_list) |> - unnest("set") + simple_unnest("set") names(out_list) <- c(terminal_id, id) diff --git a/R/utils.R b/R/utils.R index 12b72ea..fba7b81 100644 --- a/R/utils.R +++ b/R/utils.R @@ -12,7 +12,7 @@ get_hyg <- function(x, add, id = "id") { put_hyg <- function(x, hy_g) { if(!is.null(hy_g)) { orig_names <- attr(x, "orig_names") - x <- sf::st_sf(left_join(x, hy_g, by = id)) + x <- st_sf(left_join(x, hy_g, by = id)) attr(x, "orig_names") <- orig_names if(!inherits(x, "hy")) { @@ -30,7 +30,7 @@ replace_na <- function(x, y) { #' simple unnest for a single list column #' @noRd -unnest <- function(x, col) { +simple_unnest <- function(x, col) { times <- lengths(x[[col]]) base_names <- names(x)[!names(x) == col] @@ -39,7 +39,7 @@ unnest <- function(x, col) { names(out) <- base_names - out <- dplyr::bind_cols(out) + out <- bind_cols(out) out[[col]] <- unlist(x[[col]]) @@ -267,7 +267,7 @@ fix_flowdir <- function(id, network = NULL, fn_list = NULL) { rescale_measures <- function(measure, from, to) { tryCatch({ - if(!dplyr::between(measure, from, to)) + if(!between(measure, from, to)) stop("measure must be between from and to") 100 * (measure - from) / (to - from)