-
-
Save scbrown86/e7b7947e93f3ff22ab77a751e69ea1fc to your computer and use it in GitHub Desktop.
Stratified random sampling from a `data.frame` in R
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
stratified <- function(df, group, size, select = NULL, | |
replace = TRUE, bothSets = FALSE) { | |
if (is.null(select)) { | |
df <- df | |
} else { | |
if (is.null(names(select))) stop("'select' must be a named list") | |
if (!all(names(select) %in% names(df))) | |
stop("Please verify your 'select' argument") | |
temp <- sapply(names(select), | |
function(x) df[[x]] %in% select[[x]]) | |
df <- df[rowSums(temp) == length(select), ] | |
} | |
df.interaction <- interaction(df[group], drop = TRUE) | |
df.table <- table(df.interaction) | |
df.split <- split(df, df.interaction) | |
if (length(size) > 1) { | |
if (length(size) != length(df.split)) | |
stop("Number of groups is ", length(df.split), | |
" but number of sizes supplied is ", length(size)) | |
if (is.null(names(size))) { | |
n <- setNames(size, names(df.split)) | |
message(sQuote("size"), " vector entered as:\n\nsize = structure(c(", | |
paste(n, collapse = ", "), "),\n.Names = c(", | |
paste(shQuote(names(n)), collapse = ", "), ")) \n\n") | |
} else { | |
ifelse(all(names(size) %in% names(df.split)), | |
n <- size[names(df.split)], | |
stop("Named vector supplied with names ", | |
paste(names(size), collapse = ", "), | |
"\n but the names for the group levels are ", | |
paste(names(df.split), collapse = ", "))) | |
} | |
} else if (size < 1) { | |
n <- round(df.table * size, digits = 0) | |
} else if (size >= 1) { | |
if (all(df.table >= size) || isTRUE(replace)) { | |
n <- setNames(rep(size, length.out = length(df.split)), | |
names(df.split)) | |
} else { | |
message( | |
"Some groups\n---", | |
paste(names(df.table[df.table < size]), collapse = ", "), | |
"---\ncontain fewer observations", | |
" than desired number of samples.\n", | |
"All observations have been returned from those groups.") | |
n <- c(sapply(df.table[df.table >= size], function(x) x = size), | |
df.table[df.table < size]) | |
} | |
} | |
temp <- lapply( | |
names(df.split), | |
function(x) df.split[[x]][sample(df.table[x], | |
n[x], replace = replace), ]) | |
set1 <- do.call("rbind", temp) | |
if (isTRUE(bothSets)) { | |
set2 <- df[!rownames(df) %in% rownames(set1), ] | |
list(SET1 = set1, SET2 = set2) | |
} else { | |
set1 | |
} | |
} |
Creating a re-sampling index for caret
nBootstraps <- 10
rsIndex <- replicate(nBootstraps, ## Number of bootstrap samples
## Stratifying function that will select (with replacement) 80% of the minimum observed class size from all classes
as.integer(rownames(stratified(classData, "ClimZone", floor(0.8*min(table(classData$ClimZone)))))),
simplify = FALSE)
length(rsIndex)
names(rsIndex) <- paste0("Fold", 1:nBootstraps)
head(rsIndex); tail(rsIndex)
By setting size = 80% of the minimum observed class frequency, we can ensure that there will always be samples for each class in the test folds.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
From the original
The arguments to
stratified
are:df
: The inputdata.frame
group
: A character vector of the column or columns that make up the "strata".size
: The desired sample size.size
is a value less than 1, a proportionate sample is taken from each stratum.size
is a single integer of 1 or more, that number of samples is taken from each stratum.size
is a vector of integers, the specified number of samples is taken for each stratum. It is recommended that you use a named vector.For example, if you have two strata, "A" and "B", and you wanted 5 samples from "A" and 10 from "B", you would enter
size = c(A = 5, B = 10)
.select
: This allows you to subset the groups in the sampling process. This is a list. For instance, if your group variable was"Group"
, and it contained three strata,"A"
,"B"
, and"C"
, but you only wanted to sample from"A"
and"C"
, you can useselect = list(Group = c("A", "C"))
.replace
: For sampling with replacement.