R: R CSV File I/O

Last updated: 2026-08-26

In the first 10 lessons, we focused on data within R, but in real-world projects, 99% of the data is stored in external files. In this lesson, we’ll learn about the most commonly used data format—CSV files. R offers two ways to read and write CSV files: the standard R method read.csv and the tidyverse method readr, with the latter being smarter and faster.

After completing this lesson, you’ll be able to load 1,000 rows of sales data into R in 5 seconds and generate clean reports.

1. What You'll Learn



2. A Pain Point in Data Import

(1) Pain Point: Excel to R conversion causes the system to freeze

Chen is an analyst, and his manager sent him a 100 MB CSV file containing sales data:

TEXT 📖 Display only
id,date,product,region,amount
1,2024-01-15,Cell Phone,Beijing,2999
2,2024-01-15,Computer,Shanghai,5999
3,2024-01-16,Cell Phone,Guangzhou,2899
...
(100,000 rows of)

He read the data using read.csv(), waited 30 seconds, and it took another 10 seconds to print out 100,000 rows of. He also discovered that all five columns were of type chr (numbers treated as strings).

(2) Solution using R

R
# 1. Use readr Smart Reading(5 s)
library(readr)
sales <- read_csv("sales.csv")

# 2. Automatic Type Inference
# id → integer, date → date, product → character,
# region → character, amount → double

# 3. Read only the rows you need(Speed Up 3x)
sales_sample <- read_csv("sales.csv", n_max = 10000)

# 4. Write clean reports
write_csv(sales_sample, "sales_clean.csv")

1 line of code = 30 seconds → 5 seconds. That’s the power of readr.



3. What is CSV?

(1) CSV = Comma-Separated Values

TEXT 📖 Display only
Name,Age,City
Alice,25,Beijing
Bob,30,Shanghai
Charlie,35,Guangzhou

(2) Why is CSV so widely used in data science?

Advantages Description
General Compatible with Excel, Python, R, and databases
Human-readable Can be viewed directly in Notepad
Small file size 5–10 times smaller than Excel
Cross-platform No garbled characters due to different Excel versions
Easy to work with No Excel formatting or formula noise
100%
graph LR
    A[CSV Documents<br/>name,age,city] --> B[read_csv Smart Reading]
    B --> C[tibble Clean DataFrame]
    C --> D[write_csv Reports]
    C --> E[dplyr Analysis]
    C --> F[ggplot2 Visualization]

    style A fill:#cce5ff
    style B fill:#d4edda
    style C fill:#fff3cd
    style D fill:#f8d7da
    style E fill:#e1d4ff
    style F fill:#ffe1d4


4. Basic R vs. tidyverse: Two Approaches to Working with CSV Files

100%
mindmap
    root((CSV Read/write<br/>Two Options))
        Basics R
            read.csv
            write.csv
            Slow
            String Conversion factor
        tidyverse
            readr
                read_csv
                write_csv
                read_tsv
            Advantages
                Intelligent Type Inference
                Fast 5-10x
                Large File Progress Bar
                Strings are not converted factor
        Selection Recommendations
            New Project: tidyverse
            Legacy code: read.csv + stringsAsFactors = FALSE
            GB Level data: data.table::fread

(1) Comparison of the Two Approaches

Feature Base R read.csv tidyverse read_csv
Speed Slow (R-compatible) 5–10 times faster (C++ backend)
Type Inference Weak (default: chr) Smart Inference (int/dbl/date/lgl)
Progress Bar ✅ (Works well with large files)
String Convert to factor by default Keep as string
Return Type data.frame tibble (more user-friendly)
List Processing Error-Friendly Automatic Cleaning (spaces, special characters)
Missing value NA NA + custom string
String stringsAsFactors = TRUE Always FALSE

(2) Actual Comparison

R
# Basics R(Common Issues with Legacy Code)
df_old <- read.csv("data.csv")
# Default behavior:Convert all string arrays factor(Pitfall!)

# tidyverse(Recommendations)
library(readr)
df_new <- read_csv("data.csv")
# Intelligent Inference Types,Character retains character
💡 Tip: Use readr for all new projects; for legacy code, use read.csv followed by stringsAsFactors = FALSE.

