rguides

What's New in R 4.5 — R 4.5.0 adds the penguins dataset, a new

R 4.5.0, nicknamed “How About a Twenty-Six,” arrived on April 11th, 2025. This update is what’s new for everyday R programmers: from how you load packages to how you install them, the changes target real friction points in daily workflows. If you teach R, write production code, or maintain packages, several additions in this release deserve immediate attention.

TL;DR: R 4.5 brings the penguins dataset as a modern replacement for iris, the use() function for selective package imports (no more namespace conflicts), parallel package downloads that are 2-5× faster, the grepv() convenience function, and C23 compiler support. The use() function is the biggest change for everyday coding.

This article covers the changes that matter most for practical R programming.

The penguins dataset

If you have ever learned R for data science, you have probably worked with the iris dataset. It is classic but showing its age—the measurements come from just three species of iris flowers, collected in the 1930s.

R 4.5 introduces the Palmer Penguins dataset as a base dataset:

# Load the penguins dataset (new in R 4.5)
data(penguins)
head(penguins)

#   species island bill_len_mm bill_dep_mm flipper_len_mm body_mass_g     sex
# 1  Adelie Torgersen       39.1        18.7            181         3750   MALE
# 2  Adelie Torgersen       39.5        17.4            186         3800 FEMALE
# 3  Adelie Torgersen       40.3        18.0            195         3250 FEMALE
# 4  Adelie Torgersen         NA         NA              NA          NA   <NA>
# 5  Adelie Torgersen       36.7        19.3            183         3450 FEMALE
# 6  Adelie Torgersen       39.3        18.9            190         3650   MALE

There is also a penguins_raw version with the original, unreduced data—useful for teaching data cleaning workflows.

This dataset includes measurements from 344 penguins across three species (Adelie, Chinstrap, and Gentoo) from three islands in the Palmer Archipelago. It is a better choice than iris for demonstrating classification and clustering because it has more variation and a cleaner story behind the data.

The use() function

Loading packages in R has always been all-or-nothing. You either use dplyr::filter() to call a function without loading the package, or you run library(dplyr) and get every exported function in your namespace. The latter causes problems when packages have conflicting function names.

R 4.5 introduces use() as a middle ground:

# Only import specific functions from dplyr
use("dplyr", c("filter", "select"))

# Now filter() and select() are available
penguins |>
  filter(species == "Adelie") |>
  select(species:bill_dep_mm)

# But other dplyr functions are not loaded
n_distinct(penguins$species)
# Error: could not find function "n_distinct"

This is similar to what the {box} and {import} packages have offered, but now it is in base R. You get precise control over what enters your namespace, which reduces bugs from unexpected function masking.

The trade-off is that you need to know exactly which functions you want upfront. For quick exploratory work, library() is still convenient. For production code or functions that will be used by others, use() gives you more confidence about what is available.

Parallel package downloads

If you have ever installed many packages at once, you know it can be slow. Historically, install.packages() downloaded and installed packages one at a time.

R 4.5 changes this:

# In R 4.5, packages download in parallel (2-5x faster)
install.packages(c("dplyr", "tidyr", "ggplot2", "purrr", "readr"))

The speedup varies depending on your network and the packages, but typical improvements are 2-5x. This makes setting up a new R environment noticeably faster.

This works automatically—you do not need to change any code. The underlying logic handles dependency resolution correctly even when downloading in parallel.

The grepv() function

Pattern matching in base R uses grep(), which returns indices by default. This makes sense when you need positional information—which rows matched, which column numbers contain a pattern—but it is an extra step when all you want are the matching strings themselves:

comments <- c("Nest never observed", "Some other text", "Nest building")
grep("Nest", comments)
# [1] 1 3

To get the actual matching values instead of indices, you had to remember to add value = TRUE every time. The flag is easy to forget, and forgetting it silently returns integer positions that look like incorrect results in a log or report. Many R users wrote wrapper functions just to avoid this pattern:

grep("Nest", comments, value = TRUE)
# [1] "Nest never observed" "Nest building"

R 4.5 adds grepv() as a convenience function that returns values directly, eliminating the need for the value = TRUE argument entirely. It is a small change, but one that removes a persistent papercut from pattern matching workflows. The function name follows the same convention as other v-suffixed variants in R’s ecosystem:

# grepv returns values directly
grepv("Nest", comments)
# [1] "Nest never observed" "Nest building"

It is a small quality-of-life improvement, but if you use pattern matching frequently, it makes your code cleaner.

C23 compiler support

R 4.5 prefers the C23 standard when compiling packages from source. If you have a C23-capable compiler installed, R will use it. This affects package developers more than everyday users.

