library(tidyverse)3 Data Import
The previous chapters used values that we typed ourselves. We will now bring a real table into R, inspect what arrived, and prepare it for analysis. Chapters 3 and 4 follow the same file from import to a clean dataset.
3.1 Learning objectives
By the end of this chapter, you should be able to:
- explain what a CSV file is;
- explain the relationship between the tidyverse, readr, and
read_csv(); - import a local CSV with a relative path;
- read the main parts of
glimpse()output; and - identify clues that a newly imported table needs cleaning.
3.2 From a tibble to an external file
In Chapter 2, we used vectors and tibble() to create a small table ourselves. Journalists usually receive a much larger table from a government agency, company, researcher, or other source. Importing brings that external file into R as an object so we can inspect and analyse it.
We will use a CSV, or comma-separated values file. A CSV stores a table as plain text. It is widely used, easy to share, and can be opened by many programs without depending on one spreadsheet application.
Open djr.Rproj and create 03-data-import.Rmd. Download gpfg_messy.csv and save it inside the project’s data folder. Do not open and resave it in spreadsheet software before importing it.
The file is adapted from the 2025 equity-holdings data published by Norges Bank Investment Management. It contains the published values, with a few deliberately introduced problems for teaching: awkward names, a date stored as text, one incomplete row, and one duplicate. The amounts and percentages remain ordinary numeric values.
3.3 Tidyverse, readr, and read_csv()
The tidyverse is a collection of packages designed to work together. One of those packages is readr, which imports rectangular data files. The readr function for CSV files is read_csv().
Load the tidyverse at the beginning of the R Markdown document:
Loading the tidyverse also loads readr, so we do not need a second library(readr) line. The simplest use of the function is:
read_csv("file_path.csv")The function needs the path to the file. A path is text, so it appears inside quotation marks.
read_csv()
Base R also has a valid function named read.csv(). This book uses readr’s read_csv() because it returns the tibble used throughout the tidyverse and reports the column types it detected. Learning one consistent import workflow is enough for now.
3.4 Use a relative path
The learner project should now look like this:
djr/
├── data/
│ └── gpfg_messy.csv
├── 01-setup.Rmd
├── 02-r-basics.Rmd
├── 03-data-import.Rmd
└── djr.Rproj
Starting from the project folder, the path to the file is:
data/gpfg_messy.csv
This is a relative path: it describes where the file is located inside the project. Everyone who opens djr.Rproj starts from the same project folder, so the same path can work on different computers.
A path such as /Users/name/Desktop/... or C:/Users/name/Desktop/... points to one person’s computer. Keep course files inside the project and use short relative paths.
3.5 Import the CSV
Import the table and assign it to an object named gpfg_raw:
gpfg_raw <- read_csv("data/gpfg_messy.csv")Read the line from right to left: read_csv() reads the file, and <- saves the resulting tibble as gpfg_raw. The suffix _raw reminds us that this object still reflects the imported file; we have not cleaned it.
readr may print a message describing the rows, columns, and column types. That message is information about the import, not an error.
3.6 Inspect what arrived
Importing without an error does not mean the table is ready for analysis. Begin by looking at its structure and a few rows.
Look at the structure
glimpse() shows the number of rows and columns, each column name and type, and a few example values:
glimpse(gpfg_raw)Rows: 7,203
Columns: 11
$ Year <dbl> 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, …
$ `Report Date` <chr> "31/12/2025", "31/12/2025", "31/12/2025", "31/12…
$ region <chr> "Oceania", "Oceania", "Oceania", "Oceania", "Oce…
$ country <chr> "Australia", "Australia", "Australia", "Australi…
$ `Company Name` <chr> "3P Learning Ltd", "ALS Ltd", "AMP Ltd", "ANZ Gr…
$ industry <chr> "Consumer Discretionary", "Industrials", "Financ…
$ `Market Value NOK` <dbl> 34140465, 1044536415, 393367522, 16360856591, 13…
$ market_value_usd <dbl> 3384651, 103554273, 38998054, 1621998602, 131849…
$ voting_pct <dbl> 3.00, 1.39, 1.27, 2.22, 1.68, 1.28, 1.17, 3.23, …
$ ownership_pct <dbl> 3.00, 1.39, 1.27, 2.22, 1.68, 1.28, 1.17, 3.23, …
$ incorporation_country <chr> "Australia", "Australia", "Australia", "Australi…
Read the output from top to bottom:
Rows: 7,203is the number of imported rows;Columns: 11is the number of variables;- each line beginning with
$describes one column; <chr>means character text and<dbl>means a number; and- the values at the end of each line are examples, not the entire column.
Some observations should make us pause:
- names such as
Company NameandMarket Value NOKcontain spaces and punctuation, making them awkward to type in code; Report Dateis character text rather than an R date;- the market-value and percentage columns correctly appear as numbers; and
- missing and duplicate rows will require checks that a quick preview cannot settle by itself.
These are not import failures. They are clues about work needed in Chapter 4.
Preview a few rows
Use the pipe from Chapter 2 to pass the imported table to slice_head():
gpfg_raw |>
slice_head(n = 5)slice_head(n = 5) returns the first five rows without changing gpfg_raw. The named argument n = 5 tells the function how many rows to show.
head()?
head(gpfg_raw) is valid base R. This book uses slice_head() because it is a dplyr function and fits the tidyverse pipelines used in later chapters.
3.7 Make an inspection checklist
After importing any dataset, ask:
| Check | Question to ask |
|---|---|
| Rows and columns | Does the size match what the source describes? |
| Column names | Are the names clear and convenient to use? |
| Column types | Are amounts numeric and dates stored as dates? |
| Missing values | Does NA mean unavailable, and how many are present? |
| Category values | Are spelling, capitalization, and spaces consistent? |
| Repeated records | Is each apparent duplicate an error or a valid observation? |
| Units and definitions | What exactly does every number measure? |
Chapter 4 will turn a small part of this checklist into code. We will rename a few columns, convert the date, inspect a missing row, identify a repeated row, and save the clean result for later chapters.
3.8 Keep the local source copy
read_csv() can also read a public URL, and some packages provide example datasets. Those approaches are useful to recognize, but the guided lessons use a saved local file. A local copy:
- preserves the exact version used in the reporting;
- continues to work if a URL changes or the internet is unavailable; and
- avoids downloading the same file every time the document knits.
Record the publisher, original URL, and access date in your notes. Keeping a local file does not replace source documentation.
3.9 Refer to the data dictionary
Open the GPFG data dictionary and keep it beside the lesson. It explains what each column means and which unit it uses. You do not need to import the dictionary.
One row should represent one reported equity holding at the end of the year. It is not a purchase, sale, investment return, or payment received by a company. We will check that intended row meaning during cleaning.
3.10 Practice
In 03-data-import.Rmd:
- load the tidyverse;
- import
data/gpfg_messy.csvasgpfg_raw; - inspect the table with
glimpse(); - show its first five rows; and
- compare the imported types with the types you think the columns need.
Then answer:
- How many rows and columns were imported?
- Which columns did R correctly import as numbers?
- Which column still needs to become a date?
- Name one awkward column name.
- Why does a successful import not prove that the table is clean?
- What should one row represent?
3.11 Takeaways
| Function or idea | What it does |
|---|---|
library(tidyverse) |
Loads the core tidyverse packages |
read_csv("path") |
Imports a CSV as a tibble |
| Relative path | Locates a file from the RStudio Project folder |
<- |
Saves the imported tibble as an object |
glimpse() |
Shows column names, types, and example values |
slice_head(n = 5) |
Shows the first five rows |
| Local source copy | Preserves the exact file used in the reporting |
The file path is the only read_csv() argument needed in this lesson. The official read_csv() reference explains optional controls for missing values, column types, delimiters, and unusual files when a project requires them.