Skip to Content
Course content

29: Writing and Publishing R Packages

Click on the "Edit" button in the top corner of the screen to edit your slide content.

I have a few useful functions; how do I actually turn them into a package?

Look, you could try to create the folder structure manually, but you'll almost certainly miss a directory or misname a file, and then devtools will throw a fit. Don't do that. I always use the usethis package to handle the scaffolding. It's the industry standard for a reason.

Let's say we're building a package called climatesync to help normalize temperature data across different sensors. You'd start like this:

install.packages(c("usethis", "devtools"))
usethis::create_package("path/to/your/climatesync")

This creates the essential skeleton: a DESCRIPTION file (the ID card of your package), a NAMESPACE file (which controls what's exported), and an R/ folder where your actual code lives. Just drop your .R scripts into that R/ folder. I usually recommend keeping one function per file if the package gets large, but for now, just get your logic in there.

My functions rely on other packages like dplyr. How do I make sure the user has those installed?

This is where most beginners trip up. You can't just put library(dplyr) at the top of your script inside a package. If you do, you're forcing the user to load that entire library into their global environment, which is sloppy and can cause naming conflicts.

Instead, you declare dependencies in the DESCRIPTION file. I use a helper function to avoid editing the text file by hand (because one wrong indentation can break the build):

usethis::use_package("dplyr")

This adds dplyr to the Imports section. Then, inside your code, you call functions using the double colon operator. It's explicit and cleaner:

normalize_temp <- function(df) {
  df |> dplyr::mutate(temp_norm = (temp - mean(temp)) / sd(temp))
}

I've seen people use Depends instead of Imports. Avoid that unless you absolutely need the dependency to be attached whenever your package is loaded. Imports is almost always the right choice.

Do I have to write a separate manual for every function?

God, no. Writing a separate PDF or text file is a nightmare to maintain. We use roxygen2. You write your documentation as special comments directly above your function definition, and R converts them into the official .Rd help files you see when you type ?function_name.

Here is how I'd document a function in our climatesync package:

#' Normalize Sensor Temperatures
#'
#' This function takes a data frame and scales the temperature column 
#' to a z-score.
#'
#' @param data A data frame containing a 'temp' column.
#' @return A data frame with an added 'temp_norm' column.
#' @export
#'
#' @examples
#' normalize_temp(data.frame(temp = c(20, 22, 19, 25)))
normalize_temp <- function(data) {
  # ... function code ...
}

That @export tag is the most important part. Without it, the function remains "internal"—meaning it works inside the package, but the user can't call it. Once you've written these, run devtools::document() to generate the actual help files.

Okay, it works on my machine. Now how do I share it with the world?

You have two main paths: CRAN (the official repository) or GitHub. Honestly? Start with GitHub. CRAN has incredibly strict checks that can be frustrating for your first package. GitHub is where most modern R development happens anyway.

Assuming you have a GitHub account and an SSH key set up, usethis makes the publishing process almost trivial:

usethis::use_github()

This command initializes a Git repo, creates the repository on GitHub, and pushes your code. Now, anyone can install your climatesync package using remotes::install_github("your_username/climatesync"). I find this is the best way to get immediate feedback from colleagues before you even think about the bureaucracy of a CRAN submission.




📋 Practical Task

Build and Document the 'climatesync' Package

Your goal is to create a functional R package skeleton that follows professional software engineering standards.

  • Use usethis::create_package() to initialize a package named climatesync.
  • Create a function called calc_temp_diff() that takes two numeric vectors (current and baseline temperatures) and returns the difference.
  • Use usethis::use_package("magrittr") to add a dependency.
  • Write a full roxygen2 documentation block for calc_temp_diff(), including @param, @return, @export, and an @examples section.
  • Run devtools::document() to generate the NAMESPACE and help files.
  • Run devtools::check() and fix any "Warning" or "Error" messages until the package passes the basic R CMD check.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.