infer: tidyverse-flavoured statistical inference in R
An R package for tidyverse-friendly statistical inference
At a glance
- What is it?
- infer is an R package that performs statistical inference through four verbs, specify, hypothesize, generate and calculate, so that a permutation test reads like a pipeline rather than a call to a hypothesis test function. It ships with the General Social Survey sample data, and the p-value it computes in the worked example is 0.053.
- Who is it for?
- infer suits an R user who wants to see and to teach the mechanics of a randomization test rather than call a black box test function, and who thinks in the pipe syntax the rest of the tidyverse uses. It is the wrong tool for a user who wants one function that returns a p-value with no intermediate steps, since the four-verb pipeline is more code for the same result in the simple cases, and the wrong choice if you need a test the package does not implement.
- 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?
- Activity is slowing. The repository last received commits 6 months 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 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
The four verbs and why the grammar is the point
The objective of the package is to perform statistical inference using an expressive statistical grammar that coheres with the tidyverse design framework. The package is centered around four main verbs, supplemented with many utilities to visualize and extract value from their outputs.
The verbs are the whole design. `specify()` allows you to specify the variable, or relationship between variables, that you are interested in. `hypothesize()` allows you to declare the null hypothesis. `generate()` allows you to generate data reflecting the null hypothesis. `calculate()` allows you to calculate a distribution of statistics from the generated data to form the null distribution.
That is a four step pipeline, and the figure in the README shows it as a diagram: specify hypothesis, generate data, calculate statistic, and visualize, with each step connected by an arrow, and a note that generating data and calculating statistics can happen iteratively.
The pedagogy is the argument for the design. A conventional hypothesis test hides the null distribution behind a function call, and a student who has never seen what the null distribution looks like has no way to develop intuition for what a p-value means. Here the null distribution is an object you hold, you can plot it, and you can shade the tail. The README's own recommendation to read the infer vignette for the principles behind the design points the same way.
The formula for the design is in the tutorial material the README points to, Statistical Inference Via Data Science, and Introduction to Modern Statistics.
The worked example and the p-value it produces
The README's example uses the gss dataset supplied by the package, a sample from the General Social Survey, and it is a complete pipeline rather than a fragment.
The first step calculates the observed statistic.
F_hat <- gss |>
specify(age ~ partyid) |>
calculate(stat = "F")The second generates the null distribution.
null_dist <- gss |>
specify(age ~ partyid) |>
hypothesize(null = "independence") |>
generate(reps = 1000, type = "permute") |>
calculate(stat = "F")Note what is happening between them. The first pipeline has no hypothesize and no generate, so it computes the statistic on the observed data. The second declares the null of independence, permutes the data 1000 times, and computes the statistic on each permutation to build the null distribution.
The third step visualises the observed statistic against that distribution.
visualize(null_dist) +
shade_p_value(obs_stat = F_hat, direction = "greater")And the fourth extracts the p-value.
null_dist |>
get_p_value(obs_stat = F_hat, direction = "greater")The result is 0.053, from a tibble with one row and one column.
That number deserves a moment's attention, because it illustrates the whole point of the package. 0.053 is a p-value that lands just on the wrong side of the conventional 0.05 threshold, and the decision depends entirely on the threshold you pick and on the direction. The histogram in the README shows a right-tailed F distribution centred around one, with the region to the right of the observed statistic shaded red. The reader can see that the observed statistic sits near the edge of the bulk of the distribution rather than somewhere extreme, which is something the p-value alone does not convey.
Two interfaces for the same analysis
There is a detail in the README that matters more than it first appears, and it is the point about the formula and non-formula interfaces.
The note says the formula and non-formula interfaces, `age ~ partyid` against `response = age, explanatory = partyid`, work for all implemented inference procedures in infer. Use whatever is more natural for you. But if you will be doing modeling with functions such as lm() and glm(), the recommendation is to begin to use the formula notation as soon as possible.
So the same pipeline can be written in two styles. The formula style matches what you would write in a model, and the non-formula style names the response and the explanatory variables explicitly.
That is a design choice about who the package is for. Someone arriving from base R has seen specify(response, explanatory) style arguments in many places, and the package supports that so they are not forced to learn a new notation on day one. Someone who uses the tidyverse already writes y ~ x everywhere, and the package supports that too.
The practical advice, though, is the other way. The README tells you to move to the formula notation if you are going to be doing modelling, because the consistency across your own code is worth more than the convenience of matching an older habit. The package tolerates both, and the recommendation is that you should not.
Installation and where to look for help
Installation from CRAN is the short version.
install.packages("infer")The developmental version is a separate path, and the README is specific about it. Make sure to install remotes first, though the snippet it actually shows uses pak rather than remotes.
# install.packages("pak")
pak::pak("tidymodels/infer")The pkgdown site for the development version is at infer.tidymodels.org, while the stable package documentation is at the r-pkg.org and CRAN pages linked by the badges.
The support routes are tidyverse-standard and worth listing. Questions and discussions about tidymodels packages, modelling and machine learning go to Posit Community, with a category and tag set for tidymodels and questions. Bugs go to the GitHub issue tracker. And in both cases the README asks you to learn how to create and share a reprex, a minimal reproducible example, so that the code in the report is unambiguous. It also links the tidymodels contributing guidelines and their help page.
The project is part of the tidymodels organisation, the licence field on the repository records a custom licence that GitHub cannot classify with the terms in the LICENSE file, and there is also a LICENSE.md. The recent releases are v1.1.0 from 2025-12-18, v1.0.9 from 2025-06-27 and v1.0.8 from 2025-04-14, and the last push was on 2026-04-13.
Where infer is the wrong choice
The first limitation is verbosity for simple cases. If you want a p-value from a two-sample comparison, a base R t.test or wilcox.test is one line, and the infer equivalent is a four or five step pipeline. That is a fair price for learning and an unnecessary one in production code where the test is fixed and known.
The second is that the package is a fixed set of procedures. The README says the package is supplemented with many utilities but does not enumerate an exhaustive method list, and anything outside that set is not available. A specialised resampling scheme or a non-standard test has to be built from the parts, which is more work than writing it directly.
The third is the 1000 permutations default in the example. A thousand replicates gives a p-value resolution of about 0.001, so a result like 0.053 is stable but a result of 0.002 is not. Any real analysis with infer needs a far larger number of replicates, and that is a runtime cost the pipeline makes easy to forget because the default looks small.
The fourth is that this is inference by simulation, not by closed form. A permutation test on a large dataset with many replicates is slower than the analytic equivalent, and the result carries a simulation error that the closed form does not. That is a good trade for teaching and for cases where the analytic assumption is questionable, and a poor one for a routine report with a large dataset.
The fifth is the ecosystem boundary. The package is for R and it assumes the tidyverse. A team whose statistical work happens in Python or Julia gets nothing from it except the design idea, which is worth having but is not a dependency.
How infer compares with base R tests and with boot package
The comparison with base R is the direct one, and the difference in approach is philosophical. A base R test function encapsulates a decision: it takes data and a formula and returns a list containing a statistic, a p-value and a confidence interval. The number of assumptions baked in is not visible at the call site, and the null distribution is not an object you can plot.
infer inverts that. The null distribution is the primary output, and the p-value is derived from it, which means the p-value is a choice you make rather than a value you receive. The direction argument in get_p_value, greater here, is exactly that choice, made explicit.
The comparison with the boot package is the closest one in mechanism, since boot is also a general resampling framework, but the difference in scope is that boot gives you the resampling machinery and expects you to write the statistic and the inference function yourself, while infer ships with the common procedures already implemented. That is the trade: less flexibility, less to write, and a uniform interface across the procedures it does implement.
The third comparison is with a hypothesis testing package that computes exact or asymptotic p-values analytically, and the difference in approach is assumption handling. A closed form test needs the distributional assumption to be right, and the failure mode when it is wrong is a wrong number with no warning. A permutation test makes no such assumption, at the cost of simulation error and compute.
The fourth comparison is with doing it by hand, and for a teaching context the two are the same thing. Writing the loop that permutes the data, computes the statistic and counts the tail is what the package does, and running the package on the same problem is a way of seeing the code that would have produced the answer.
Licence, project structure and what to check before you use it
The licence field on the repository records a custom licence that GitHub cannot classify, and the README points at the LICENSE file for the terms, with a LICENSE.md also present. For a package in the tidymodels organisation this is worth reading rather than assuming, since the tidymodels packages have not all used the same terms.
The project is not archived and the last push was on 2026-04-13, so it is being maintained. The release history shows v1.1.0 from 2025-12-18, so the current line is 1.x, and the package is on CRAN with the standard CRAN status badge.
The repository layout is the tidyverse layout. The R/ directory holds the code, man/ and man-roxygen/ the documentation, tests/ the test suite, vignettes/ the long-form articles including the infer vignette and the observed_stat_examples vignette the README's examples come from, data/ and data-raw/ the bundled datasets, inst/ the installed assets, pkgdown/ the site configuration and figs/ the figures. There is an Rproj file, an air.toml formatting configuration, a codecov.yml and a .vscode directory.
Four things to check before you rely on it.
Which procedure you need is actually implemented, since the package covers a set of inference procedures rather than all of them, and the same set applies to both the formula and non-formula interfaces.
How many replicates you will use. The example uses 1000, and a p-value near the threshold needs more.
Which direction to use when you get the p-value, since the argument is explicit here and the observed statistic versus the null distribution is visible in the plot, so the choice can be made with the distribution in view rather than by reflex.
And whether you need the developmental version, because the CRAN release and the pkgdown site at infer.tidymodels.org may differ, and the latter is where the newest documentation lives.
Editorial conclusion
infer suits an R user who wants to see and to teach the mechanics of a randomization test rather than call a black box test function, and who thinks in the pipe syntax the rest of the tidyverse uses. It is the wrong tool for a user who wants one function that returns a p-value with no intermediate steps, since the four-verb pipeline is more code for the same result in the simple cases, and the wrong choice if you need a test the package does not implement. Before adopting it, read the infer vignette for the design principles, and note that the package gives you the null distribution rather than only the p-value, which is the thing worth learning from it.
Frequently asked questions
What is the infer R package?
It is an R package for statistical inference using an expressive statistical grammar that coheres with the tidyverse design framework. The package is centered around four verbs, specify(), hypothesize(), generate() and calculate(), supplemented with utilities to visualize and extract value from their outputs.
How do I install infer?
The stable version installs from CRAN with `install.packages("infer")`. The developmental version is installed with `pak::pak("tidymodels/infer")`, after installing pak, and its pkgdown documentation is at infer.tidymodels.org.
What is the difference between the formula and non-formula interfaces in infer?
The formula interface uses `age ~ partyid` and the non-formula one uses `response = age, explanatory = partyid`, and both work for all implemented inference procedures. The README says to use whichever is more natural, but recommends moving to the formula notation as soon as possible if you will be doing modelling with lm() and glm().
What p-value does the README's infer example produce?
The worked example computes an F statistic for age by partyid on the gss dataset, generates a null distribution of 1000 permutations under independence, and gets a p-value of 0.053 in the greater direction, which the README visualises by shading the tail of the null distribution beyond the observed statistic.
What data does the infer package ship with?
The gss dataset, a sample of 500 rows from the General Social Survey, with variables including year, age, sex, college, partyid, hompop, hours, income, class, finrela and weight.
What licence does the infer package use?
The repository records a custom licence that GitHub cannot classify, with the terms in the LICENSE file, and a LICENSE.md is also present in the repository root.
Official sources
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.
[](https://hysenlabs.com/projects/tidymodels-infer)Was this useful?