library(tidyverse)4 Data Import
In this part, you will bring data into R and prepare it for analysis. We begin with one CSV file and add new tidyverse tools gradually.
4.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(); - compare package data, URL imports, and local-file imports;
- import a CSV file with a relative path;
- explain the main parts of
glimpse()output; and - identify common issues to investigate after an import.
4.2 Why import data?
So far, we have typed small examples directly into R. In reporting, the data usually come from somewhere else: a government website, an institution, a researcher, or a file supplied by a source. Importing brings that file into R so we can work with 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 a good format for learning data analysis.
Open djr.Rproj and create 02-data-import.Rmd. Download gpfg.csv and save it inside your project’s data folder. Do not open and resave the file in spreadsheet software.
4.3 Tidyverse, readr, and read_csv()
The tidyverse is a collection of packages that work together. One of those packages is readr, which imports rectangular data files. The readr function for a CSV file is read_csv().
Loading the tidyverse also loads readr:
Because readr is one of the core tidyverse packages, you do not also need to run library(readr). The single library(tidyverse) line makes read_csv() and the other core tidyverse tools available. If a project used readr without the rest of the tidyverse, it could load only readr with library(readr), but that is not the workflow used in this book.
The simplest form of the import function is:
read_csv("file_path.csv")The function needs the path to the file. Because a path is text, it goes inside quotation marks.
read_csv(), not read.csv()
Both functions can import a CSV, but they come from different parts of R:
| Function | Where it comes from | What it returns |
|---|---|---|
read_csv() |
readr and the tidyverse | A tibble |
read.csv() |
Base R | A traditional data frame |
This book uses read_csv() for four reasons:
- It returns a tibble, the table format used throughout the tidyverse.
- It reports the column types it detected, giving us immediate information about the import.
- It records values that it could not convert correctly, which we can inspect with
problems(). - Its name and behavior fit consistently with other tidyverse functions used in the book.
read.csv() is valid R code, but it belongs to a different workflow and its name uses a period instead of an underscore. Using one approach consistently makes the course easier to follow. Check the function name carefully: read_csv() is the one used here.
4.4 Package data, URLs, and local files
Data can enter R in several ways. Three common possibilities are package data, a direct URL, and a file stored on your computer.
Data supplied by a package
Some R packages include small datasets for examples and practice. For example, the ggplot2 package includes a table called mpg. It becomes available when the tidyverse is loaded:
library(tidyverse)
mpgPackage datasets are useful for learning because everyone with the same package receives the same data. In journalism, however, the main source is more often a file published by an organization or obtained through reporting.
Data imported from a URL
read_csv() can read a public CSV directly from its web address:
gpfg_online <- read_csv(
"https://raw.githubusercontent.com/binchen19/djr/main/data/gpfg.csv"
)This can be convenient for a quick exploration or an automated update. It also means that R must download the file again whenever the code runs.
Data imported from a local file
For the main course workflow, download the source file once, keep it unchanged inside the Project, and import the local copy. This is safer for a reproducible analysis because:
- a website or URL may move, disappear, or become temporarily unavailable;
- the contents at the same URL may be replaced without warning;
- an internet connection may fail when the document is knitted;
- a website may limit or block repeated downloads; and
- the local file preserves the exact version used for the story.
Record the original URL and the date of download in your notes. A local copy is not a replacement for documenting the source; it is an archived snapshot of what the source provided at that time.
The book mentions package data and URL imports so you can recognize them. The guided lessons use local CSV files stored in data/, which makes the code more reliable and allows the same analysis to run again later.
4.5 Relative paths and RStudio Projects
Our file is stored here inside the learner project:
djr/
├── data/
│ └── gpfg.csv
├── 02-data-import.Rmd
└── djr.Rproj
Starting from the project folder, the relative path is:
data/gpfg.csv
This is why we open the .Rproj file before working. Everyone who opens the same project starts in the same place, so the same relative path works on different computers.
Do not use a path such as /Users/name/Desktop/... or C:/Users/name/Desktop/... in shared code. It points to one person’s computer. Keep the data inside the project and use a relative path.
4.6 Import the CSV
The teaching file comes from the equity-holdings data published by Norges Bank Investment Management. It is a prepared CSV containing one year of reported holdings.
Import it and save the result as gpfg:
gpfg <- read_csv("data/gpfg.csv")Read the line from right to left: read_csv() reads the file, and <- saves the imported table with the name gpfg.
readr may display a message describing the rows, columns, and column types it found. This is information about the import, not an error.
4.7 Inspect the imported data
Never begin an analysis without looking at what was imported.
Look at the structure
glimpse() shows the number of rows and columns, the column names, their data types, and a few example values:
glimpse(gpfg)Rows: 7,201
Columns: 13
$ year <dbl> 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, …
$ report_date <date> 2025-12-31, 2025-12-31, 2025-12-31, 2025-12-31,…
$ region <chr> "Oceania", "Oceania", "Oceania", "Oceania", "Oce…
$ country <chr> "Australia", "Australia", "Australia", "Australi…
$ company <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…
$ source_file <chr> "gpfg-equities-2025-12-31.xlsx", "gpfg-equities-…
$ source_url <chr> "https://www.nbim.no/api/investments/v2/report/?…
Read the output from top to bottom:
Rows: 7,201tells us how many observations were imported.Columns: 13tells us how many variables were imported.- Each line beginning with
$describes one column. - The column name appears after
$, such ascompanyormarket_value_usd. - The short label in angle brackets is the data type. For example,
<chr>means text,<dbl>means a number, and<date>means a date. - The values at the end of the line are examples from that column.
For example, a line beginning $ company <chr> tells us that company is a text column. A line beginning $ market_value_usd <dbl> tells us that market_value_usd is numeric.
glimpse() is a quick overview. It shows only a few example values, so it cannot prove that every value is valid or tell us how many values are missing.
Preview a few rows
slice_head() shows rows from the beginning of a table. The n argument says how many rows to show:
gpfg |>
slice_head(n = 5)The result contains the same columns as gpfg, but only its first five rows. The original gpfg object is not changed.
slice_head() instead of head()?
Both functions can show the first rows of a table, and head(gpfg) is valid R code. This book uses slice_head() because it belongs to dplyr, works naturally in a tidyverse pipeline, and makes the action explicit. In slice_head(n = 5), the named argument n clearly says how many rows to show.
What is a parsing problem?
A CSV stores values as text. During import, read_csv() tries to convert that text into suitable R values, such as numbers or dates. This conversion is called parsing.
Imagine that a column should contain numbers:
amount
120
unknown
95
The value unknown cannot be converted into a number. This is a parsing problem. Depending on the column type, the value may become NA, and readr records where the problem occurred.
After importing a file, use problems() to check whether readr recorded any such values:
problems(gpfg)If problems are found, the result identifies the row and column, what readr expected, and the value it actually found. An empty result means readr did not detect a parsing problem. It does not prove that every value in the source is correct—for example, an incorrect but valid number would not be reported.
4.8 What should you inspect?
Importing a file successfully does not guarantee that the data are ready for analysis. Ask these questions before calculating anything:
| Check | Why it matters |
|---|---|
| Number of rows and columns | An incomplete download or wrong file may have an unexpected size |
| Column names | Names should identify the variables clearly |
| Data types | A numeric column imported as <chr> may contain symbols or unexpected text |
| Missing values | NA means a value is unavailable; it is not the same as zero |
| Unexpected values | Negative amounts, impossible percentages, or unusual spellings may need investigation |
| Repeated rows | A duplicate may be an error, or it may represent a legitimate repeated observation |
| Units and definitions | A number cannot be interpreted correctly without knowing what it measures |
| Parsing problems | A parsing problem means readr could not interpret a value as expected |
At this stage, use glimpse(), slice_head(), problems(), and the data dictionary to form questions about the table. In the next chapter, we will use tidyverse functions to investigate missing, unexpected, and repeated values more systematically.
4.9 Refer to the data dictionary
Before using a column, check what it means and what unit it uses. Open the GPFG data dictionary and keep it as a reference. You do not need to import the dictionary into R for this lesson.
For example, the dictionary explains the difference between market value in Norwegian kroner and US dollars, and it explains that ownership values are percentage points.
One row in the imported file represents one reported equity holding at the end of the year. It does not represent a purchase or sale.
4.10 Practice
In an R Markdown document:
- load the tidyverse;
- import
data/gpfg.csvasgpfg; - inspect it with
glimpse(); - show its first five rows; and
- check for parsing problems.
Then write five short answers:
- How many rows and columns were imported?
- What data types do
companyandmarket_value_usdhave? - What does one row represent?
- Name one important issue that
glimpse()alone cannot fully check. - Give one reason for importing the saved local file instead of downloading it from the URL every time the document runs.
4.11 Takeaways
| Function or idea | What it does |
|---|---|
library(tidyverse) |
Loads the core tidyverse packages |
read_csv("path") |
Imports a CSV file as a tibble |
| Relative path | Locates a file from the RStudio Project folder |
| Local source copy | Preserves the version used in the analysis |
glimpse() |
Shows columns, types, and example values |
slice_head(n = 5) |
Shows the first five rows |
problems() |
Reports values readr could not parse as expected |
The file path is the only argument needed in this lesson. The official read_csv() reference explains optional controls for missing values, column types, delimiters, and unusual files when you need them later.