Model or dataset
tidymodels/parsnip avatar
tidymodels/parsnip

parsnip lets you swap the engine without rewriting the model

A tidy unified interface to models

661 stars115 forksRNOASSERTION

At a glance

What is it?
parsnip is the tidymodels package that gives R a single way to specify a model, so the same random forest specification can be fitted on ranger or on Spark, and the price is a vocabulary of types, modes and engines that has to be learned before the first call.
Who is it for?
parsnip fits a team that compares model families and needs to change implementation without rewriting every script, and it fits anyone who has lost an afternoon to argument names that differ between packages. It does not fit a project that needs an engine specific feature parsnip does not expose, because engine specific arguments still have to be passed through the engine argument of set_engine, and it does not fit a pipeline that wants no abstraction layer at all.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 26 days ago.
What is it written in?
Mainly R, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The same forest, three incompatible interfaces

The opening example in the README is the argument for the whole package. Fitting a random forest regression model can be done three ways, and the three calls barely look alike:

r
# From randomForest
rf_1 <- randomForest(
  y ~ ., 
  data = dat, 
  mtry = 10, 
  ntree = 2000, 
  importance = TRUE
)

The ranger version takes the same idea with different argument names and a different importance value, and the sparklyr version does not use formula syntax at all. It passes the data, the response and the features separately, and it names the number of trees num.trees and the sampling rate col.sample.rate.

The README's summary is that the model syntax can be very different and that the argument names and their formats are also different, which it calls a pain if you switch between implementations. That pain is the reason parsnip exists, and it is a more honest framing than a claim about elegance.

The stated goal of the package is to provide a tidy, unified interface to models that can be used to try a range of models without getting bogged down in the syntactical minutiae of the underlying packages. The tidymodels family exists because a modelling workflow has many steps, and if the model fitting step is different for every model, every step that comes after it is different too.

Type, mode and engine are the three words to learn

The README decomposes the problem into three concepts, and this is the vocabulary the whole package is built on.

The type of the model is random forest. The mode is regression, as opposed to classification and the other modes. The computational engine is the name of the R package that will do the work, which is ranger or randomForest or sparklyr depending on the implementation.

The goals of parsnip follow directly. It separates the definition of a model from its evaluation, so a specification can be built, inspected and reused before anything is fitted. It decouples the model specification from the implementation, whether that implementation is in R, in Spark or somewhere else, so the user calls rand_forest rather than ranger::ranger or another specific package function.

And it harmonises argument names, so that n.trees, ntrees and trees become one name a user only has to remember once. The README is careful to note that this helps across model types as well, so trees means the same thing for a random forest as it does for boosting or bagging. That is the part that pays off over a project rather than over a single script.

The engine itself is still an R package that has to be installed, which is the most commonly missed detail: parsnip unifies the interface, it does not bundle the implementations.

A model specification is built with the pipe

The parsnip version of the random forest is a three step pipeline, and the printed result shows what the object holds before any fitting happens.

r
library(parsnip)

rand_forest(mtry = 10, trees = 2000) |>
  set_engine("ranger", importance = "impurity") |>
  set_mode("regression")
#> Random Forest Model Specification (regression)
#> 
#> Main Arguments:
#>   mtry = 10
#>   trees = 2000
#> 
#> Engine-Specific Arguments:
#>   importance = impurity
#> 
#> Computational engine: ranger

The printed structure is worth reading. The main arguments are the harmonised ones, the engine specific arguments are separated out, and the computational engine is named at the bottom. Nothing has been fitted yet, and the object is a specification, not a model.

The engine argument of set_engine is where the implementation details go, which is how the package keeps the promise of decoupling while still letting you reach the underlying package. importance = impurity is a ranger argument, and it is passed through verbatim.

Changing the engine is a one word edit, and the README shows the Spark case directly:

r
rand_forest(mtry = 10, trees = 2000) |>
  set_engine("spark") |>
  set_mode("regression")
#> Random Forest Model Specification (regression)
#> 
#> Main Arguments:
#>   mtry = 10
#>   trees = 2000
#> 
#> Computational engine: spark

Note what is absent from the Spark output: there are no engine specific arguments, because the specification for the Spark engine did not set any.

Fitting is the same call whichever engine you chose

Both specifications are fitted the same way, with a set.seed for reproducibility and the familiar formula interface:

r
set.seed(192)
rand_forest(mtry = 10, trees = 2000) |>
  set_engine("ranger", importance = "impurity") |>
  set_mode("regression") |>
  fit(mpg ~ ., data = mtcars)
#> parsnip model object
#> 
#> Ranger result
#> 
#> Call:
#>  ranger::ranger(x = maybe_data_frame(x), y = y, mtry = min_cols(~10,      x), num.trees = 2000, importance = "impurity", num.threads = 1,      verbose = FALSE, seed = sample.int(10^5, 1)) 

The printed call is where the abstraction shows its working, and it is worth reading closely. The harmonised trees = 2000 became num.trees = 2000 for ranger. The mtry value of 10 became min_cols(~10, x), a ranger expression that caps the value at the number of columns. And parsnip supplied several arguments the user never wrote: num.threads = 1, verbose = FALSE, and a seed drawn with sample.int(10^5, 1).

That last detail has a consequence. A model that is supposed to be reproducible needs the seed to be controlled, and here the seed is generated inside the call. The set.seed in the example makes the whole run reproducible, but the user has to know that the package is drawing a random seed for the engine, and that a single threaded engine is the default, which is a performance choice that a large data set will care about.