(3) When Should You Use Basic R?

Scenario Recommendation
New Project / Write New Code read_csv (readr)
Processing GB-scale massive datasets data.table::fread (Faster)
Maintaining Legacy Code read.csv and stringsAsFactors = FALSE
Tutorial / Simple Script Either


5. 8 Common Parameters of read_csv()

(1) Basic Syntax

R
read_csv(file, col_types = TRUE, col_names = TRUE, na = "NA",
         skip = 0, n_max = Inf, locale = default_locale(),
         progress = TRUE)

(2) Parameter Quick Reference Table

Parameter Function Default Value
file File path or URL Required
col_types Column Type Specification TRUE (Auto-inferred)
col_names TRUE/Vector (custom column name) TRUE (use the first row)
na Missing Value Marker "NA"
skip Skip the first N lines 0
n_max Maximum number of rows to read Inf (all)
locale Regional Settings (Encoding, Decimal Point) default_locale()
progress Show progress bar TRUE

(3) Hands-On: Common Parameters

R
library(readr)

# 1. Basic Reading(Intelligent Inference Types)
df <- read_csv("data.csv")

# 2. Specify Column Type(Avoiding Fallacies in Reasoning)
df <- read_csv("data.csv", col_types = cols(
  id = col_integer(),
  date = col_date(format = "%Y-%m-%d"),
  amount = col_double(),
  name = col_character()
))

# 3. Skip to the beginning 5 Line Comments
df <- read_csv("data.csv", skip = 5)

# 4. Custom NA Mark(CSV For internal use "NULL" Indicates missing)
df <- read_csv("data.csv", na = c("", "NA", "NULL", "N/A"))

# 5. Read-only before 1000 row(For debugging purposes)
df <- read_csv("data.csv", n_max = 1000)

# 6. Skip Column(Use col_types Set as col_skip())
df <- read_csv("data.csv", col_types = cols(
  temp_col = col_skip()
))

(4) A Detailed Explanation of Smart Type Inference

The biggest advantage of readr is automatic inference:

Sample Data Inferred as
1, 2, 3 integer
1.5, 2.3 double
2024-01-15 date
12:30:00 time
TRUE, FALSE logical
Beijing, Shanghai character
R
# Examples of Inference
sales <- read_csv("sales.csv")
spec(sales)  # View the inference results
# cols(
#   id = col_integer(),
#   date = col_date(format = "%Y-%m-%d"),
#   product = col_character(),
#   region = col_character(),
#   amount = col_double()
# )


6. write_csv(): Output a report

(1) Basic Syntax

R
write_csv(x, file, na = "NA", append = FALSE, col_names = TRUE)

(2) Practical Application

R
# 1. Basic Output
write_csv(sales, "sales_clean.csv")

# 2. Output to the specified path
write_csv(sales, "output/2024_sales.csv")

# 3. Append to the existing file(Do not cover)
write_csv(new_data, "sales.csv", append = TRUE, col_names = FALSE)

# 4. Custom NA indicates
write_csv(df, "output.csv", na = "")

(3) ⚠️ write_csv does not preserve data types

When readr writes to CSV, type information is lost (since CSV is plain text). When reading it back, the types must be inferred again:

R
# Write
write_csv(sales, "sales.csv")

# Read it again
sales_loaded <- read_csv("sales.csv")
# Type by read_csv Re-inference
💡 Tip: Use .rds for internal persistence in R (type preservation)—see Lesson 12.



7. Troubleshooting Common Issues

(1) Garbled Chinese characters

R
# 1. Read Using a Specified Encoding
df <- read_csv("data.csv", locale = locale(encoding = "UTF-8"))
# Or
df <- read_csv("data.csv", locale = locale(encoding = "GBK"))

# 2. Check the file encoding
guess_encoding("data.csv")
# Returns in most cases "UTF-8" or "GBK"

# 3. Specify the encoding when writing
write_csv(df, "output.csv")  # Default UTF-8

