Lesson 10 / 25

Importing and Cleaning Data

Read CSV, Excel and other files and clean column names and types.

Getting data in

Real analysis starts with importing data. readr's read_csv() reads CSV files quickly into tibbles, guessing column types and reporting them so you can check; specify types explicitly with col_types for reliable pipelines, and handle Indian number formats or other locales with locale(). readxl's read_excel() reads .xlsx and .xls sheets (with sheet and range arguments), haven reads SPSS, Stata and SAS files, jsonlite reads JSON, and DBI with a driver (such as RPostgres or RSQLite) queries databases, with dbplyr translating dplyr code to SQL. arrow reads Parquet files and datasets larger than memory. After importing, clean the data: standardise column names with janitor::clean_names() (snake_case), fix types (parse_number() strips currency symbols and commas), trim whitespace, handle missing values consistently (na = c("", "NA", "-")), and check for duplicates. Save cleaned data in a typed format such as Parquet or .rds rather than CSV to preserve types.

Many sources, one tibble

Different importers bring CSV, Excel, JSON, databases and Parquet into the same tidy table format.

Five source icons on the left with arrows converging into a single table icon on the right.
Figure 4.1 — Importing data from many formats into tibbles.

Importing and cleaning a messy CSV

Explicit types, missing-value markers and clean column names.

library(readr)
library(dplyr)
library(janitor)

sales_raw <- read_csv(
  "data/sales_2026.csv",
  na = c("", "NA", "-"),
  col_types = cols(
    `Order ID` = col_character(),
    `Order Date` = col_date(format = "%d/%m/%Y"),
    `Amount (INR)` = col_character(),
    City = col_character()
  )
)

sales <- sales_raw |>
  clean_names() |>                                   # order_id, order_date, amount_inr, city
  mutate(
    amount_inr = parse_number(amount_inr),           # "Rs 1,200" -> 1200
    city = stringr::str_to_title(stringr::str_trim(city))
  ) |>
  distinct(order_id, .keep_all = TRUE)               # drop duplicate orders

problems(sales_raw)                                   # rows readr could not parse
write_rds(sales, "data/sales_clean.rds")              # keeps column types

Check readr's column report

When read_csv() guesses types, it prints them. A column you expected to be numeric showing as character usually means stray text such as "N/A" or currency symbols that need cleaning.

Quick check: Which function standardises messy column names such as "Order ID" into snake_case?

  • read_csv
  • glimpse
  • janitor::clean_names
  • pivot_longer
Answer

janitor::clean_names — clean_names converts column names to consistent snake_case.