Installing it alone, with tidymodels, or from source

The installation section offers three routes, and they differ in how much you get.

r
# The easiest way to get parsnip is to install all of tidymodels:
install.packages("tidymodels")

# Alternatively, install just parsnip:
install.packages("parsnip")

# Or the development version from GitHub:
# install.packages("pak")
pak::pak("tidymodels/parsnip")

Installing the whole tidymodels metapackage is described as the easiest way, and it is the right choice for a project that will use the rest of the family, since the packages are designed to work together. Installing parsnip alone is for the case where you only want the model specification layer.

The third route goes through pak, the R package manager, for the development version from GitHub, which is what you want when you need a fix that has not reached CRAN yet or want to work against the main branch.

Whatever the route, the engines are separate. A parsnip installation does not bring ranger or any other engine with it, so a project that fits a random forest still has to install ranger. That is a deliberate separation, since the point of the engine concept is that you can swap implementations, and it also means a Docker image for a modelling pipeline has more than one layer to it.

The alternative, and what the abstraction does not hide

The alternative is to call the packages directly, which is what the first example in the README does, and the trade is a straight exchange of convenience for control.

Calling ranger::ranger directly gives you every argument the package exposes, in its own names, with its own documentation, and no translation layer to learn. It also gives you a different script for every engine, which is the cost the parsnip README describes first. If you fit one model family on one engine and never change, the direct call is arguably simpler than the specification object.

What parsnip does not hide is the engine. The engine specific arguments still have to be passed through set_engine, in the engine's own vocabulary, so a feature that only one engine has is still an engine specific argument. The abstraction harmonises the common case, which is the majority of arguments, and leaves the rest to you.

The second limitation is that parsnip is a model specification layer, not a modelling workflow. It will fit a model for you, and it hands the result to whichever engine produced it, but tuning, resampling, comparison and deployment live in other tidymodels packages. A team that wants the whole workflow should install tidymodels; a team that only wants a consistent way to specify models can use parsnip on its own.

A list of all parsnip models across different CRAN packages is published at tidymodels.org/find/parsnip, and that page is the practical check for whether the model you need is covered.

A custom licence, a stable badge and a quarterly release line

The licence recorded for the repository is a custom one that GitHub cannot classify, and the terms to read are in the LICENSE file, with a second LICENSE.md in the root. That is worth checking before you build a dependency on it, since the standard R package licences are not what the classifier recognises here.

The activity signals are good. The repository is not archived, the last push was on 2026-09-27, and the recent releases are v1.6.1 on 2026-09-27, v1.6.0 on 2026-05-14 and v1.5.0 on 2026-04-10. The 1.6.1 release and the push share a date, so the tagged version matches the branch.

The badges say a lot about the project's status. There is a CRAN status badge, which means the package is published on CRAN, a lifecycle badge marked stable, an R-CMD-check workflow for the standard R package checks, and a Codecov badge for test coverage. A stable lifecycle badge from the r-lib project means the package has reached the stage where its interface is considered settled, which is a stronger statement than a version number.

The repository layout matches a serious R package. There is R/ for the source, man/ for the generated documentation with man-roxygen/ for the templates, tests/, vignettes/, data/, inst/, a NEWS.md for the changelog, a DESCRIPTION and a NAMESPACE, a _pkgdown.yml for the site, and plans/ for development plans. There are also AGENTS.md and a .claude/ directory, and an air.toml, which is the configuration for the Air formatter, so the project is set up for agent-assisted work and for a specific formatting style.

Editorial conclusion

parsnip fits a team that compares model families and needs to change implementation without rewriting every script, and it fits anyone who has lost an afternoon to argument names that differ between packages. It does not fit a project that needs an engine specific feature parsnip does not expose, because engine specific arguments still have to be passed through the engine argument of set_engine, and it does not fit a pipeline that wants no abstraction layer at all. Before adopting it, check which engines the model you care about supports, since the engine has to be installed separately, and read the model list at tidymodels.org/find/parsnip to see what is actually available.

Frequently asked questions

What problem does parsnip solve?

Different R modelling packages offer the same model with different interfaces and different argument names, so parsnip separates the model definition from its evaluation, decouples the specification from the implementation package, and harmonises argument names such as n.trees, ntrees and trees into one.

How do I switch parsnip between engines?

You change the engine argument of set_engine. The README shows the same specification built with set_engine("ranger", importance = "impurity") and then with set_engine("spark"), and both are fitted with the same fit call.

Do I have to install the modelling engine separately?

Yes. The engine is the R package that does the work, so parsnip unifies the interface without bundling the implementations, and a project fitting a random forest still needs ranger installed.

How do I install parsnip?

With install.packages("tidymodels") for the whole family, install.packages("parsnip") for the package alone, or pak::pak("tidymodels/parsnip") for the development version from GitHub using the pak package manager.

Is parsnip actively maintained and on CRAN?

The repository carries a CRAN status badge and a stable lifecycle badge, the last push was on 2026-09-27, and the recent releases are v1.6.1 on 2026-09-27, v1.6.0 on 2026-05-14 and v1.5.0 on 2026-04-10.

Official sources

  1. Issues
  2. Project website
  3. README
  4. Releases
  5. tidymodels/parsnip on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/tidymodels-parsnip.svg)](https://hysenlabs.com/projects/tidymodels-parsnip)

Was this useful?