(2) The delimiter is not a comma

R
# 1. Tab-separated(TSV)
df <- read_tsv("data.tsv")  # sep = "\t"

# 2. Semicolon-separated(Commonly Used in Europe)
df <- read_csv2("data.csv")  # sep = ";"

# 3. Custom Delimiters(e.g. |)
df <- read_delim("data.txt", delim = "|")

(3) Numbers with thousands separators

R
# European Format:1.234,56(A percentile is .,A decimal is ,)
df <- read_csv("data_eu.csv",
               locale = locale(grouping_mark = ".", decimal_mark = ","))

(4) Slow reading of large files

R
# 1. Read only the required columns(Speed Up 2-3x)
df <- read_csv("big.csv", col_select = c(id, amount))

# 2. Use col_types Skip Column
df <- read_csv("big.csv", col_types = cols(
  temp_col_1 = col_skip(),
  temp_col_2 = col_skip()
))

# 3. Close the progress bar
df <- read_csv("big.csv", progress = FALSE)

# 4. A Faster Solution:data.table::fread
# fread("big.csv") is 5-10x faster than read_csv


8. Advanced: Column Type Specification cols()

(1) Full Syntax of cols()

R
col_types = cols(
  id = col_integer(),       # Integer
  name = col_character(),   # Character
  amount = col_double(),    # Double-precision floating-point
  date = col_date(),        # Date
  time = col_time(),        # Time
  datetime = col_datetime(),# Date and Time
  is_active = col_logical(),# Logic
  skip = col_skip(),        # Skip this column
  guess = col_guess()       # Let readr infer
)

(2) String Shorthand (More Concise)

R
# Use "i" "c" "d" "D" "l" Abbreviation
col_types = cols(
  id = "i",          # integer
  name = "c",        # character
  amount = "d",      # double
  date = "D",        # date
  is_active = "l"    # logical
)

# All Inferences(Default)
col_types = TRUE

# Using a list(Sequence-Mapped Columns)
col_types = list(
  id = col_integer(),
  name = col_character()
)


9. Complete Example: Importing and Analyzing Sales Data

Below is an example of a complete workflow that ties together all the concepts covered in this lesson.

▶ Example: Importing Sales Data + Data Cleaning + Generating Reports

R 📖 Display only
# ============================================
# Importing and Processing Sales Data
# Features:Read CSV → Cleaning → Analysis → Generate Report
# ============================================

library(readr)
library(dplyr)

# 1. Preparing Sample Data
sample_data <- tibble(
  id = 1:10,
  date = as.Date("2024-01-01") + 0:9,
  product = c("Cell Phone", "Computer", "Cell Phone", "Tablet", "Cell Phone",
              "Computer", "Tablet", "Cell Phone", "Computer", "Cell Phone"),
  region = c("Beijing", "Shanghai", "Guangzhou", "Beijing", "Shenzhen",
            "Shanghai", "Guangzhou", "Beijing", "Shenzhen", "Shanghai"),
  amount = c(2999, 5999, 2899, 3999, 3099,
            6299, 3899, 2899, 5999, 3199),
  note = c(NA, "Promo", NA, NA, "New Products", NA, NA, "Promo", NA, NA)
)

# Write Example CSV(UTF-8 Coding)
write_csv(sample_data, "sales_demo.csv")

cat("=== The file has been generated ===\n")

# 2. Read CSV(Intelligent Inference)
sales <- read_csv("sales_demo.csv",
                  na = c("", "NA"),
                  col_types = cols(
                    id = col_integer(),
                    date = col_date(format = "%Y-%m-%d"),
                    product = col_character(),
                    region = col_character(),
                    amount = col_double(),
                    note = col_character()
                  ))

cat("=== Read the results ===\n")
print(sales)

# 3. Data Quality Check
cat("\n=== Data Structures ===\n")
str(sales)

# 4. Data Analysis
cat("\n=== Sales Statistics ===\n")
summary_stats <- sales |>
  group_by(region) |>
  summarise(
    Number of Orders = n(),
    Total Sales = sum(amount),
    Average Order = round(mean(amount), 2),
    Highest Order = max(amount)
  ) |>
  arrange(desc(Total Sales))

