Nothing
#' Remove hard to classify points
#'
#' Under-samples the majority classes by removing the points that are hardest
#' to classify.
#'
#' @inheritParams step_instance_hardness
#' @param df data.frame or tibble. Must have 1 factor variable and remaining
#' numeric variables.
#' @param var Character, name of variable containing factor variable.
#' @param k An integer. Number of nearest neighbors used to estimate the
#' instance hardness of each observation.
#'
#' @return A data.frame or tibble, depending on type of `df`.
#' @export
#'
#' @template details-instance_hardness
#'
#' @details
#' All columns used in this function must be numeric with no missing data.
#'
#' @references Smith, M. R., Martinez, T., & Giraud-Carrier, C. (2014). An
#' instance level analysis of data complexity. Machine learning, 95(2),
#' 225-256.
#'
#' @seealso [step_instance_hardness()] for step function of this method
#' @family Direct Implementations
#'
#' @examples
#' circle_numeric <- circle_example[, c("x", "y", "class")]
#'
#' res <- instance_hardness(circle_numeric, var = "class")
#'
#' res <- instance_hardness(circle_numeric, var = "class", k = 10)
#'
#' res <- instance_hardness(circle_numeric, var = "class", under_ratio = 1.5)
#'
#' res <- instance_hardness(circle_numeric, var = "class", distance = "manhattan")
instance_hardness <- function(
df,
var,
k = 5,
under_ratio = 1,
distance = "euclidean"
) {
check_data_frame(df)
check_var(var, df)
check_number_whole(k, min = 1)
check_ratio(under_ratio)
check_distance_arg(distance)
predictors <- setdiff(colnames(df), var)
check_numeric(df[, predictors])
check_na(select(df, -all_of(var)))
instance_hardness_impl(
df,
var,
ignore_vars = character(),
k,
under_ratio,
distance = distance
)
}
instance_hardness_impl <- function(
df,
var,
ignore_vars,
k = 5,
under_ratio = 1,
distance = "euclidean",
call = caller_env()
) {
classes <- downsample_count(df, var, under_ratio, call = call)
if (length(classes) == 0) {
return(df)
}
df_only <- df[, !names(df) %in% ignore_vars]
full <- as.matrix(df_only[, names(df_only) != var])
labels <- df[[var]]
if (nrow(full) <= k) {
cli::cli_abort(
c(
"Not enough observations to compute {k} nearest neighbors.",
i = "{nrow(full)} observation{?s} {?was/were} found, but {k + 1} {?is/are} needed."
),
call = call
)
}
idx <- nn_indices(full, k, distance)
deleted_rows <- integer()
for (i in seq_along(classes)) {
which_rows <- which(labels == names(classes)[i])
neigh_idx <- idx[which_rows, , drop = FALSE]
neigh_labels <- matrix(labels[neigh_idx], nrow = nrow(neigh_idx))
disagree <- neigh_labels != labels[which_rows]
disagree[neigh_idx == which_rows] <- NA
hardness <- rowMeans(disagree, na.rm = TRUE)
n_keep <- length(which_rows) - classes[i]
selected_ind <- rank(hardness, ties.method = "first") <= n_keep
deleted_rows <- c(deleted_rows, which_rows[!selected_ind])
}
if (length(deleted_rows) > 0) {
df <- df[-deleted_rows, ]
}
df
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.