Skip to main content
v2026.11,858 entries · CC-BY 4.0
LAC HealthMedical & Research SuppliesReagents, PPE & instruments — chain-of-custody documented.Fast, traceable sourcing built for regulated research environments, from bench consumables to instrumentation.Shop lac.us CodeCASRAIlac.us

readxl R Guide: Import Excel Workbooks and Sheets

A practical guide to the readxl R package: importing .xls and .xlsx files, configuring column types, extracting cell ranges, and reading multi-sheet files.

Written and maintained by CASRAI Editorial Board

Last updated

The readxl package in R is the standard tool in the tidyverse for importing tabular data from Excel workbooks directly into R data frames and tibbles without requiring external software installations or Java runtimes. In research environments where experimental data, instrument exports, and administrative rosters are routinely archived in Microsoft Excel formats, readxl provides deterministic control over sheet selection, cell ranges, column type coercion, and missing value representations.

What it is used for in research

In academic and clinical research, tabular datasets frequently originate in proprietary spreadsheet formats rather than plain text. Laboratory instruments, clinical trial management systems, electronic lab notebooks, and inter-institutional collaborators regularly exchange datasets formatted as legacy binary Excel files (.xls) or OpenXML spreadsheets (.xlsx). The readxl package is designed to bridge these spreadsheet files into reproducible analytical pipelines in R.

Unlike general-purpose spreadsheet software that permits mixed data types, arbitrary annotations, and embedded charts within a single sheet, downstream statistical workflows—such as linear regression via modeling in R or factorial ANOVA through afex—require rectangular, well-typed data frames. readxl extracts rectangular tables into tidyverse tibbles while offering fine-grained control over column type guessing, date serial number decoding, and cell coordinate boundaries.

Maintained by Jennifer Bryan at Posit under an MIT open-source license, readxl is built on embedded C and C++ parsing libraries: libxls for binary .xls files (Excel 97–2004) and RapidXML for modern .xlsx files. Because these libraries are compiled directly into the R package binary, researchers can ingest spreadsheets without installing Microsoft Office or configuring Java dependencies on their local workstations or high-performance compute clusters. For broader data ingestion across multiple software formats, researchers often combine readxl with other file readers described in the guide on how to load data into R.

Install or access (official source only)

The readxl package is distributed via the Comprehensive R Archive Network (CRAN) and can be installed directly from the official R console:

install.packages("readxl")

Although readxl is part of the tidyverse ecosystem, it is not a core tidyverse package loaded automatically by library(tidyverse). To use its functions in an R script or analysis notebook, load it explicitly:

library(readxl)

The package includes built-in sample spreadsheets that researchers can use to test import pipelines before pointing code at experimental files. The helper function readxl_example() locates these bundled files on your local filesystem:

# List all bundled sample spreadsheets
readxl_example()

# Get the path to a specific example workbook
datasets_path <- readxl_example("datasets.xlsx")

Step-by-step task (numbered, with menu names or commands exactly as in the current docs)

The following five-step workflow demonstrates how to inspect an Excel workbook, read target sheets, restrict cell boundaries, define column data types, and batch-import multiple sheets into a unified dataset.

1. Inspect available sheets in the workbook

Before reading data, determine the names and order of the sheets contained in the workbook using excel_sheets():

library(readxl)

# Path to the target workbook
workbook_file <- "study_results.xlsx"

# Retrieve sheet names as a character vector
sheet_names <- excel_sheets(workbook_file)
print(sheet_names)
# Example output: [1] "Metadata" "Cohort_A" "Cohort_B" "Summary"

2. Read a specific worksheet

By default, read_excel() imports the first sheet in the workbook. To target an experimental cohort or specific analysis tab, specify the sheet by its exact string name or its 1-based integer index:

# Read by sheet name
cohort_a <- read_excel(workbook_file, sheet = "Cohort_A")

# Read by sheet index (second sheet)
cohort_b <- read_excel(workbook_file, sheet = 2)

read_excel() automatically identifies whether the file is binary .xls or OpenXML .xlsx by inspecting the file signature. If you prefer explicit function calls, you can call read_xls() or read_xlsx() directly.

3. Restrict import to a specific cell rectangle

Research spreadsheets often contain introductory title banners, protocol notes, or summary blocks that violate strict tabular layout. Use the range argument to isolate the rectangular data matrix:

# Using standard Excel A1 notation (with or without sheet prefix)
subset_data <- read_excel(
  workbook_file,
  sheet = "Cohort_A",
  range = "B5:G120"
)

