09 · Packages¶
What a package is¶
An R package bundles functions, data, and documentation for reuse — the
same idea as a Python package or an npm module. Base R ships with a useful
core (stats, utils, graphics, etc., all loaded automatically), and
CRAN (the Comprehensive R Archive Network) hosts tens of thousands more
that you install as needed.
Installing a package¶
This downloads and installs the package (and its dependencies) from CRAN. It's a one-time step per machine — you don't re-run it every time you use the package, only when installing for the first time or upgrading.
Loading a package with library()¶
Installing a package makes it available on disk; library() actually loads
it into your current R session so its functions are usable:
library(dplyr)
# Now dplyr's functions are available directly:
starwars |> filter(species == "Human") |> nrow()
A common beginner mistake is calling a package's function without
library() first and getting could not find function "..." — the fix is
almost always a missing library() call at the top of the script.
Checking what's installed¶
installed.packages()[, "Package"] # character vector of all installed package names
"dplyr" %in% installed.packages()[, "Package"]
# [1] TRUE
A common defensive pattern, especially in scripts shared with others:
require() behaves like library() but returns FALSE instead of erroring
if the package isn't installed, which is what makes this "install if missing"
pattern work.
The :: operator — calling a function without loading the package¶
package::function() calls a function directly from a package without a
library() call first. This is useful for a one-off call, for avoiding name
collisions between two loaded packages that both define a function with the
same name, and for making it obvious in code review exactly where a function
comes from.
Package name collisions¶
If two loaded packages export a function with the same name, the more recently loaded one wins by default, and R prints a warning when this happens:
library(dplyr)
library(MASS) # MASS also has a function called "select"
# Attaching package: 'MASS'
# The following object is masked from 'package:dplyr':
# select
select(mtcars, mpg) # now calls MASS::select, not dplyr::select -- surprising!
dplyr::select(mtcars, mpg) # unambiguous -- always calls the intended one
Reading that "masked" warning when it appears (rather than ignoring it) saves real debugging time later.
CRAN Task Views and popular packages¶
CRAN organizes packages into Task Views — curated lists for a domain (e.g. the "TimeSeries" or "MachineLearning" task views). A few packages you'll meet throughout this curriculum:
| Package | Purpose |
|---|---|
dplyr |
Data manipulation (filter, select, mutate, summarize) |
ggplot2 |
Declarative, layered data visualization |
readr |
Fast, predictable file reading |
stringr |
Consistent string manipulation |
lubridate |
Date/time handling |
tidyr |
Reshaping data (pivoting, splitting columns) |
testthat |
Unit testing |
shiny |
Interactive web apps |
Installing all of them individually is tedious; the tidyverse meta-package installs and loads the most common ones together:
install.packages("tidyverse")
library(tidyverse) # loads dplyr, ggplot2, readr, tidyr, stringr, and more at once
Updating and removing packages¶
update.packages() # check for and install updates to everything
remove.packages("somepackage") # uninstall a package
Packages cheat sheet¶
| Task | Command |
|---|---|
| Install | install.packages("pkgname") |
| Load | library(pkgname) |
| Load or error clearly if missing | require(pkgname) |
| Call without loading | pkgname::function_name() |
| List installed | installed.packages()[, "Package"] |
| Update all | update.packages() |
| Remove | remove.packages("pkgname") |
How It Actually Works¶
library(pkg) doesn't "import" symbols the way Python does — it takes the
package's already-compiled namespace environment (built once when the
package was installed, via R CMD INSTALL, from its NAMESPACE file and
R/ source) and attaches it to R's search path, a chain of
environments R walks when resolving a bare name like mutate. Attaching
inserts the package's exported-functions environment right after the
global environment in that chain, which is exactly why a function you
define yourself can "mask" one from a package with the same name — your
global environment is searched first.
Each package actually has two environments: a namespace (everything
defined in the package, exported or not) and an exports environment
(just the public API), linked so that internal functions can see each
other and the package's own private helpers even though users can't.
pkg::fun bypasses the search path entirely and looks fun up directly in
pkg's namespace — which is why it works even for functions the package
chose not to export via :::, and why it never breaks due to masking.
🔀 See this in another language¶
- MATLAB — Basic Numerical Methods
- JavaScript — Modules & npm Basics
- Rust — Modules & Cargo Project Structure
Exercise¶
Write a script that checks whether the readr package is installed using
require(), installs it if missing, then loads it and uses readr::read_csv()
(with the :: syntax, even though it's already loaded, just to practice the
syntax) to read any CSV file from earlier modules. Print sessionInfo() at
the end to see the full list of currently loaded packages.