knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) library(minex)
When you ask for help with an R problem, the standard advice is to post a minimal reproducible example. The reprex package handles the reproducible half: it runs your snippet in a clean session and formats the code together with its output. It does nothing about the minimal half. That part, stripping a script down to the few lines that actually matter, is still done by hand, and it is the tedious step.
minex() automates it. Give it failing code and it returns the smallest subset
of statements that still produces the same error.
Here is a script where only the last line is to blame, surrounded by setup that has nothing to do with the failure.
script <- c( "a <- 10", "b <- 20", "log('not a number')" ) res <- minex(code = script, backend = "inprocess") res
The setup lines are gone. as.character() gives you the bare code, ready to
paste into a bug report.
cat(as.character(res), "\n")
(The examples here use backend = "inprocess" so that they run quickly inside
the vignette. In normal use the default backend = "callr" evaluates each
candidate in a fresh R process, which is what you want when statements have side
effects.)
Reduction never throws away a statement the failure depends on. When a later line needs an earlier one, removing the earlier line changes the error, and the oracle rejects that reduction. Both lines below survive because they are jointly required.
script <- c( "x <- c(1, 2, NA)", "m <- mean(x)", "if (is.na(m)) stop('mean is NA')" ) minex(code = script, backend = "inprocess")
By default a candidate must fail with the same condition message as the original. This is usually the right choice: an over-reduced fragment tends to fail differently (often "object not found"), and message matching rejects it.
If the message embeds changing details such as a value or an index, match on the condition class instead:
minex(code = script, match = "class", backend = "inprocess")
Use match = "both" to require the message and a shared class.
For full control, pass an oracle: a predicate over the statements that returns
TRUE when they still reproduce whatever you care about. With a custom oracle,
minex() does not record a target failure and match is ignored.
minex( code = c("one <- 1", "two <- 2", "three <- 3"), oracle = function(stmts) any(grepl("two", stmts)), backend = "inprocess" )
A bug often hides in a large data frame even though a couple of rows are enough
to trigger it. reduce_rows() runs the same search over rows.
df <- data.frame(id = 1:6, value = c(3, 8, 999, 2, 5, 7)) reduce_rows(df, function(d) any(d$value > 100))
The result is usually small enough to capture with dput().
Both minex() and reduce_rows() are front ends to ddmin(), an
implementation of the delta debugging algorithm of Zeller and Hildebrandt
(2002). ddmin() works on any collection plus a predicate, so it is reusable on
its own.
ddmin(strsplit("the quick brown fox", " ")[[1]], function(s) "fox" %in% s)
It partitions the candidate into blocks, keeps the smallest block (or complement) that still reproduces the behavior, and increases the granularity until every remaining element is load-bearing. The output is one-minimal: removing any single element makes the behavior disappear.
minex() targets R-level conditions (errors). Failures that crash the R process
or hang are not captured as conditions; with the callr backend they are simply
treated as not reproducing the target. Reduction granularity is the top-level
statement, so it will not reach inside a single large expression.
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.