Chapter 39 Package Development Basics
What You’ll Learn:
- Creating R packages
- Package structure
- devtools workflow
- Common package errors
- DESCRIPTION and NAMESPACE
Key Errors Covered: 20+ package errors
Difficulty: ⭐⭐⭐ Advanced
39.2 Package Structure
💡 Key Insight: Essential Package Files
mypackage/
├── R/ # R code (.R files)
├── man/ # Documentation (.Rd files)
├── tests/ # Unit tests
│ └── testthat/
├── DESCRIPTION # Package metadata
├── NAMESPACE # Exports and imports
├── LICENSE # License file
├── README.md # Package description
├── NEWS.md # Version history
└── .Rbuildignore # Files to ignore
39.4 DESCRIPTION File
💡 Key Insight: Package Metadata
Package: mypackage
Title: What the Package Does (One Line)
Version: 0.1.0
Authors@R:
person("First", "Last", email = "first.last@example.com",
role = c("aut", "cre"))
Description: What the package does (one paragraph).
License: MIT + file LICENSE
Encoding: UTF-8
LazyData: true
Roxygen: list(markdown = TRUE)
RoxygenNote: 7.2.3
Imports:
dplyr,
ggplot2
Suggests:
testthat (>= 3.0.0)
Key Fields: - Package: Name (letters, numbers, dots only) - Title: Short description (< 65 chars) - Version: Major.Minor.Patch - Authors@R: Package authors - Description: Detailed description - License: Legal terms - Imports: Required packages - Suggests: Optional packages
39.5 Error #1: Invalid Package Name
⭐ BEGINNER 📝 NAME
39.5.1 The Error
# Invalid package names
create_package("my-package") # Hyphens not allowed
create_package("my_package") # OK but not recommended
create_package("123package") # Can't start with number🔴 ERROR
Error: 'my-package' is not a valid package name
39.5.2 Solutions
✅ SOLUTION: Valid Package Names
# ✅ Good names
create_package("mypackage")
create_package("MyPackage")
create_package("my.package") # OK but uncommon
create_package("mypackage2")
# Package name rules:
# - Letters, numbers, and dots only
# - Must start with letter
# - No hyphens or underscores
# - Case sensitive
# - 2+ characters recommended39.6 Adding Functions
🎯 Best Practice: Add Functions with Documentation
# Create new R file
use_r("my_function")
# In R/my_function.R:
#' Calculate Mean with NA Removal
#'
#' @param x A numeric vector
#' @param na.rm Logical; remove NA values?
#'
#' @return The mean of x
#' @export
#'
#' @examples
#' safe_mean(c(1, 2, 3, NA))
#' safe_mean(c(1, 2, 3, NA), na.rm = TRUE)
safe_mean <- function(x, na.rm = FALSE) {
if (!is.numeric(x)) {
stop("x must be numeric")
}
mean(x, na.rm = na.rm)
}
# Generate documentation
devtools::document()39.7 NAMESPACE
💡 Key Insight: Exports and Imports
NAMESPACE controls what functions are exported (visible to users) and what external functions are imported.
# Generated by roxygen2
# Export your functions
export(safe_mean)
export(my_function)
# Import from other packages
importFrom(dplyr, filter)
importFrom(ggplot2, ggplot)
# Import entire package (not recommended)
import(dplyr)Best Practices:
- Use @export in roxygen comments
- Use @importFrom pkg function for imports
- Let roxygen2 manage NAMESPACE
- Never edit NAMESPACE manually
39.9 Dependencies
🎯 Best Practice: Manage Dependencies
# Add required package
use_package("dplyr")
# Adds to DESCRIPTION Imports
# Add suggested package
use_package("ggplot2", type = "Suggests")
# Using imported functions
# Option 1: Import in NAMESPACE
#' @importFrom dplyr filter
#' @export
my_filter <- function(data, condition) {
filter(data, {{ condition }})
}
# Option 2: Use :: (no import needed)
my_summarize <- function(data) {
dplyr::summarize(data, mean = mean(value))
}
# Option 3: Import entire package (not recommended)
#' @import dplyr39.10 Development Workflow
🎯 Best Practice: Iterative Development
# 1. Load package in development
devtools::load_all()
# Simulates installing and loading
# 2. Try functions
safe_mean(c(1, 2, 3, NA), na.rm = TRUE)
# 3. Update documentation
devtools::document()
# 4. Run tests
devtools::test()
# 5. Check package
devtools::check()
# 6. Install locally
devtools::install()
# Keyboard shortcuts in RStudio:
# Ctrl/Cmd + Shift + L - Load all
# Ctrl/Cmd + Shift + D - Document
# Ctrl/Cmd + Shift + T - Test
# Ctrl/Cmd + Shift + E - Check
# Ctrl/Cmd + Shift + B - Build and reload39.11 Error #3: Missing Dependencies
⭐⭐ INTERMEDIATE 📦 DEPENDENCIES
39.12 Package Documentation
🎯 Best Practice: Complete Documentation
# Package-level documentation
use_package_doc()
# In R/mypackage-package.R:
#' mypackage: A Short Title
#'
#' A longer description of what the package does.
#'
#' @docType package
#' @name mypackage
NULL
# Function documentation with roxygen2
#' Calculate Summary Statistics
#'
#' This function calculates mean, median, and standard deviation
#' for a numeric vector.
#'
#' @param x A numeric vector
#' @param na.rm Logical; should NA values be removed?
#' @param digits Integer; number of decimal places
#'
#' @return A named vector with mean, median, and sd
#' @export
#'
#' @examples
#' x <- c(1, 2, 3, 4, 5, NA)
#' summary_stats(x, na.rm = TRUE)
#' summary_stats(x, na.rm = TRUE, digits = 2)
#'
#' @seealso \code{\link{mean}}, \code{\link{median}}, \code{\link{sd}}
summary_stats <- function(x, na.rm = FALSE, digits = 3) {
c(
mean = round(mean(x, na.rm = na.rm), digits),
median = round(median(x, na.rm = na.rm), digits),
sd = round(sd(x, na.rm = na.rm), digits)
)
}39.13 Data in Packages
💡 Key Insight: Including Data
# Create data
my_data <- data.frame(
x = 1:10,
y = rnorm(10)
)
# Add to package
use_data(my_data)
# Creates data/my_data.rda
# Document the data
# In R/data.R:
#' My Example Data
#'
#' A dataset containing example values.
#'
#' @format A data frame with 10 rows and 2 variables:
#' \describe{
#' \item{x}{Integer values from 1 to 10}
#' \item{y}{Random normal values}
#' }
#' @source Generated for package examples
"my_data"
# Use in examples
#' @examples
#' data(my_data)
#' plot(my_data$x, my_data$y)39.14 Summary
Key Takeaways:
- Use devtools/usethis - Modern package development
- Valid package names - Letters, numbers, dots only
- Document everything - roxygen2 comments
- Export functions - Use (export?)
- Declare dependencies - DESCRIPTION and (importFrom?)
- Iterative workflow - load_all, document, test, check
- Include data properly - use_data() and document
Quick Reference:
# Create package
usethis::create_package("mypackage")
# Add function
usethis::use_r("function_name")
# Document
devtools::document()
# Test
devtools::test()
# Check
devtools::check()
# Install
devtools::install()
# Add dependency
usethis::use_package("dplyr")
# Add data
usethis::use_data(my_data)Package Name Rules: - Letters, numbers, and dots only - Start with letter - No hyphens or underscores - Case sensitive - Keep it short and memorable
Best Practices: