Writing Data to Multiple Formats
Export data frames to CSV, Excel, and RDS with write_csv() and writexl.
Writing Data to Multiple Formats is a free R Academy lesson on CoddyKit — lesson 4 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the R Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
Writing Data from R
Reading data is only half the story — you also need to save results. R supports multiple output formats: CSV for universal compatibility, RDS for preserving R objects exactly, Parquet for high-performance analytics, and more. Choose the format based on your audience and use case.
library(readr)
# Create a sample data frame to write
df <- data.frame(
name = c('Alice','Bob','Carol'),
score = c(85.5, 92.0, 78.3),
grade = c('B','A','C'),
passed = c(TRUE, TRUE, TRUE)
)
print(df)
cat('\nWe will save this in multiple formats!')write_csv() — Save as CSV
write_csv(df, 'file.csv') saves a data frame as a comma-separated file. It does NOT write row names by default (unlike write.csv()). The result is always UTF-8 encoded. It invisibly returns the data frame, so it can be used in pipes.
library(readr)
df <- data.frame(
name = c('Alice','Bob','Carol'),
score = c(85, 92, 78)
)
# Write to /tmp for demonstration
write_csv(df, '/tmp/students.csv')
# Verify by reading back
read_csv('/tmp/students.csv', show_col_types=FALSE)write_csv() Options
write_csv() accepts several options: na='NA' controls how NAs are written, append=TRUE appends to an existing file, col_names=FALSE omits the header, and quote controls when to quote fields.
library(readr)
df <- data.frame(
name = c('Alice','Bob'),
city = c('New York','Los,Angeles'), # Contains comma
score = c(85, NA)
)
# na='' writes NA as empty string (common Excel convention)
write_csv(df, '/tmp/test_write.csv', na='')
cat(paste(read_lines('/tmp/test_write.csv'), collapse='\n'))write_tsv() — Tab-Separated Output
write_tsv(df, 'file.tsv') saves as tab-separated. This is preferred when the data contains commas or when the downstream system expects TSV. Same options as write_csv().
library(readr)
df <- data.frame(
address = c('123 Main St, NYC','456 Oak Ave, LA'),
score = c(85, 92)
)
# TSV avoids delimiter conflict with commas in addresses
write_tsv(df, '/tmp/addresses.tsv')
cat(paste(read_lines('/tmp/addresses.tsv'), collapse='\n'))saveRDS() — Save Any R Object
saveRDS(obj, 'file.rds') serializes any R object to disk in R's native binary format. readRDS('file.rds') restores it exactly — preserving column types, factors, attributes, and even model objects. Not human-readable but perfectly portable between R sessions.
library(readr)
df <- data.frame(
name = c('Alice','Bob'),
score = c(85.5, 92.0),
grade = factor(c('B','A'), levels=c('A','B','C','D'))
)
# Save with full type preservation
saveRDS(df, '/tmp/students.rds')
# Restore exactly
df2 <- readRDS('/tmp/students.rds')
print(df2)
print(class(df2$grade)) # factor — preserved!saveRDS() vs save() — Key Difference
saveRDS(obj, 'file.rds') saves a single object without its name. readRDS() lets you assign it any name. save(obj1, obj2, 'file.RData') saves multiple named objects; load() restores them with original names (can overwrite existing variables).
# saveRDS: single object, flexible on load
model <- lm(mpg ~ wt, data=mtcars)
saveRDS(model, '/tmp/my_model.rds')
best_model <- readRDS('/tmp/my_model.rds') # Any name!
cat('R-squared:', round(summary(best_model)$r.squared, 3), '\n')
# save: multiple named objects, fixed names on load
# save(model, mtcars, file='/tmp/workspace.RData')
# load('/tmp/workspace.RData') # Restores 'model' and 'mtcars'write_delim() — Custom Delimiter
write_delim(df, file, delim='|') writes with any delimiter. Use this when the downstream system expects pipe-delimited or other non-standard formats. The same data quality guarantees as write_csv() apply.
library(readr)
df <- data.frame(
id = 1:3,
name = c('Alice','Bob','Carol'),
value = c(100, 200, 150)
)
# Write pipe-delimited
write_delim(df, '/tmp/data.psv', delim='|')
cat(paste(read_lines('/tmp/data.psv'), collapse='\n'))Excel Output with writexl
The writexl package (companion to readxl) writes data frames to .xlsx format: writexl::write_xlsx(df, 'file.xlsx'). For multiple sheets, pass a named list. No Java or Excel installation required.
# writexl::write_xlsx() — example (library not always available)
# library(writexl)
df1 <- data.frame(name=c('Alice','Bob'), score=c(85,92))
df2 <- data.frame(region=c('East','West'), sales=c(100,200))
# Single sheet:
# write_xlsx(df1, '/tmp/students.xlsx')
# Multiple sheets (named list):
# write_xlsx(
# list(students=df1, sales=df2),
# '/tmp/report.xlsx'
# )
cat('writexl writes pure .xlsx without Java or Excel!')Parquet Files with arrow
Apache Parquet is a columnar storage format ideal for large datasets: it compresses well and reads much faster than CSV for column-oriented queries. The arrow package provides write_parquet() and read_parquet().
# arrow::write_parquet() concept
# library(arrow)
# Large CSV -> Parquet (10-100x smaller, 10-100x faster reads)
# df <- read_csv('big_file.csv')
# write_parquet(df, 'big_file.parquet')
# df_back <- read_parquet('big_file.parquet')
# Parquet benefits:
# - Columnar: only reads columns you need
# - Compressed: ~10x smaller than CSV
# - Typed: no type re-guessing on read
# - Partitioned: split by date/region for parallel reads
cat('Parquet is the modern standard for large-scale analytics in R!')Writing Data in a Pipeline
Both write_csv() and saveRDS() return their input invisibly, making them usable inside a pipe without breaking the flow. Insert a write step mid-pipeline with a checkpoint write_csv() for reproducibility.
library(readr)
library(dplyr)
# Write mid-pipeline as a checkpoint
final_result <- data.frame(
region=c('East','West','North'),
sales=c(100,200,80)
) %>%
mutate(pct = round(100*sales/sum(sales),1)) %>%
{. ->> checkpoint_df; .} %>% # Save checkpoint
filter(sales > 90)
write_csv(checkpoint_df, '/tmp/checkpoint.csv')
print(final_result)Format Selection Guide
Choosing the right output format depends on your goal:
- CSV/TSV: universal, human-readable, anyone can open it
- RDS: R-to-R, preserves all types and attributes exactly
- xlsx: business users, Excel consumers
- Parquet: large data, analytics pipelines, production systems
- JSON: APIs, nested/hierarchical data (
jsonlite::toJSON())
library(readr)
df <- data.frame(
id = 1:5,
value = c(10.5, 20.3, 15.7, 30.1, 25.8)
)
# CSV — universal
write_csv(df, '/tmp/data.csv')
# TSV — when commas are in data
write_tsv(df, '/tmp/data.tsv')
# RDS — R-native, type-preserving
saveRDS(df, '/tmp/data.rds')
cat('Files created:\n')
cat('CSV size:', file.size('/tmp/data.csv'), 'bytes\n')
cat('RDS size:', file.size('/tmp/data.rds'), 'bytes\n')Quick Check
What is the key advantage of saveRDS() over write_csv() for saving R data?
Recap: Writing Data
Key takeaways for writing data from R:
write_csv(df, 'file.csv')— comma-separated, no row names, UTF-8write_tsv(df, 'file.tsv')— tab-separated outputwrite_delim(df, file, delim='|')— any delimitersaveRDS(obj, 'file.rds')— binary R format, preserves all typesreadRDS('file.rds')— restore any name;load()restores original nameswritexl::write_xlsx()— Excel output, no Java neededarrow::write_parquet()— columnar format for large analytical datasets
library(readr)
df <- data.frame(
name = c('Alice','Bob','Carol'),
score = c(85.5, 92.0, 78.3),
grade = factor(c('B','A','C'))
)
# CSV: universal but loses factor
write_csv(df, '/tmp/demo.csv')
df_csv <- read_csv('/tmp/demo.csv', show_col_types=FALSE)
cat('CSV grade type:', class(df_csv$grade), '\n') # character
# RDS: preserves factor
saveRDS(df, '/tmp/demo.rds')
df_rds <- readRDS('/tmp/demo.rds')
cat('RDS grade type:', class(df_rds$grade)) # factorFrequently asked questions
Is the “Writing Data to Multiple Formats” lesson free?
Yes — the full text of “Writing Data to Multiple Formats” is free to read here on the web, and the R Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the R Academy course, upgrade to CoddyKit PRO.
What will I learn in “Writing Data to Multiple Formats”?
Export data frames to CSV, Excel, and RDS with write_csv() and writexl. You practise R Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start R Academy?
No prior experience is required. R Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 4 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Writing Data to Multiple Formats” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this R Academy lesson?
Yes. Every R Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- Reading CSV Files with read_csv()
- Parsing TSV and Fixed-Width Files
- Importing Excel Files with readxl
- Writing Data to Multiple Formats