For programmatic range selection, readxl exports helper functions from the cellranger package:

# Read rows 5 through 50 across all columns
rows_subset <- read_excel(workbook_file, range = cell_rows(5:50))

# Read columns B through E across all populated rows
cols_subset <- read_excel(workbook_file, range = cell_cols("B:E"))

# Specify upper-left and lower-right coordinates: c(row, col)
bounded_data <- read_excel(
  workbook_file,
  range = cell_limits(ul = c(5, 2), lr = c(120, 7))
)

Specifying range takes precedence over the skip and n_max arguments.

4. Enforce column types and configure missing values

Excel allows arbitrary data types within the same column. To prevent unexpected type conversions, specify column types explicitly using the col_types argument. Supported types are "skip", "guess", "logical", "numeric", "date", "text", and "list":

# Explicitly declare types for each column
cleaned_data <- read_excel(
  workbook_file,
  sheet = "Cohort_A",
  col_types = c("text", "text", "numeric", "date", "logical", "skip"),
  na = c("", "NA", "N/A", "ND", "-999")
)

If you have a sheet where all columns should be imported as strings (for instance, to preserve leading zeros in subject identification numbers), pass a single string: col_types = "text".

5. Batch-import multiple sheets iteratively

When data across experimental sites or treatment cohorts is split across multiple sheets with identical column headers, use purrr::map() and purrr::list_rbind() to combine them into a single longitudinal tibble:

library(readxl)
library(purrr)

# Identify data sheets, excluding metadata
all_sheets <- excel_sheets(workbook_file)
data_sheets <- all_sheets[all_sheets != "Metadata"]
names(data_sheets) <- data_sheets

# Iterate, read each sheet, and bind rows with a cohort indicator column
combined_study <- map(
  data_sheets,
  ~ read_excel(workbook_file, sheet = .x, guess_max = 5000)
) |>
  list_rbind(names_to = "cohort")

Output and how to read it

The primary return object of read_excel() is a tibble (class tbl_df, tbl, data.frame). When printed in the R console, the tibble displays the total dimensions (rows × columns) and annotates each column header with its inferred or specified data type:

  • <chr> (character): Text values and categoricals.
  • <dbl> (double/numeric): Continuous floating-point and integer measurements.
  • <dttm> (POSIXct date-time): Calendar dates and timestamps.
  • <lgl> (logical): Boolean TRUE or FALSE values.
  • <list>: Complex list columns holding heterogeneous cell types when col_types = "list" is specified.

Understanding how readxl parses dates is critical for research reproducibility. Excel does not store dates as ISO 8601 text strings; instead, it stores them as floating-point serial numbers representing elapsed days from an origin date. Excel supports two distinct date systems:

  • 1900 date system (default on Windows and modern Excel): Starts on January 1, 1900. Because Microsoft Excel intentionally preserved an intentional bug from Lotus 1-2-3 that treated 1900 as a leap year, the effective underlying calculation epoch is December 30, 1899.
  • 1904 date system (historical default on macOS Excel): Starts on January 1, 1904, and does not contain the leap year bug. Dates in this system are exactly 1,462 days ahead of the 1900 system.

readxl inspects the internal workbook metadata file (such as workbook.xml in .xlsx archives) to determine whether the workbook utilizes the 1900 or 1904 system, automatically translating serial numbers to the correct R POSIXct date-times without requiring manual origin offsets. For research data repositories requiring long-term archiving, validating these structures aligns with standard data curation tools and institutional preservation workflows.

Troubleshooting

When working with complex research workbooks, users commonly encounter the following parsing issues:

  • Subject IDs losing leading zeros: Excel frequently classifies numeric identifiers (such as medical record numbers or ZIP codes) as numbers, stripping leading zeros. Supplying col_types = "text" or setting that specific column to "text" in col_types preserves the string representation intact.
  • Numeric columns converting to NA with warning messages: By default, readxl scans only the first 1,000 rows (guess_max = 1000) to infer column data types. If a column contains numbers for the first 1,000 rows followed by text annotations (such as “below detection limit” or “<LOD”), readxl guesses numeric, causing later text entries to be coerced to NA with a warning. Resolve this by increasing the guessing threshold (guess_max = Inf) or explicitly designating the column as col_types = "text".
  • Custom missing value codes: Field surveys and electronic lab systems often encode missing values using domain codes such as -999, "ND" (not detected), or "NULL". Add all expected codes to the missing value vector: na = c("", "NA", "ND", "-999").
  • Password-encrypted .xlsx workbooks: readxl includes the embedded tiny-AES-c library, permitting decryption of password-protected OpenXML files directly via the password argument: read_excel("secure_data.xlsx", password = "study_password").
  • Accented and non-ASCII character corruption: readxl decodes characters in UTF-8. If non-ASCII characters appear corrupted, confirm that the originating spreadsheet was saved with standard UTF-8 encoding in modern Excel rather than an obsolete regional code page.

