Skip to content
Back to skills

Add Recipe Step

ASecurity

Create a new preprocessing step for the recipes package following

  • 10 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added May 26, 2026
developmentrustgobashtestingdebugginggitdocumentation

Works with

  • cli

Security analysis

A100/100

Pro scans all 20 files and shows the line behind each finding

Scanned May 27, 2026

npx -y skills add tidymodels/skills --skill add-recipe-step --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Add Recipe Step?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Add Recipe Step
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/tidymodels-add-recipe-step/badge)](https://www.skillsdirectory.com/skills/tidymodels-add-recipe-step)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: add-recipe-step
description: Create a new preprocessing step for the recipes package following
  tidymodels conventions
---

# Add Recipe Step

Guide for developing new preprocessing steps that extend the recipes package.
This skill provides best practices, complete code templates, and testing
patterns for creating custom recipe steps.

--------------------------------------------------------------------------------

## Two Development Contexts

This skill supports **two distinct development contexts**:

### šŸ†• Extension Development (Default)

**Creating a new R package** that extends recipes with custom steps.

- āœ… Use this for: New packages, standalone steps, CRAN submissions

- āš ļø **Constraint**: Must use `recipes::` prefix for all functions

### šŸ”§ Source Development (Advanced)

**Contributing directly to recipes** via pull requests.

- āœ… Use this for: Contributing to tidymodels/recipes repository

- ✨ **Benefit**: Can use internal functions directly (no prefix needed)

--------------------------------------------------------------------------------

## Getting Started

**INSTRUCTIONS FOR CLAUDE:** Run the verification script first to determine the
development context:

```bash
Rscript -e 'source(Sys.glob(path.expand("~/.claude/plugins/cache/tidymodels-skills/tidymodels-dev/*/tidymodels/shared-references/scripts/verify-setup.R"))[1])'
```

**Then follow the appropriate path based on the output:**

- **Output: "All checks for source development complete."** → Go to [Source
  Development Guide](references/source-guide.md)

- **Output: "All checks for extension development complete." (no warnings)** →
  Go to [Extension Development Guide](references/extension-guide.md)

- **Output: Shows "Warning - [UUID]" messages** → Go to [Extension
  Prerequisites](references/package-extension-prerequisites.md) to resolve
  warnings first

--------------------------------------------------------------------------------

## Implementation Workflow

**INSTRUCTIONS FOR CLAUDE:** Load references progressively based on step type.

### Step 1: Read Step Type Reference ONLY

Determine step type from user requirements, then read ONLY that reference:

- **Modifies existing columns?** Read
  [modify-in-place-steps.md](references/modify-in-place-steps.md) ONLY

- **Creates new columns?** Read
  [create-new-columns-steps.md](references/create-new-columns-steps.md) ONLY

- **Filters/removes rows?** Read
  [row-operation-steps.md](references/row-operation-steps.md) ONLY

Do NOT read all three references. Read only the one needed for this step type.

### Step 2: Read Additional References If Needed

Read other references ONLY if specifically mentioned or needed:

- **User asks about helpers?** Read
  [helper-functions.md](references/helper-functions.md)

- **User mentions tunable?** Read
  [optional-methods.md](references/optional-methods.md)

- **Troubleshooting?** Read troubleshooting references

Default: Don't pre-load optional references.

--------------------------------------------------------------------------------

## Overview

Creating a custom recipe step provides:

- Integration with the recipes preprocessing pipeline

- Automatic handling of variable selection and roles

- Support for case weights

- Consistent prep/bake workflow

- Integration with tune for hyperparameter optimization

- Proper handling of grouped data frames

- Sparse data support (when applicable)

## Repository Access (Optional but Recommended)

**INSTRUCTIONS FOR CLAUDE:** Check if `repos/recipes/` exists in the current
working directory. Use this to guide development:

**If `repos/recipes/` exists:**

- āœ… Use it as a reference throughout development

- Read source files (e.g., `repos/recipes/R/center.R`) to study implementation
  patterns

- Read test files (e.g., `repos/recipes/tests/testthat/test-step_center.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/recipes/` does NOT exist:**

- Suggest cloning the repository using the scripts in [Repository Access
  Guide](references/package-repository-access.md)

- This is **optional but strongly recommended** for high-quality development

- If the user declines, reference files using GitHub URLs:

  - Format: `https://github.com/tidymodels/recipes/blob/main/R/[file-name].R`

  - Example: https://github.com/tidymodels/recipes/blob/main/R/center.R

  - This allows users to click through to see implementations

**When to use repository references:**

- Complex implementation questions (e.g., "How does recipes handle variable
  roles?")

- Debugging issues (compare user's code to working implementation)

- Understanding patterns (study similar steps)

- Test design (see how recipes tests edge cases)

- Architecture decisions (understand internal structure)

See [Repository Access Guide](references/package-repository-access.md) for setup
instructions.

## Quick Navigation

**Development Guides:**

- [Extension Development Guide](references/extension-guide.md) - Creating new
  packages that extend recipes

- [Source Development Guide](references/source-guide.md) - Contributing PRs to
  recipes itself

**Reference Files:**

- [Step Architecture](references/step-architecture.md) - Three-function pattern,
  prep/bake workflow, step types

- [Modify-in-Place Steps](references/modify-in-place-steps.md) - Transform
  existing columns

- [Create-New-Columns Steps](references/create-new-columns-steps.md) - Generate
  new columns

- [Row-Operation Steps](references/row-operation-steps.md) - Filter or remove
  rows

- [Optional Methods](references/optional-methods.md) - tunable(),
  required_pkgs(), sparsity methods

- [Helper Functions](references/helper-functions.md) - recipes helper function
  reference

**Shared References (Extension Development):**

- [Extension Prerequisites](references/package-extension-prerequisites.md) -
  Extension prerequisites

- [Development Workflow](references/package-development-workflow.md) - Fast
  iteration cycle

- [Testing Patterns (Extension)](references/package-testing-patterns.md) -
  Extension testing guide

- [Roxygen Documentation](references/package-roxygen-documentation.md) -
  Documentation templates

- [Package Imports](references/package-imports.md) - Managing dependencies

- [Best Practices (Extension)](references/package-best-practices.md) - Extension
  code style

- [Troubleshooting (Extension)](references/package-troubleshooting.md) -
  Extension issues

**Source Development Specific:**

- [Testing Patterns (Source)](references/testing-patterns-source.md) - Using
  internal helpers

- [Best Practices (Source)](references/best-practices-source.md) - Using
  internal functions

- [Troubleshooting (Source)](references/troubleshooting-source.md) -
  Source-specific issues

## Development Workflow

See [Development Workflow](references/package-development-workflow.md) for
complete details.

**Fast iteration cycle (run repeatedly):**

1. `devtools::document()` - Generate documentation
2. `devtools::load_all()` - Load your package
3. `devtools::test()` - Run tests

**Final validation (run once at end):**

4. `devtools::check()` - Full R CMD check

**WARNING:** Do NOT run `check()` during iteration. It takes 1-2 minutes and is
unnecessary until you're done.

## Understanding Recipe Steps

See [Step Architecture](references/step-architecture.md) for complete details.

### The Three-Function Pattern

Every recipe step consists of three functions:

1. **Step constructor** (e.g., `step_center()`) - User-facing function

   - Captures user arguments

   - Uses `enquos(...)` to capture variable selections

   - Returns recipe with step added via `add_step()`

2. **Step initialization** (e.g., `step_center_new()`) - Internal constructor

   - Minimal function with no defaults

   - Calls `step(subclass = "name", ...)` to create S3 object

3. **S3 methods** - Required methods for every step:

   - `prep.step_*()` - Estimates parameters from training data

   - `bake.step_*()` - Applies transformation to new data

   - `print.step_*()` - Displays step in recipe summary

   - `tidy.step_*()` - Returns step information as tibble

### The prep/bake Workflow

**prep() - Training phase:**

- Resolves variable selections (e.g., `all_numeric()` → actual column names)

- Validates column types

- Computes statistics/parameters from training data

- Stores learned parameters in step object

- Returns updated step with `trained = TRUE`

**bake() - Application phase:**

- Takes trained step and new data

- Validates required columns exist

- Applies transformation using stored parameters

- Returns transformed data

**Example workflow:**

```r
# Define recipe with step
rec <- recipe(mpg ~ ., data = mtcars) |>
  step_center(all_numeric_predictors())

# prep() trains the step (calculates means)
trained_rec <- prep(rec, training = mtcars)

# bake() applies the step (subtracts means)
new_data <- bake(trained_rec, new_data = mtcars)
```

## Step Type Decision Tree

Choose the appropriate template based on what your step does:

```
ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│ What does your step do?                                    │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
                            │
        ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”¼ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
        │                   │                   │
        ā–¼                   ā–¼                   ā–¼
   Transform           Create new          Remove/filter
   existing            columns             rows
   columns
        │                   │                   │
        ā–¼                   ā–¼                   ā–¼
ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”     ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”     ā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”
│  MODIFY IN  │     │   CREATE    │     │     ROW     │
│    PLACE    │     │ NEW COLUMNS │     │  OPERATION  │
ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜     ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜     ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜
        │                   │                   │
        ā–¼                   ā–¼                   ā–¼
  role = NA           role =            skip = TRUE
  No keep_cols    "predictor"          (usually)
                  keep_original_cols
        │                   │                   │
        ā–¼                   ā–¼                   ā–¼
  Examples:           Examples:           Examples:

  - center            - dummy             - filter

  - scale             - pca               - sample

  - normalize         - interact          - naomit

  - log               - poly              - slice
```

**Decision guide:**

- **Modify-in-place**: Transforms existing columns → [Modify-in-Place
  Steps](references/modify-in-place-steps.md)

- **Create new columns**: Generates new columns from existing →
  [Create-New-Columns Steps](references/create-new-columns-steps.md)

- **Row operations**: Filters or removes rows → [Row-Operation
  Steps](references/row-operation-steps.md)

## Complete Example: Modify-in-Place Step (Centering)

This example shows all required components for a modify-in-place step **using
extension development patterns** (with `recipes::` prefix).

**For source development**, see [Source Development
Guide](references/source-guide.md) for examples using internal functions
directly.

**Reference implementation:** `R/center.R` in recipes repository

### 1. Step constructor

```r
# R/step_center.R

#' Center numeric variables
#'
#' `step_center()` creates a *specification* of a recipe step that will
#' normalize numeric data to have a mean of zero.
#'
#' @inheritParams step_normalize
#' @param ... One or more selector functions to choose variables for this step.
#'   See [recipes::selections()] for more details.
#' @param role Not used by this step since no new variables are created.
#' @param na_rm A logical value indicating whether NA values should be removed
#'   when computing means.
#' @param means A named numeric vector of means. This is `NULL` until computed
#'   by [prep()].
#'
#' @return An updated version of `recipe` with the new step added to the
#'   sequence of any existing operations.
#'
#' @family normalization steps
#' @export
#'
#' @details
#' Centering data means that the average of the variable is subtracted from the
#' data. `step_center` estimates the variable means from the data used in the
#' `training` argument of [prep()]. [bake()] then applies the centering to new
#' data sets using these means.
#'
#' # Tidying
#'
#' When you [`tidy()`][recipes::tidy.recipe()] this step, a tibble is returned
#' with columns `terms`, `value`, and `id`:
#'
#' \describe{
#'   \item{terms}{character, the selectors or variables selected}
#'   \item{value}{numeric, the means}
#'   \item{id}{character, id of this step}
#' }
#'
#' # Case weights
#'
#' This step performs an unsupervised operation that can utilize case weights.
#' As a result, case weights are used with frequency weights as well as
#' importance weights. For more information, see the documentation in
#' [recipes::case_weights] and the examples on `tidymodels.org`.
#'
#' @examplesIf rlang::is_installed("modeldata")
#' data(biomass, package = "modeldata")
#'
#' biomass_tr <- biomass[biomass$dataset == "Training", ]
#' biomass_te <- biomass[biomass$dataset == "Testing", ]
#'
#' rec <- recipe(
#'   HHV ~ carbon + hydrogen + oxygen + nitrogen + sulfur,
#'   data = biomass_tr
#' )
#'
#' center_trans <- rec |>
#'   step_center(carbon, hydrogen)
#'
#' center_obj <- prep(center_trans, training = biomass_tr)
#'
#' transformed_te <- bake(center_obj, biomass_te)
#'
#' biomass_te[1:10, names(transformed_te)]
#' transformed_te
#'
#' tidy(center_trans, number = 1)
#' tidy(center_obj, number = 1)
step_center <- function(
  recipe,
  ...,
  role = NA,
  trained = FALSE,
  means = NULL,
  na_rm = TRUE,
  skip = FALSE,
  id = recipes::rand_id("center")
) {
  recipes::add_step(
    recipe,
    step_center_new(
      terms = rlang::enquos(...),
      trained = trained,
      role = role,
      means = means,
      na_rm = na_rm,
      skip = skip,
      id = id,
      case_weights = NULL
    )
  )
}

step_center_new <- function(terms, role, trained, means, na_rm, skip, id,
                            case_weights) {
  recipes::step(
    subclass = "center",
    terms = terms,
    role = role,
    trained = trained,
    means = means,
    na_rm = na_rm,
    skip = skip,
    id = id,
    case_weights = case_weights
  )
}
```

### 2. prep() method

```r
#' @export
prep.step_center <- function(x, training, info = NULL, ...) {
  # 1. Resolve variable selections to actual column names
  col_names <- recipes::recipes_eval_select(x$terms, training, info)

  # 2. Validate column types
  recipes::check_type(training[, col_names], types = c("double", "integer"))

  # 3. Extract case weights if applicable
  wts <- recipes::get_case_weights(info, training)
  were_weights_used <- recipes::are_weights_used(wts, unsupervised = TRUE)
  if (isFALSE(were_weights_used)) {
    wts <- NULL
  }

  # 4. Compute means for each column
  means <- vapply(
    training[, col_names],
    function(col) {
      if (is.null(wts)) {
        mean(col, na.rm = x$na_rm)
      } else {
        weighted.mean(col, w = as.double(wts), na.rm = x$na_rm)
      }
    },
    numeric(1)
  )

  # 5. Check for issues
  inf_cols <- col_names[is.infinite(means)]
  if (length(inf_cols) > 0) {
    cli::cli_warn(
      "Column{?s} {.var {inf_cols}} returned Inf or NaN. \\
      Consider checking your data before preprocessing."
    )
  }

  # 6. Return updated step with trained = TRUE
  step_center_new(
    terms = x$terms,
    role = x$role,
    trained = TRUE,
    means = means,
    na_rm = x$na_rm,
    skip = x$skip,
    id = x$id,
    case_weights = were_weights_used
  )
}
```

### 3. bake() method

```r
#' @export
bake.step_center <- function(object, new_data, ...) {
  # 1. Get column names from trained step
  col_names <- names(object$means)

  # 2. Validate required columns exist in new data
  recipes::check_new_data(col_names, object, new_data)

  # 3. Apply transformation
  for (col_name in col_names) {
    new_data[[col_name]] <- new_data[[col_name]] - object$means[[col_name]]
  }

  # 4. Return modified data
  new_data
}
```

### 4. print() and tidy() methods

```r
#' @export
print.step_center <- function(x, width = max(20, options()$width - 30), ...) {
  title <- "Centering for "
  recipes::print_step(
    x$columns,
    x$terms,
    x$trained,
    title,
    width,
    case_weights = x$case_weights
  )
  invisible(x)
}

#' @rdname tidy.recipe
#' @export
tidy.step_center <- function(x, ...) {
  if (recipes::is_trained(x)) {
    res <- tibble::tibble(
      terms = names(x$means),
      value = unname(x$means)
    )
  } else {
    term_names <- recipes::sel2char(x$terms)
    res <- tibble::tibble(
      terms = term_names,
      value = rlang::na_dbl
    )
  }
  res$id <- x$id
  res
}
```

### 5. Tests

```r
# tests/testthat/test-center.R

test_that("centering works correctly", {
  rec <- recipe(mpg ~ ., data = mtcars) |>
    step_center(disp, hp)

  prepped <- prep(rec, training = mtcars)
  results <- bake(prepped, mtcars)

  # Check means are approximately zero
  expect_equal(mean(results$disp), 0, tolerance = 1e-7)
  expect_equal(mean(results$hp), 0, tolerance = 1e-7)

  # Check tidy output
  trained_tidy <- tidy(prepped, 1)
  expect_equal(trained_tidy$value, c(mean(mtcars$disp), mean(mtcars$hp)))
})

test_that("centering handles NA correctly", {
  df <- mtcars
  df$disp[1:3] <- NA

  # With na_rm = TRUE (default)
  rec_remove <- recipe(mpg ~ ., data = df) |>
    step_center(disp, na_rm = TRUE)
  prepped <- prep(rec_remove, training = df)
  results <- bake(prepped, df)

  # Mean should be computed ignoring NAs, then subtracted
  expect_true(all(is.na(results$disp[1:3])))
  expect_false(any(is.na(results$disp[4:nrow(df)])))
})

test_that("centering validates input types", {
  df <- data.frame(
    x = 1:5,
    y = letters[1:5]
  )

  rec <- recipe(~ ., data = df) |>
    step_center(y)  # Character column

  expect_error(prep(rec, training = df))
})

test_that("centering works with case weights", {
  df <- mtcars[1:10, ]
  df$weights <- c(rep(1, 5), rep(10, 5))  # Heavy weight on last 5

  rec <- recipe(mpg ~ ., data = df) |>
    step_center(disp)

  # Without weights
  rec_unweighted <- prep(rec, training = df)

  # With weights (need to add case_weights role)
  df_weighted <- df
  df_weighted$weights <- hardhat::importance_weights(df_weighted$weights)

  rec_weighted <- recipe(mpg ~ ., data = df_weighted) |>
    update_role(weights, new_role = "case_weights") |>
    step_center(disp)
  rec_weighted <- prep(rec_weighted, training = df_weighted)

  # Weighted and unweighted should differ
  expect_false(
    tidy(rec_unweighted, 1)$value[1] == tidy(rec_weighted, 1)$value[1]
  )
})
```

**Reference test pattern:** `tests/testthat/test-center.R` in recipes repository

See [Testing Patterns](references/package-testing-patterns.md) for comprehensive
testing guide.

## Implementation Guide by Step Type

### Modify-in-Place Steps

**Use for:** Transform existing columns without creating new ones.

**Pattern:** role = NA, no keep_original_cols parameter

**Complete guide:** [Modify-in-Place Steps](references/modify-in-place-steps.md)

**Key points:**

- Preserve existing column roles with `role = NA`

- Use `recipes_eval_select()` to resolve selections

- Validate with `check_type()` in prep()

- Apply transformation in place in bake()

**Examples:** center, scale, normalize, log

**Reference implementations:**

- Simple transformations: `R/center.R`, `R/scale.R`, `R/normalize.R`

- Math transformations: `R/log.R`, `R/sqrt.R`, `R/logit.R`

- With parameters: `R/BoxCox.R` (power transformation with lambda)

### Create-New-Columns Steps

**Use for:** Generate new columns from existing ones.

**Pattern:** role = "predictor", keep_original_cols parameter

**Complete guide:** [Create-New-Columns
Steps](references/create-new-columns-steps.md)

**Key points:**

- Assign role to new columns with `role = "predictor"`

- Include `keep_original_cols` parameter (default FALSE)

- Use `remove_original_cols()` helper in bake()

- Consider implementing `.recipes_estimate_sparsity()` for sparse columns

**Examples:** dummy, pca, interact, poly

**Reference implementations:**

- Encoding: `R/dummy.R` (one-hot encoding)

- Dimension reduction: `R/pca.R`, `R/ica.R`

- Feature engineering: `R/interact.R`, `R/poly.R`

### Row-Operation Steps

**Use for:** Filter or remove rows from data.

**Pattern:** skip = TRUE by default

**Complete guide:** [Row-Operation Steps](references/row-operation-steps.md)

**Key points:**

- Default `skip = TRUE` since row ops usually only for training

- prep() typically doesn't learn parameters

- bake() applies filtering logic

- Respect skip parameter in bake()

**Examples:** filter, sample, naomit, slice

**Reference implementations:**

- Filtering: `R/filter.R`, `R/filter_missing.R`

- Sampling: `R/sample.R`

- Row removal: `R/naomit.R`, `R/slice.R`

## Helper Functions

See [Helper Functions](references/helper-functions.md) for complete reference.

**Essential helpers:**

- `recipes_eval_select()` - Convert selections to column names (prep)

- `check_type()` - Validate column types (prep)

- `check_new_data()` - Verify columns exist (bake)

- `get_case_weights()` - Extract case weights (prep)

- `are_weights_used()` - Check if weights apply (prep)

- `remove_original_cols()` - Handle keep_original_cols (bake)

- `print_step()` - Standard printing (print)

- `sel2char()` - Convert selections to strings (tidy)

## Optional Methods

See [Optional Methods](references/optional-methods.md) for complete details.

**Optional S3 methods:**

- `tunable()` - Declare parameters for tune package

- `required_pkgs()` - Declare external package dependencies

- `.recipes_preserve_sparsity()` - Indicate sparse preservation

- `.recipes_estimate_sparsity()` - Estimate sparsity of new columns

## Documentation

See [Roxygen Documentation](references/package-roxygen-documentation.md) for
complete templates.

**Required roxygen tags:**

```r
#' @inheritParams step_center
#' @param ... One or more selector functions
#' @param role Role for new variables (or NA)
#' @param trained Logical for training status
#' @param [params] Step-specific parameters
#' @return Updated recipe object
#' @family [category] steps
#' @export
```

## Testing

See [Testing Patterns (Extension)](references/package-testing-patterns.md) for
comprehensive guide.

**Required test categories:** 1. **Correctness**: Step transforms data correctly
2. **Variable selection**: Works with all_numeric(), all_predictors(), etc. 3.
**NA handling**: Both na_rm = TRUE and FALSE 4. **Case weights**: Weighted and
unweighted differ 5. **Infrastructure**: Works in full recipe pipeline 6. **Edge
cases**: Empty data, all same values, etc.

## Package-Specific Patterns (Source Development)

If you're contributing to recipes itself, you have access to internal functions
and conventions not available in extension development.

### File Naming Conventions

Recipes organizes steps by category:

- Normalization: `R/center.R`, `R/scale.R`, `R/normalize.R`

- Encoding: `R/dummy.R`, `R/novel.R`, `R/other.R`

- Dimension reduction: `R/pca.R`, `R/ica.R`

- Row operations: `R/filter.R`, `R/sample.R`

- Tests: `tests/testthat/test-center.R`

### Internal Functions Available

When developing recipes itself, you can use functions directly (no `recipes::`
prefix):

- `recipes_eval_select()` - Variable selection

- `get_case_weights()`, `are_weights_used()` - Case weight handling

- `check_type()` - Column type validation

- `check_new_data()` - Verify columns exist in new data

- `remove_original_cols()` - Handle keep_original_cols

- `print_step()` - Standard printing

- `sel2char()` - Convert selections to strings

### Documentation Patterns

Recipes uses extensive parameter inheritance:

```r
#' @inheritParams step_normalize
#' @template step-return
#' @template case-weights-unsupervised
```

### The Three-Function Pattern (Source)

```r
# 1. Constructor (exported)
step_center <- function(recipe, ..., role = NA, ...) {
  add_step(recipe, step_center_new(...))
}

# 2. Initialization (internal)
step_center_new <- function(terms, role, trained, ...) {
  step(subclass = "center", ...)
}

# 3. Methods (all exported)
prep.step_center <- function(x, training, info = NULL, ...) {
  # Use internal functions directly
  col_names <- recipes_eval_select(x$terms, training, info)
  check_type(training[, col_names], types = c("double", "integer"))
  # ...
}
```

**Complete source development guide:** [Source Development
Guide](references/source-guide.md)

--------------------------------------------------------------------------------

## Best Practices

See [Best Practices](references/package-best-practices.md) for complete guide.

**Key principles:**

- Use base pipe `|>` not magrittr pipe `%>%`

- Prefer for-loops over `purrr::map()` for better error messages

- Use `cli::cli_abort()` for error messages

- Validate early (in prep), trust data in bake

- Use recipes helpers instead of reimplementing

## Troubleshooting

See [Troubleshooting (Extension)](references/package-troubleshooting.md) for
complete guide.

**Common issues:**

- "No visible global function definition" → Add to package imports

- "Object not found" in tests → Use `devtools::load_all()` before testing

- Column selection not working → Check `recipes_eval_select()` usage

- Case weights ignored → Check conversion of hardhat weights

## Related Skills

- [add-yardstick-metric](../add-yardstick-metric/SKILL.md) - Custom recipe steps
  may generate outputs that need custom metrics

- [add-dials-parameter](../add-dials-parameter/SKILL.md) - Recipe steps often
  have tunable parameters that can be optimized

- [add-parsnip-model](../add-parsnip-model/SKILL.md) - Preprocessed data flows
  into model specifications

- [add-parsnip-engine](../add-parsnip-engine/SKILL.md) - Recipe steps prepare
  data for model engines

## Next Steps

**For Extension Development (creating new packages):**

1. **Extension prerequisites:** [Extension
   Prerequisites](references/package-extension-prerequisites.md) - START HERE

**For Source Development (contributing to recipes):**

1. **Start here:** [Source Development Guide](references/source-guide.md)
2. **Clone repository:** See [Repository
   Access](references/package-repository-access.md)
3. **Study existing steps:** Browse `R/center.R`, `R/dummy.R`, `R/pca.R`, etc.
4. **Follow package conventions:** File naming, internal functions,
   three-function pattern
5. **Test with internal helpers:** See [Testing Patterns
   (Source)](references/testing-patterns-source.md)
6. **Submit PR:** See [Source Development Guide](references/source-guide.md) for
   PR process

Files in this skill

  • SKILL.md26.9 KB
  • evals/README.md6.3 KB
  • evals/evals.json9.4 KB
  • references/best-practices-source.md14.8 KB
  • references/create-new-columns-steps.md14.4 KB
  • references/extension-guide.md16.9 KB
  • references/helper-functions.md16.2 KB
  • references/modify-in-place-steps.md13.5 KB
  • references/optional-methods.md10 KB
  • references/package-best-practices.md9.1 KB
  • references/package-development-workflow.md6.9 KB
  • references/package-extension-prerequisites.md16.1 KB
  • references/package-extension-requirements.md33.7 KB
  • references/package-imports.md6.3 KB
  • references/package-repository-access.md13.9 KB
  • references/package-roxygen-documentation.md13.4 KB
  • references/package-testing-patterns.md12.2 KB
  • references/package-troubleshooting.md11.4 KB
  • references/row-operation-steps.md11.3 KB
  • references/scripts/README.md6.7 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.