print(summary_stats)

# 5. Find promotional orders
cat("\n=== Promotional Orders ===\n")
promo <- sales |> filter(!is.na(note))
print(promo)

# 6. By Product Category
cat("\n=== Product Sales ===\n")
product_stats <- sales |>
  group_by(product) |>
  summarise(Sales = n(), Sales = sum(amount))
print(product_stats)

# 7. Generate clean reports
write_csv(summary_stats, "sales_by_region.csv")
write_csv(product_stats, "sales_by_product.csv")
cat("\n=== The report has been generated ===\n")
cat("  - sales_by_region.csv(By Region)\n")
cat("  - sales_by_product.csv(By Product)\n")

# 8. Use RDS Reserved Types(Recommendations)
saveRDS(sales, "sales_clean.rds")
sales_reloaded <- readRDS("sales_clean.rds")
cat("\nRDS Type After Loading:\n")
print(sapply(sales_reloaded, class))
57 logic lines (exceeds 40-line limit, display only)

Expected Output (Excerpt):

TEXT 📖 Display only
=== Sales Statistics ===
# A tibble: 4 × 5
  region Number of Orders Total Sales Average Order Highest Order
  <chr>   <int>   <dbl>    <dbl>    <dbl>
1 Beijing       3   9897    3299     3999
2 Shanghai       3  15497    5166.    6299
3 Guangzhou       2   6798    3399     3899
4 Shenzhen       2   9098    4549.    5999

=== Promotional Orders ===
# A tibble: 2 × 6
     id date       product region amount note
  <int> <date>     <chr>   <chr>   <dbl> <chr>
1     2 2024-01-02 Computer    Shanghai     5999 Promo
2     8 2024-01-08 Cell Phone    Beijing     2899 Promo

❓ FAQ

Q What should I do if the CSV type is inferred incorrectly?
A Use col_types = cols(...) to explicitly specify the type. After reading the file, use spec(df) to view the inference results, then use type_convert() or re-read the file to correct it.
Q Are Chinese characters displayed as garbled text when reading a CSV file?
A Use locale = locale(encoding = "UTF-8") or "GBK". If you're unsure of the encoding, try guess_encoding() first to check.
Q How do I read a 1 GB CSV file?
A 3 optimization steps: ① col_select Read only the columns you need ② col_types Skip unnecessary columns ③ Disable progress = FALSE. If it’s still slow, use data.table::fread (faster).
Q What should I do if write_csv can't save the data type?
A CSV is a plain text format that doesn't store data types by design. R uses saveRDS() / readRDS() for internal persistence—which preserves data types and is faster (more native to R).
Q How do I choose between read_csv and fread?
A read_csv is the default in the tidyverse and integrates well with dplyr and ggplot2; fread is faster (the preferred choice for GB-scale data), but requires installing the data.table package. For most projects, read_csv is sufficient.

📖 Summary


📝 Exercises

  1. Basic Exercise: Create a tibble containing 5 rows and 4 columns (id/name/score/date). Use write_csv() to write to test.csv, then use read_csv() to read it back, and verify that the type inference is correct (date is of type Date).

  2. Basic Exercise: Read the test.csv from the previous question, use col_types = cols() to explicitly specify the types of all columns (id: integer, score: double, name: string, date: date), and verify that the types match the explicit specifications.

  3. Basic Exercise: Create a data frame containing NA values (using c(1, NA, 3, NA, 5)), export it to CSV, then read it using na = c("NA", ""), and verify that the NA values are handled correctly.

  4. Advanced Exercise: Simulate 100 lines of sales data (product/region/amount/date). After importing the data using read_csv: ① Calculate the total sales by region; ② Identify the 5 orders with the highest amounts; ③ Use read_csv to generate two reports: top5.csv and by_region.csv.

  5. Challenge: Create a CSV file containing special characters (column names with spaces, strings containing commas, and the | separator), read it using read_csv and read_delim respectively, and compare the differences in the results. Take a screenshot of the console output and save it.

Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