Most users will not notice this change. It matters if you compile packages from source and want access to the latest C language features.

What you should do

If you are upgrading to R 4.5:

  1. Try the penguins dataset, If you teach R or write tutorials, consider switching from iris to penguins. It is more interesting and has a better story.

  2. Experiment with use(), For functions that will be used in production or by others, try use() instead of library(). It gives you cleaner namespaces.

  3. Enjoy faster package installs, The parallel downloads happen automatically. You do not need to change anything, but the speed improvement is noticeable when setting up new environments.

  4. Use grepv() when you need values, It is a minor convenience, but code becomes more readable when you want matched strings rather than indices.

The use() function is the biggest change for everyday coding. It addresses a long-standing complaint about R’s package loading model without requiring external packages.

Performance improvements in R 4.5

R 4.5 includes improvements to the JIT compiler and the altrep (alternative representation) framework. The JIT compiler in R 4.x compiles bytecode for functions called frequently, reducing interpretation overhead. The altrep framework allows packages to implement data structures that defer computation, 1:1e9 does not allocate a billion-element vector but instead uses a sequence object that computes values on demand.

The native pipe operator (|>) introduced in R 4.1 and improved in subsequent versions continues to replace the magrittr pipe in most new code. R 4.5 improves the anonymous function shorthand: \(x) x + 1 is the native lambda syntax, equivalent to function(x) x + 1 but more concise. These small syntax improvements reduce the need for magrittr as a dependency in new packages.

Package ecosystem updates

R 4.5 ships with updates to base packages. The tools, utils, and methods packages receive regular maintenance. Package developers benefit from improved R CMD check diagnostics that catch more issues before CRAN submission.

The CRAN submission infrastructure continues to require that packages pass checks on Windows, macOS, and Linux. This requirement has driven better cross-platform testing practices in the community, and the GitHub Actions r-lib/actions CI templates make it straightforward to test on all three platforms automatically on every pull request.

Upgrading

R 4.5 maintains strong backward compatibility with 4.4. Most existing code and packages run without modification. Review the full NEWS file for the complete list of changes before upgrading production environments. Packages that use internal C-level APIs may need recompilation; check your package library with tools::check_packages_in_dir() after upgrading. The performance improvements in the JIT compiler and garbage collector make upgrading worthwhile for compute-intensive workloads.

Performance improvements

R 4.5 continues incremental JIT compiler improvements. The bytecode compiler, enabled by default, reduces execution time for loops and function calls. Garbage collection improvements reduce pause times for long-running analyses. Benchmarking with bench::mark() helps verify whether specific code benefits from the new R version. For code already using vectorized operations and avoiding explicit loops, the JIT improvements have less impact, the gains are most visible in interpreted loop-heavy code.

Incremental progress in a mature language

R 4.5 continues the pattern of incremental improvements in a language that is already mature. The kinds of changes that appear in minor version updates, new syntax for common patterns, performance improvements in core operations, bug fixes, and expanded standard library functions, reflect a language that has found its stable form and is refining instead of reinventing.

For analysts, the most valuable changes in any R version are those that reduce common friction: shorter syntax for operations that were verbose, faster execution for operations that were slow, and clearer error messages for errors that were confusing. Tracking these changes enables adopting improvements as they become available instead of writing verbose code that a newer version would simplify.

Adoption strategy for new R features

The right time to use a new R feature depends on your environment. If you control the runtime, your own machine, a Docker image you maintain, you can adopt features in the R version you run. If your code runs on machines you do not control, packages distributed to users with varied R versions, code submitted to servers with fixed R versions, you must be aware of what R version your users have.

CRAN packages must declare the minimum R version they require in DESCRIPTION. Using a feature from R 4.5 in a package requires declaring Depends: R (>= 4.5). This limits your package’s users to those running R 4.5 or later. For features that provide modest convenience, the restriction may not be worth the exclusion. For features that substantially change what is possible, the restriction is justified.

Testing against new R versions

Testing your code against a new R version before upgrading your production environment catches compatibility issues in a controlled setting. The rhub service provides R-devel testing for packages. For analysis projects, creating a separate renv environment with the new R version and running your analysis pipeline verifies that nothing breaks before committing to the upgrade.

Most R version upgrades are straightforward, the changes are carefully backward-compatible. The upgrades that require attention are those that change default behavior or that deprecate functions that have cleaner replacements. Reading the NEWS file for the version you are upgrading to, or the summaries from R-bloggers or R Weekly, identifies the changes that need attention.

See also

  • c(), The base function for combining values, now works better with the new penguins dataset
  • filter(), dplyr filtering function, now loadable via use()
  • grep() — Pattern matching, see also the new grepv() for value returns