**Create custom tuning parameters for hyperparameter tuning in Tidymodels**
npx claudepluginhub tidymodels/skills --plugin tidymodels-usersThis skill uses the workspace's default tool permissions.
**Create custom tuning parameters for hyperparameter tuning in Tidymodels**
evals/README.mdevals/evals.jsonevals/grading-config.jsonreferences/best-practices-source.mdreferences/data-dependent-parameters.mdreferences/extension-guide.mdreferences/grid-integration.mdreferences/package-best-practices.mdreferences/package-development-workflow.mdreferences/package-extension-prerequisites.mdreferences/package-extension-requirements.mdreferences/package-imports.mdreferences/package-repository-access.mdreferences/package-roxygen-documentation.mdreferences/package-testing-patterns.mdreferences/package-troubleshooting.mdreferences/parameter-system.mdreferences/qualitative-parameters.mdreferences/quantitative-parameters.mdreferences/scripts/README.mdVerifies tests pass on completed feature branch, presents options to merge locally, create GitHub PR, keep as-is or discard; executes choice and cleans up worktree.
Guides root cause investigation for bugs, test failures, unexpected behavior, performance issues, and build failures before proposing fixes.
Writes implementation plans from specs for multi-step tasks, mapping files and breaking into TDD bite-sized steps before coding.
Create custom tuning parameters for hyperparameter tuning in Tidymodels
Guide for creating new dials parameters for hyperparameter tuning. Use when a developer needs to define custom tuning parameters for models, recipes, or workflows, including quantitative parameters (continuous/integer), qualitative parameters (categorical), parameters with transformations, and data-dependent parameters requiring finalization.
This skill supports two distinct development contexts with different capabilities and constraints:
Use when: Creating a new R package that defines custom tuning parameters
✅ Build new packages extending Tidymodels with custom parameters
✅ Use all exported dials functions with dials:: prefix
❌ Cannot use internal functions (:::)
📘 Start here: Extension Development Guide
Package detection: DESCRIPTION file does NOT have Package: dials
Use when: Contributing parameter definitions directly to tidymodels/dials repository
✅ Contribute parameters to dials package itself
✅ Access internal helper functions without dials:: prefix
✅ Use validation helpers (check_type(), check_range())
✅ Create custom finalize functions with range_get()/range_set()
📗 Start here: Source Development Guide
Package detection: DESCRIPTION file has Package: dials
Before you begin, verify your development context:
# Extension development (most common)
usethis::create_package("myextension")
# DESCRIPTION will have: Package: myextension
# Source development (contributing to dials)
# Clone repository: git clone https://github.com/tidymodels/dials
# DESCRIPTION will have: Package: dials
INSTRUCTIONS FOR CLAUDE:
Detect development context by checking:
DESCRIPTION file check (most reliable):
If Package: dials → Source development
If Package: [anything else] → Extension development
Prompt signals (when DESCRIPTION not available):
Extension indicators:
"for my package"
"I'm building/creating [package name]"
"new package called [name]"
"my [package] package"
No mention of cloning or PR
Source indicators:
"contributing to dials"
"PR to tidymodels/dials"
"I'm in the dials repo"
"forked tidymodels/dials"
"working on a feature branch"
"clone of tidymodels/dials"
When uncertain:
Ask explicitly: "Are you creating a new package or contributing to dials itself?"
Default to extension development (most common use case)
Apply appropriate patterns:
Extension: Use dials:: prefix, link to extension-guide.md, create DESCRIPTION if needed
Source: No prefix, link to source-guide.md, assume dials repo structure exists
dials is the tuning parameter infrastructure package for Tidymodels. It provides:
Parameter object definitions (quantitative and qualitative)
Parameter range specifications and transformations
Grid generation methods (regular, random, space-filling)
Integration with tune, parsnip, recipes, and workflows packages
The name reflects the idea that tuning predictive models can be like turning a set of dials on a complex machine.
INSTRUCTIONS FOR CLAUDE: Check if repos/dials/ exists in the current working directory. Use this to guide development:
If repos/dials/ exists:
✅ Use it as a reference throughout development
Read source files (e.g., repos/dials/R/param_mtry.R) to study implementation patterns
Read test files (e.g., repos/dials/tests/testthat/test-param_mtry.R) for testing patterns
Reference these files when answering complex questions or solving problems
Look at actual code structure, validation patterns, and edge case handling
If repos/dials/ does NOT exist:
Suggest cloning the repository using the scripts in Repository Access Guide
This is optional but strongly recommended for high-quality development
If the user declines, reference files using GitHub URLs:
Format: https://github.com/tidymodels/dials/blob/main/R/[file-name].R
Example: https://github.com/tidymodels/dials/blob/main/R/param_mtry.R
This allows users to click through to see implementations
When to use repository references:
Complex implementation questions (e.g., "How does dials handle finalization?")
Debugging issues (compare user's code to working implementation)
Understanding patterns (study similar parameters)
Test design (see how dials tests edge cases)
Architecture decisions (understand internal structure)
See Repository Access Guide for setup instructions.
┌─────────────────────────────────────────────────────────────┐
│ What type of tuning parameter do you need? │
└─────────────────────────────────────────────────────────────┘
│
┌───────────────────┴───────────────────┐
│ │
▼ ▼
Numeric values Categorical choices
(continuous or integer) (discrete options)
│ │
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ QUANTITATIVE │ │ QUALITATIVE │
│ PARAMETER │ │ PARAMETER │
└─────────────────┘ └─────────────────┘
│ │
│ │
├───────────┬────────────┐ │
▼ ▼ ▼ ▼
Simple Transformed Data- Examples:
range (log scale) dependent - activation()
- weight_func()
│ │ │ - prune_method()
▼ ▼ ▼
Examples: Examples: Examples:
- threshold - penalty - mtry
- mixture - learn_rate - num_comp
- neighbors - cost - sample_size
Decision guide:
Quantitative, simple range: Fixed numeric bounds, no transformation → Quantitative Parameters
Quantitative, transformed: Log scale or other transformation → Quantitative Parameters + Transformations
Quantitative, data-dependent: Upper bound depends on dataset → Quantitative Parameters + Data-Dependent Parameters
Qualitative: Discrete categorical options → Qualitative Parameters
A threshold parameter with fixed range (extension pattern):
# R/param_my_threshold.R
#' Threshold value
#'
#' A threshold parameter for filtering or classification decisions.
#'
#' @param range A two-element vector with the lower and upper bounds.
#' @param trans A transformation object (default NULL for no transformation).
#'
#' @details
#' This parameter is used for models or recipes that require a threshold value.
#'
#' @examples
#' my_threshold()
#' my_threshold(range = c(0, 0.5))
#'
#' @export
my_threshold <- function(range = c(0, 1), trans = NULL) {
dials::new_quant_param(
type = "double",
range = range,
inclusive = c(TRUE, TRUE),
trans = trans,
label = c(my_threshold = "Threshold Value"),
finalize = NULL
)
}
A penalty parameter on log scale (extension pattern):
# R/param_my_penalty.R
#' Penalty amount
#'
#' A penalty parameter for regularization on log scale.
#'
#' @param range A two-element vector with the lower and upper bounds
#' (in log10 units).
#' @param trans A transformation object (default log10 transformation).
#'
#' @details
#' This parameter uses a log10 transformation, so `range = c(-5, 0)`
#' represents actual penalty values from 10^-5 to 1.
#'
#' @examples
#' my_penalty() # Range: 10^-10 to 1
#' my_penalty(range = c(-5, 0)) # Range: 10^-5 to 1
#'
#' @export
my_penalty <- function(range = c(-10, 0), trans = scales::transform_log10()) {
dials::new_quant_param(
type = "double",
range = range,
inclusive = c(TRUE, TRUE),
trans = trans,
label = c(my_penalty = "Penalty Amount"),
finalize = NULL
)
}
A parameter with unknown upper bound (extension pattern):
# R/param_num_features.R
#' Number of features
#'
#' The number of features to select, depends on data.
#'
#' @param range A two-element vector with the lower and upper bounds.
#' @param trans A transformation object (default NULL).
#'
#' @details
#' The upper bound is set to `unknown()` and must be finalized using
#' `finalize()` with training data.
#'
#' @examples
#' num_features()
#'
#' # Finalize with data
#' param <- num_features()
#' finalized <- dials::finalize(param, mtcars[, -1])
#' finalized$range$upper # Will be number of columns
#'
#' @export
num_features <- function(range = c(1L, dials::unknown()), trans = NULL) {
dials::new_quant_param(
type = "integer",
range = range,
inclusive = c(TRUE, TRUE),
trans = trans,
label = c(num_features = "# Features to Select"),
finalize = dials::get_p # Built-in finalize function
)
}
A parameter with custom finalization logic (extension pattern):
# R/param_num_initial_terms.R
#' Number of initial MARS terms
#'
#' The number of initial terms for MARS model, depends on data.
#'
#' @param range A two-element vector with the lower and upper bounds.
#' @param trans A transformation object (default NULL).
#'
#' @details
#' The upper bound is set to `unknown()` and is finalized using a custom
#' function based on the earth package formula: min(200, max(20, 2 * ncol(x))) + 1
#'
#' @examples
#' num_initial_terms()
#'
#' # Finalize with data
#' param <- num_initial_terms()
#' finalized <- dials::finalize(param, mtcars[, -1])
#' finalized$range$upper
#'
#' @export
num_initial_terms <- function(range = c(1L, dials::unknown()), trans = NULL) {
dials::new_quant_param(
type = "integer",
range = range,
inclusive = c(TRUE, TRUE),
trans = trans,
label = c(num_initial_terms = "# Initial MARS Terms"),
finalize = get_initial_mars_terms
)
}
# Custom finalize function
get_initial_mars_terms <- function(object, x) {
# Calculate upper bound based on number of predictors
upper_bound <- min(200, max(20, 2 * ncol(x))) + 1
upper_bound <- as.integer(upper_bound)
# Get current range and update upper bound
bounds <- dials::range_get(object)
bounds$upper <- upper_bound
dials::range_set(object, bounds)
}
A categorical parameter with options (extension pattern):
# R/param_aggregation.R
#' Aggregation method
#'
#' The method to use for aggregating embeddings.
#'
#' @param values A character vector of possible methods.
#'
#' @details
#' This parameter defines how embeddings are aggregated.
#' By default, no aggregation is performed.
#'
#' @examples
#' values_aggregation
#' aggregation()
#' aggregation(values = c("none", "mean"))
#'
#' # Sample values
#' set.seed(123)
#' aggregation() %>% dials::value_sample(3)
#'
#' @export
aggregation <- function(values = values_aggregation) {
dials::new_qual_param(
type = "character",
values = values,
default = "none",
label = c(aggregation = "Aggregation Method")
)
}
#' @rdname aggregation
#' @export
values_aggregation <- c("none", "min", "max", "mean", "sum")
Extension Development Guide - Creating new packages with custom parameters
Source Development Guide - Contributing to dials package
Parameter System Overview - Architecture and parameter classes
Quantitative Parameters - Creating numeric parameters
Qualitative Parameters - Creating categorical parameters
Transformations - Using log scale and custom transformations
Data-Dependent Parameters - Using unknown() and finalization
Grid Integration - How parameters work with grids
Testing Patterns (Source) - dials-specific testing
Best Practices (Source) - dials conventions and patterns
Troubleshooting (Source) - Common issues in dials
Before creating custom parameters in a new package, ensure your package is properly set up:
R Package Structure: See Extension Prerequisites
Dependencies: Add dials to DESCRIPTION Imports
Roxygen: Configure documentation system
Testing: Set up testthat framework
To contribute parameters to dials:
Clone the repository:
git clone https://github.com/tidymodels/dials
cd dials
Install dependencies:
pak::pak()
Load the package:
devtools::load_all()
See Source Development Guide for complete setup.
For rapid parameter development:
R/ directorydevtools::load_all()See Development Workflow for details.
Essential tests for all parameters:
Range validation: Parameter accepts valid ranges
Type checking: Correct type enforcement
Grid integration: Works with grid_regular(), grid_random()
Value utilities: value_sample() and value_seq() work correctly
Edge cases: Invalid inputs produce errors
See testing guides:
Extension: Testing Requirements
Source: Testing Patterns (Source)
dials follows strict naming conventions:
Parameter files: R/param_[name].R
Test files: tests/testthat/test-params.R (shared), test-constructors.R
One parameter per file (usually)
Use roxygen tags consistently:
#' @inheritParams new_quant_param
#' @param range A two-element vector with the lower and upper bounds.
#' @details
#' This parameter is used for...
#' @examples
#' my_param()
#' @export
See Roxygen Documentation for complete patterns.
For qualitative parameters, create a values_* vector:
#' @rdname param_name
#' @export
values_param_name <- c("option1", "option2", "option3")
This convention is strongly recommended for consistency.
repos/dials/R/param_*.Rtests/testthat/test-params.Radd-yardstick-metric - Custom metrics may need custom tuning parameters
add-recipe-step - Recipe steps often have tunable parameters
add-parsnip-model - Model specifications have tunable main arguments
add-parsnip-engine - Model engines have tunable parameters
Extension development:
R/param_[name].R (with complete roxygen docs and examples)
tests/testthat/test-param_[name].R (comprehensive tests)
README.md (only if package has no README)
Expected total: 2-3 files (aim for these targets; acceptable to exceed by 2-3 files if implementation requires it)
Source development:
R/param_[name].R (with complete roxygen docs and examples)
tests/testthat/test-param_[name].R (or additions to existing test file)
Expected total: 2 files (aim for these targets; acceptable to exceed by 2-3 files if implementation requires it)
Files to avoid creating:
Documentation files (content belongs in roxygen comments):
IMPLEMENTATION_SUMMARY.md, IMPLEMENTATION_NOTES.md, QUICKSTART.md, QUICK_REFERENCE.md
INDEX.md, FILE_GUIDE.md, SUMMARY.md, OVERVIEW.md, INTEGRATION_GUIDE.md
example_usage.R, USAGE_EXAMPLE.R
PR-related files (content belongs in conversation):
PR_CHECKLIST.md, PR_DESCRIPTION.md, PR_SUMMARY.md
NEWS_entry.md, pkgdown_update.txt
WORKFLOW_COMMANDS.sh, setup.sh
For PRs to dials:
README.md (dials already has one)
test-params-addition.R (tell user in conversation what to add)
Where content belongs:
Examples → roxygen @examples in R file
Implementation notes → roxygen @details in R file
PR description → conversation with user
NEWS entry → conversation (maintainer adds it)
Test additions → conversation (tell user what to add)
Creating extra documentation files clutters the codebase. All documentation should be in roxygen comments (for code) or in conversation (for PR descriptions).
See extension-guide.md Step 5 and source-guide.md "File Creation Guidelines for PRs" for detailed enforcement rules.
Last Updated: 2026-03-31