Free alternatives (table)

The following table compares open-source R packages for managing Excel spreadsheet workflows in research, noting system dependencies and functional scope:

Package Read Support Write Support System Dependencies Primary Strengths and Trade-offs
readxl .xls, .xlsx No None (embedded C/C++) Fastest reading performance; zero external dependencies; outputs native tidyverse tibbles; cannot modify or write Excel files.
openxlsx .xlsx only Yes None (C++ via Rcpp) Comprehensive workbook creation; supports cell styling, formulas, borders, and conditional formatting; does not read legacy .xls.
writexl No .xlsx only None (embedded libxlsxwriter C library) Lightweight and zero-dependency writer; ideal companion to readxl for exporting clean rectangular data frames without Java.
xlsx .xls, .xlsx Yes Java Runtime (rJava / Apache POI) Full-featured read/write capabilities, but requires configuring Java Development Kit (JDK); prone to JVM memory bottlenecks on large sheets.
readODS .ods only Yes None (C++ via Rcpp) Specialized reader and writer for OpenDocument Spreadsheets (.ods) used in LibreOffice and open-source data curation environments.

FAQ

How do I read a specific sheet by name using readxl in R?

Pass the sheet name as a character string to the sheet argument of read_excel(): read_excel("experiment.xlsx", sheet = "Trial_1"). You can also specify the 1-based numerical index of the sheet (for example, sheet = 1).

Why does readxl convert numbers or dates to NA?

This typically occurs when a column contains mixed data types. readxl uses the first 1,000 rows by default to guess the column type. If those rows contain numeric values but subsequent rows contain text notes, readxl assigns the column a numeric type and coerces non-numeric cells to NA. You can resolve this by increasing the row inspection sample with guess_max = Inf or by explicitly setting the column type using col_types = "text".

Does readxl require Java or external system libraries to read Excel files?

No. readxl is completely self-contained. It incorporates embedded C and C++ libraries (libxls for .xls and RapidXML for .xlsx) that compile directly into the package. It does not require Java, Python, or Microsoft Office to be installed on the system.

How can I read all sheets from an Excel workbook at once?

Combine excel_sheets() with lapply() in base R or purrr::map() in the tidyverse. For example: sheets <- excel_sheets("file.xlsx"); data_list <- lapply(sheets, function(s) read_excel("file.xlsx", sheet = s)). This returns a named list of data frames corresponding to each sheet.

Can readxl export or write modified data back to an Excel file?

No. readxl is strictly a file-reading package. To export data from R back into an Excel workbook without installing Java, use the companion package writexl (via writexl::write_xlsx()) or use openxlsx for advanced styling and multi-tab authoring.

Sources

Follow CASRAI

Research-administration guidance, standards updates and independent tool reviews.

Ask CASRAI · free to try

Ask about readxl R Guide: Import Excel Workbooks and Sheets

Ask your first 2 questions free below. Subscribers get 150 a day for $29 a month.

An AI assistant specialized in research administration. It cites the sources behind every answer, labels web answers and says when it can't answer.

Answers draw on CASRAI's guides and dictionary plus the federal and funder documents we index: Federal Register, Grants.gov, Regulations.gov and UKRI.

Works on this site and inside Claude, Cursor and the AI tools you already use.

Everything CASRAI publishes — this page, the dictionary, the guides and the news — stays free to read, with no account and no card.

Referenced across the research world

University of Cambridge logoColumbia University logoCrossref logoUniversity of Edinburgh logoHarvard University logoUniversity of Oxford logoPrinceton University logoStanford School of Medicine logoUniversity College London logoORCID logoUniversity of Cambridge logoColumbia University logoCrossref logoUniversity of Edinburgh logoHarvard University logoUniversity of Oxford logoPrinceton University logoStanford School of Medicine logoUniversity College London logoORCID logo
  • University of Cambridge logo
  • Columbia University logo
  • Crossref logo
  • University of Edinburgh logo
  • Harvard University logo
  • University of Oxford logo
  • Princeton University logo
  • Stanford School of Medicine logo
  • University College London logo
  • ORCID logo

View CASRAI adoption →