# install tidyverse packages
install.packages("tidyverse")
# additionally install packages to handle XLS(X) files
install.packages(c("readxl", "writexl"))
Reading-In and Writing-Out Data
Introduction
In this lecture, we will learn different ways how to read data from the disk into R’s memory, and how to write data out from R back to the disk. Along the way, we’ll learn a little about basic data object types in R, and how to examine created objects.
This handout covers the following topics:
- Read in tables from text files (.csv / .tab / .tsv files)
- Data file formats
- R object (vector) types
- Write out tables to text files (.csv / .tab / .tsv files)
- Read in tables from Excel files (.xls / .xlsx files)
- Saving/Loading Objects (.rdata / .rds files)
Set up
R Packages
We can extend the basic functionality of R
by installing additional packages. These packages can be found either in several repositories. The main repositories are the official R package repository CRAN Bioconductor. The source code of many packages can be found in GitHub.
If you want to use extra package, it has to be:
- installed first (one time action)
- and loaded before use (at the beginning of each session).
We are going to use tidyverse
framework, which is a collection of several packages for data analysis with new modern concept.
We are also going to use additional two packages, which provide functions for reading-in (readxl
) and writing out XLS(X) (writexl
) files.
All these packages can be installed via “Packages” tab of the GUI Menu:
Packages can be also installed by using “Console” and by typing in the following code:
Loading the packages
After successful installation we can load them to our R session.
library(tidyverse)
── Attaching core tidyverse packages ─────────────────── tidyverse 2.0.0 ──
✔ dplyr 1.1.4 ✔ readr 2.1.5
✔ forcats 1.0.0 ✔ stringr 1.5.1
✔ ggplot2 3.5.1 ✔ tibble 3.2.1
✔ lubridate 1.9.3 ✔ tidyr 1.3.1
✔ purrr 1.0.2
── Conflicts ───────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag() masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(readxl)
library(writexl)
Data files
Before we can read data files into R, we need to download some example files from the course webpage. These files are provided under “Code, Datasets” for each lecture.
Once you have downloaded the file, move it to somewhere organised, e.g. using the following steps:
- Go to your
working_directory
for this course. - Create a new sub-directory and call it
data
. - Move the newly downloaded files inside the
data
directory.
Alternatively we can also download the file via R in the “Console”:
# first we need to create a `data` directory
dir.create("data")
# then we can download the file
download.file(url = "https://ivanek.github.io/introductionToR/data/penguins.csv",
destfile = "data/penguins.csv")
Reminders from Session 1
First, a few reminders of useful functions/operators seen in Session 1:
# Assignment of some values to an object
<- 1:5
x
# Function to list objects in your workspace
# see also RStudio's Environment pane.
ls()
# Function to remove object x from workspace
rm(x)
# How to get help
?ls
?rm
# "Where am I?"
getwd()
# Changing working directory
setwd("/take/me/to/this/directory/")
Data File formats
There are plenty of file formats for storing data that can be read into R, or written out from it. These can vary in many ways:
- Plain text, e.g.
.csv
. - More advanced text files, e.g.
.xml
. - Compressed, e.g.
.zip
or.gz
. - Open or Proprietary formats, e.g.
.hdf5
and.xlsx
, respectively. - Proprietary statistics formats, e.g.
.dta
for STATA,.sav
for SPSS. - R formats, e.g.
.rds
and.rdata
.
We will focus on the two commonly found formats in biomedical research, i.e. mostly plain text format .csv
, Excel files .xls
and .xlsx
, and of course on the R formats.
Read in tables from text files (.csv / .tab / .tsv files)
Using the GUI interface of RStudio
In case of these plain text files, we mostly assume that they contain data in a tabular format, where individual columns are separated by a delimiter (,
, ;
, or \t
)
We can navigate to the file data/penguins.csv
in the “File” tab of the RStudio. We can directly open the file (“View”) or load it into the current R
session by selecting “Import Dataset…” option in the context menu.
This will open new window where we can set few parameters for the import. On the bottom-right side we can see the code which will be executed once we press “Import” button. Let’s copy it to the script, which would allows us to reproduce our steps in the future.
We can now also explore the different options for the import. Try to change and check how this is reflected in the code:
- “First Row as Names”
- “Open Data Viewer”
- “Delimiter”
- “NA” values
Exercise 1
Choose the best options to import the dataset.
<- read_csv("data/penguins.csv") penguins
Exercise 2
Create a script and save all code used so far into it.
The new script can be opened via menu “File” -> “New File” -> “R Script”. Save it into your working directory under the name: ‘lecture_02.R’. Do not forget to include the loading of the libraries. Optionally, add there also code block, which was used to download the file.
library(tidyverse)
library(readxl)
library(writexl)
library(readr)
# first we need to create a `data` directory
dir.create("data")
# then we can download the file
download.file(url = "https://ivanek.github.io/introductionToR/data/penguins.csv",
destfile = "data/penguins.csv")
<- read_csv("data/penguins.csv") penguins
Explore the resulting object (GUI)
We have loaded the file into variable penguins
. Let’s view it using the “Environment tab”:
This dataset is coming from the R package palmerpenguins. It was specifically designed for data exploration and visualization. There are three different species of penguins in this dataset, collected from three islands in the Palmer Archipelago, Antarctica. Description of individual columns can be found in the help page of the dataset penguins.
Exercise 3
In order to be able to access the help page, we need to install the package first. Install the package palmerpenguins
and find out what are the individual columns in the dataset. Help page can be found in the “Help” tab.
install.packages("palmerpenguins")
library(palmerpenguins)
?penguins
Exploring the object in details
We can also explore the object directly in console by typing in variable name:
penguins
# A tibble: 344 × 8
species island bill_length_mm bill_depth_mm flipper_length_mm
<chr> <chr> <dbl> <dbl> <dbl>
1 Adelie Torgersen 39.1 18.7 181
2 Adelie Torgersen 39.5 17.4 186
3 Adelie Torgersen 40.3 18 195
4 Adelie Torgersen NA NA NA
5 Adelie Torgersen 36.7 19.3 193
6 Adelie Torgersen 39.3 20.6 190
7 Adelie Torgersen 38.9 17.8 181
8 Adelie Torgersen 39.2 19.6 195
9 Adelie Torgersen 34.1 18.1 193
10 Adelie Torgersen 42 20.2 190
# ℹ 334 more rows
# ℹ 3 more variables: body_mass_g <dbl>, sex <chr>, year <dbl>
We can see, that in contrary to the viewer, only part of the dataset was printed on scree. At the same time, in the header we can read: # A tibble: 344 × 8
. The tibble
is a modern alternative to original data.frame
. The word is coming from Australian spelling of table.
To some extent, it is similar to “Excel” sheet.
tibble
- structure to store tabular data
- it is always rectangular, each column has the same number of rows.
- every column (named) can store only one data type, exception is column with
list
- there are no row names, only index
- it has a special
print
method, which by default shows only first 10 rows and only columns which fit to the screen - there is also a main difference in sub-setting, but this is the topic of the next lecture
tibble
to data.frame
data.frame
- part of base
R
, still the most frequently used object type - similar to
tibble
also stores tabular data, each column can contain only a single data type - can have row names
- there is no default print method, the entire table is printed on the screen!
- supports arithmetic operations on all columns at once
- suffers from inconsistent behaviors (subsetting, value recycling while creating)
One can easily convert between the tibble
and the data.frame
:
as_tibble()
as.data.frame()
Individual Columns of the tibble
We could see that the default print method also shows short description under each column name (<chr>
, <dbl>
). The function read_csv
tried to guess the appropriate format for each of them. Typically a column contain a vector with values.
tibble
column can contain also more complicated structures like list
. And the list can contain anything, so potentially it can be a list of tibbles.
For the simple vectors, there are:
- character: to store string data
<- c("Gentoo", "Adelie", "Gentoo", "Chinstrap", "Adelie")
species class(species)
[1] "character"
is.character(species)
[1] TRUE
- double or numeric: to store floating point numbers
<- c(44.5, 38.6, 45.3, 52.8, 37.3)
bill_length_mm class(bill_length_mm) # numeric is the name of the type, implicit class
[1] "numeric"
is.numeric(bill_length_mm)
[1] TRUE
is.double(bill_length_mm) # double is the name of type
[1] TRUE
- integer: to store whole numbers
<- c(4100, 3800, 4300, 4550, 3775)
body_mass_g class(body_mass_g) # numeric!
[1] "numeric"
# for integers we need to specifically ask for that representation
<- as.integer(body_mass_g)
body_mass_g class(body_mass_g)
[1] "integer"
is.integer(body_mass_g)
[1] TRUE
# or directly while creating it, by adding `L` to each value
<- c(4100L, 3800L, 4300L, 4550L, 3775L)
body_mass_g class(body_mass_g)
[1] "integer"
is.integer(body_mass_g)
[1] TRUE
- logical: only
TRUE
orFALSE
values
<- c(TRUE, TRUE, FALSE, TRUE, FALSE)
rare class(rare)
[1] "logical"
is.logical(rare)
[1] TRUE
- factor: similar to character but uses different representation of the data. Typically used with few categories (sex, education, …). There is a collection of unique labels (“male”, “female”) called
levels
and the vector of indices pointing to the appropriate level. This was the object makes much smaller footprint in the memory.
<- c("male", "female", "female", "female", "female")
sex <- factor(sex, levels=c("female", "male"))
sex class(sex)
[1] "factor"
is.factor(sex)
[1] TRUE
sex
[1] male female female female female
Levels: female male
# levels
levels(sex)
[1] "female" "male"
# individual values
as.integer(sex) # in fact there are only integers pointing to the individual level
[1] 2 1 1 1 1
# replace all values with `f` and `m`
# we can only replace the levels -> the individual values are just integers == indices!
levels(sex) <- c("f", "m")
- various representations for date and times
For details see the package lubridate
.
library(lubridate)
<- c("2007-06-01", "2009-04-15", "2009-07-20", "2009-09-16", "2009-12-24")
collected <- ymd(collected)
collected class(collected)
[1] "Date"
# special functions to extract specific information
year(collected)
[1] 2007 2009 2009 2009 2009
month(collected)
[1] 6 4 7 9 12
day(collected)
[1] 1 15 20 16 24
Functions to explore the objects
Often we are interested in general properties of the object, like length, size, dimensions, unique values and maybe a general overview of the structure. Here are the most commonly used functions to explore the properties of a vector
:
# length of a vector
length(species)
[1] 5
# unique values
unique(species)
[1] "Gentoo" "Adelie" "Chinstrap"
# general structure
str(species)
chr [1:5] "Gentoo" "Adelie" "Gentoo" "Chinstrap" "Adelie"
str(sex)
Factor w/ 2 levels "f","m": 2 1 1 1 1
# summary
summary(species)
Length Class Mode
5 character character
summary(bill_length_mm)
Min. 1st Qu. Median Mean 3rd Qu. Max.
37.3 38.6 44.5 43.7 45.3 52.8
summary(sex)
f m
4 1
For two dimensional objects (with rows and columns), we can use additionally:
# dimensions
dim(penguins)
[1] 344 8
# number of rows
nrow(penguins)
[1] 344
# number of columns
ncol(penguins)
[1] 8
# general structure
str(penguins)
spc_tbl_ [344 × 8] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
$ species : chr [1:344] "Adelie" "Adelie" "Adelie" "Adelie" ...
$ island : chr [1:344] "Torgersen" "Torgersen" "Torgersen" "Torgersen" ...
$ bill_length_mm : num [1:344] 39.1 39.5 40.3 NA 36.7 39.3 38.9 39.2 34.1 42 ...
$ bill_depth_mm : num [1:344] 18.7 17.4 18 NA 19.3 20.6 17.8 19.6 18.1 20.2 ...
$ flipper_length_mm: num [1:344] 181 186 195 NA 193 190 181 195 193 190 ...
$ body_mass_g : num [1:344] 3750 3800 3250 NA 3450 ...
$ sex : chr [1:344] "male" "female" "female" NA ...
$ year : num [1:344] 2007 2007 2007 2007 2007 ...
- attr(*, "spec")=
.. cols(
.. species = col_character(),
.. island = col_character(),
.. bill_length_mm = col_double(),
.. bill_depth_mm = col_double(),
.. flipper_length_mm = col_double(),
.. body_mass_g = col_double(),
.. sex = col_character(),
.. year = col_double()
.. )
- attr(*, "problems")=<externalptr>
# summary of each column
summary(penguins)
species island bill_length_mm bill_depth_mm
Length:344 Length:344 Min. :32.10 Min. :13.10
Class :character Class :character 1st Qu.:39.23 1st Qu.:15.60
Mode :character Mode :character Median :44.45 Median :17.30
Mean :43.92 Mean :17.15
3rd Qu.:48.50 3rd Qu.:18.70
Max. :59.60 Max. :21.50
NA's :2 NA's :2
flipper_length_mm body_mass_g sex year
Min. :172.0 Min. :2700 Length:344 Min. :2007
1st Qu.:190.0 1st Qu.:3550 Class :character 1st Qu.:2007
Median :197.0 Median :4050 Mode :character Median :2008
Mean :200.9 Mean :4202 Mean :2008
3rd Qu.:213.0 3rd Qu.:4750 3rd Qu.:2009
Max. :231.0 Max. :6300 Max. :2009
NA's :2 NA's :2
There are also special functions to display the first or last few elements/rows of an object:
head(penguins)
# A tibble: 6 × 8
species island bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
<chr> <chr> <dbl> <dbl> <dbl> <dbl>
1 Adelie Torge… 39.1 18.7 181 3750
2 Adelie Torge… 39.5 17.4 186 3800
3 Adelie Torge… 40.3 18 195 3250
4 Adelie Torge… NA NA NA NA
5 Adelie Torge… 36.7 19.3 193 3450
6 Adelie Torge… 39.3 20.6 190 3650
# ℹ 2 more variables: sex <chr>, year <dbl>
tail(penguins)
# A tibble: 6 × 8
species island bill_length_mm bill_depth_mm flipper_length_mm body_mass_g
<chr> <chr> <dbl> <dbl> <dbl> <dbl>
1 Chinst… Dream 45.7 17 195 3650
2 Chinst… Dream 55.8 19.8 207 4000
3 Chinst… Dream 43.5 18.1 202 3400
4 Chinst… Dream 49.6 18.2 193 3775
5 Chinst… Dream 50.8 19 210 4100
6 Chinst… Dream 50.2 18.7 198 3775
# ℹ 2 more variables: sex <chr>, year <dbl>
And we can also open a built-in viewer for the data by calling:
View(penguins)
Create a tibble
manually
We can also create tibble
manually in R
. We can specify the individual columns and their values. Let’s create the following table:
species | n | avg_body_mass_g |
---|---|---|
Adelie | 152 | 3701 |
Chinstrap | 68 | 3733 |
Gentoo | 124 | 5076 |
tibble(
species = c("Adelie", "Chinstrap", "Gentoo"),
n = c(152L, 68L, 124L),
avg_body_mass_g = c(3701, 3733, 5076)
)
# A tibble: 3 × 3
species n avg_body_mass_g
<chr> <int> <dbl>
1 Adelie 152 3701
2 Chinstrap 68 3733
3 Gentoo 124 5076
Exercise 4
In the palmerpenguins
there is additional dataset penguins_raw
. Can you create a summary table (tibble
) with names of both datasets (“penguins”, “penguins_raw”) in first column, with number of rows of each dataset in the second column and and third column with number of columns of each dataset?
library(palmerpenguins)
dim(penguins)
dim(penguins_raw)
tibble(dataset=c("penguins", "penguins_raw"),
nrows = c(344, 344),
ncols = c(8, 17))
Writing out data from R session
Writing out the data can be done only via the console. Similarly to reading in, there is a dedicated function write_csv
which can export tabular data (tibble
or data.frame
) to a CSV file.
write_csv(penguins, file="data/penguins_exported.csv")
Exercise 5
Export the penguins
as TSV file and try to import it again into R. TSV stands for tab separated values. Hint: use function apropos("tsv")
to find out which function contain ‘tsv’ as part of their name. You can also try to import it via the GUI.
# write it out
write_tsv(penguins, file="data/penguins.tsv")
# read it in again
<- read_tsv("data/penguins.tsv")
penguins_tsv # or use again the GUI
Exchanging data with Excel tables (.xls / .xlsx files)
Reading from Excel
To read Excel files (i.e. files with .xls or .xlsx extension), there are several R packages available. We recommend installing (if necessary) and using the readxl
package.
The functions in this package read data from one sheet in the Excel file. The data in this sheet should be tabular, with samples in rows, and variables in columns. There can be a header row, but only a single header row is supported. Avoid leaving blank rows/columns before your data table. Cell formatting in Excel (borders, fill, how numbers are rendered) are typically not carried across. Be aware of date conversions!
The read_excel()
function looks at the file name at determines whether it is a .xls or .xlsx file, calling sub-functions read_xls()
or read_xlsx()
. By default, it assumes you want to read the maximal range of data available on the 1st sheet of the file, and that the first row contains a header. (NB: cannot deal with “multi-row headers”) - all these behaviors can be changed with arguments to the function, for details check the help.
library(readxl)
# load using default settings
<- read_xlsx("data/penguins.xlsx")
penguins_xlsx
penguins_xlsx
# A tibble: 344 × 8
species island bill_length_mm bill_depth_mm flipper_length_mm
<chr> <chr> <dbl> <dbl> <dbl>
1 Adelie Torgersen 39.1 18.7 181
2 Adelie Torgersen 39.5 17.4 186
3 Adelie Torgersen 40.3 18 195
4 Adelie Torgersen NA NA NA
5 Adelie Torgersen 36.7 19.3 193
6 Adelie Torgersen 39.3 20.6 190
7 Adelie Torgersen 38.9 17.8 181
8 Adelie Torgersen 39.2 19.6 195
9 Adelie Torgersen 34.1 18.1 193
10 Adelie Torgersen 42 20.2 190
# ℹ 334 more rows
# ℹ 3 more variables: body_mass_g <dbl>, sex <chr>, year <dbl>
We can verify if the original and newly read object are identical.
identical(penguins, penguins_xlsx)
[1] FALSE
The two R objects are not identical. There is a subtle difference in classes.
Object penguins is a spec_tbl_db
, which is special subclass of tibble
generated by readr
package. While penguins_xlsx is a normal tibble
.
class(penguins)
[1] "spec_tbl_df" "tbl_df" "tbl" "data.frame"
class(penguins_xlsx)
[1] "tbl_df" "tbl" "data.frame"
identical(as_tibble(penguins), penguins_xlsx)
[1] TRUE
Writing to Excel
The usage of function write_xlsx()
from R package writexl
is relatively straightforward and mimics the behavior of write_csv
and similar functions from the same family.
library(writexl)
write_xlsx(penguins, "data/penguins_out.xlsx")
Exercise 6
Import the file penguins_extra.xlsx
into R. How would you fix the formatting issue while importing the file into R? You can preview the file in Excel or try to use the import in the GUI if RStudio.
Fixing it in Excel is not accepted, this goes far beyond the content of this course :)
<- read_excel("data/penguins_extra.xlsx", skip = 2) penguins_extra
Saving/Loading R Objects
Rather than keeping data in text or Excel files, it can be helpful to save the objects that are in R’s memory for future use in R, thus circumventing the need to read and prepare the data each time. This can be useful to introduce breakpoints into your analysis workflow, or to share prepared data.
Note that R saves objects in RDS/RData files in a compressed format, so these will not be view-able with a text editor.
Saving/Loading single or multiple named objects at once (.Rdata files)
The complete session (all objects), or a selection of objects, can be stored/retrieved with:
save()
load()
- convenient file extension: .RData
The objects will be loaded with the names they originally had.
<- 1:5
x <- "high"
y
# The following 2 commands are equivalent
save(list = c("x", "y") , file="obj1.rdata")
# or
save(x,y, file="obj1.rdata")
# Save all objects you have in the session
save(list = ls(), file="obj1.rdata")
# Delete / change the variables
rm(x)
<- "low"
y
# Load them up from saved file
load(file="obj1.rdata")
# Let us check the variables we just read in
# the variables are updated/re-created to
# whatever was in the RData file.
x y
Note on RStudio
By default, RStudio asks to save all the objects in your current session to a hidden .RData file upon exit, and restores it upon return. This behavior can be changed in RStudio’s preferences (Preferences > General). We recommend disabling this, to ensure that your workspace is always clean upon restarting. Otherwise, your environment can get cluttered, slow to load/save, and you might end up with object dependencies you did not plan for. Keeping your environment clean from session to session also forces you to keep completing your R script, so that commands can be executed again.
Saving/Loading single objects as RDS files
If you do not need to save multiple objects, and/or want the loaded object to have a different name, single to-be-named objects can be stored/retrieved with:
dput()
anddget()
(text)saveRDS()
andreadRDS()
(.rds file extension)
Text-based
We’re mentioning dput()
here as it can be quite useful for creating the textual representations of data in R needed to share them in places such as forums where you’re asking for help!
dput(head(penguins))
structure(list(species = c("Adelie", "Adelie", "Adelie", "Adelie",
"Adelie", "Adelie"), island = c("Torgersen", "Torgersen", "Torgersen",
"Torgersen", "Torgersen", "Torgersen"), bill_length_mm = c(39.1,
39.5, 40.3, NA, 36.7, 39.3), bill_depth_mm = c(18.7, 17.4, 18,
NA, 19.3, 20.6), flipper_length_mm = c(181, 186, 195, NA, 193,
190), body_mass_g = c(3750, 3800, 3250, NA, 3450, 3650), sex = c("male",
"female", "female", NA, "female", "male"), year = c(2007, 2007,
2007, 2007, 2007, 2007)), row.names = c(NA, -6L), class = c("tbl_df",
"tbl", "data.frame"))
RDS-based
RDS files contain a single object, without a name (like the output from dput()
), but compressed (so non-readable).
<- 1:5
x <- "high"
y
## save variables individually
saveRDS(x, file="x.rds")
saveRDS(y, file="y.rds")
## make a change
<- "low"
y
## reload and compare
<- readRDS(file="x.rds")
x.new identical(x, x.new)
## not identical, due to the change above
<- readRDS(file="y.rds")
y.new identical(y, y.new)
Exercise 7
Export the manually created tibble
with dataset names, number of rows and columns in each dataset as a plain text and as a RDS object.
# lets store it in a variable first to save typing
<- tibble(dataset=c("penguins", "penguins_raw"),
tab nrows = c(344, 344),
ncols = c(8, 17))
dput(tab)
saveRDS(tab, file="data/summary_table.rds")
Getting help
If you need a hand:
- Look at the R help pages.
- Ask us your questions during the face-to-face lecture.
- You can also write them on the Etherpad on ADAM.
- Search on Google.
- Talk to colleagues.
- Ask on forums.
Further material
Session info
::session_info() sessioninfo
─ Session info ──────────────────────────────────────────────────────────
setting value
version R version 4.4.1 Patched (2024-09-18 r87181)
os macOS Sequoia 15.0
system aarch64, darwin24.0.0
ui X11
language (EN)
collate en_US.UTF-8
ctype en_US.UTF-8
tz Europe/Zurich
date 2024-09-24
pandoc 3.4 @ /opt/homebrew/bin/ (via rmarkdown)
─ Packages ──────────────────────────────────────────────────────────────
package * version date (UTC) lib source
BiocManager 1.30.25 2024-08-28 [1] CRAN (R 4.4.1)
BiocStyle * 2.33.1 2024-06-12 [1] Bioconductor 3.20 (R 4.4.1)
bit 4.5.0 2024-09-20 [1] CRAN (R 4.4.1)
bit64 4.5.2 2024-09-22 [1] CRAN (R 4.4.1)
cellranger 1.1.0 2016-07-27 [1] CRAN (R 4.4.0)
cli 3.6.3 2024-06-21 [1] CRAN (R 4.4.1)
colorspace 2.1-1 2024-07-26 [1] CRAN (R 4.4.1)
crayon 1.5.3 2024-06-20 [1] CRAN (R 4.4.1)
digest 0.6.37 2024-08-19 [1] CRAN (R 4.4.1)
dplyr * 1.1.4 2023-11-17 [1] CRAN (R 4.4.0)
evaluate 1.0.0 2024-09-17 [1] CRAN (R 4.4.1)
fansi 1.0.6 2023-12-08 [1] CRAN (R 4.4.0)
fastmap 1.2.0 2024-05-15 [1] CRAN (R 4.4.0)
forcats * 1.0.0 2023-01-29 [1] CRAN (R 4.4.0)
generics 0.1.3 2022-07-05 [1] CRAN (R 4.4.0)
ggplot2 * 3.5.1 2024-04-23 [1] CRAN (R 4.4.0)
glue 1.7.0 2024-01-09 [1] CRAN (R 4.4.0)
gtable 0.3.5 2024-04-22 [1] CRAN (R 4.4.0)
hms 1.1.3 2023-03-21 [1] CRAN (R 4.4.0)
htmltools 0.5.8.1 2024-04-04 [1] CRAN (R 4.4.0)
htmlwidgets 1.6.4 2023-12-06 [1] CRAN (R 4.4.0)
jsonlite 1.8.9 2024-09-20 [1] CRAN (R 4.4.1)
knitr * 1.48 2024-07-07 [1] CRAN (R 4.4.1)
lifecycle 1.0.4 2023-11-07 [1] CRAN (R 4.4.0)
lubridate * 1.9.3 2023-09-27 [1] CRAN (R 4.4.0)
magrittr 2.0.3 2022-03-30 [1] CRAN (R 4.4.0)
munsell 0.5.1 2024-04-01 [1] CRAN (R 4.4.0)
pillar 1.9.0 2023-03-22 [1] CRAN (R 4.4.0)
pkgconfig 2.0.3 2019-09-22 [1] CRAN (R 4.4.0)
png * 0.1-8 2022-11-29 [1] CRAN (R 4.4.0)
purrr * 1.0.2 2023-08-10 [1] CRAN (R 4.4.0)
R6 2.5.1 2021-08-19 [1] CRAN (R 4.4.0)
readr * 2.1.5 2024-01-10 [1] CRAN (R 4.4.0)
readxl * 1.4.3 2023-07-06 [1] CRAN (R 4.4.0)
rlang 1.1.4 2024-06-04 [1] CRAN (R 4.4.1)
rmarkdown 2.28 2024-08-17 [1] CRAN (R 4.4.1)
rstudioapi 0.16.0 2024-03-24 [1] CRAN (R 4.4.0)
scales 1.3.0 2023-11-28 [1] CRAN (R 4.4.0)
sessioninfo 1.2.2 2021-12-06 [1] CRAN (R 4.4.0)
stringi 1.8.4 2024-05-06 [1] CRAN (R 4.4.0)
stringr * 1.5.1 2023-11-14 [1] CRAN (R 4.4.0)
tibble * 3.2.1 2023-03-20 [1] CRAN (R 4.4.0)
tidyr * 1.3.1 2024-01-24 [1] CRAN (R 4.4.0)
tidyselect 1.2.1 2024-03-11 [1] CRAN (R 4.4.0)
tidyverse * 2.0.0 2023-02-22 [1] CRAN (R 4.4.0)
timechange 0.3.0 2024-01-18 [1] CRAN (R 4.4.0)
tzdb 0.4.0 2023-05-12 [1] CRAN (R 4.4.0)
utf8 1.2.4 2023-10-22 [1] CRAN (R 4.4.0)
vctrs 0.6.5 2023-12-01 [1] CRAN (R 4.4.0)
vroom 1.6.5 2023-12-05 [1] CRAN (R 4.4.0)
withr 3.0.1 2024-07-31 [1] CRAN (R 4.4.1)
writexl * 1.5.0 2024-02-09 [1] CRAN (R 4.4.0)
xfun 0.47 2024-08-17 [1] CRAN (R 4.4.1)
yaml 2.3.10 2024-07-26 [1] CRAN (R 4.4.1)
[1] /Library/Frameworks/R.framework/Versions/4.4/Resources/library
─────────────────────────────────────────────────────────────────────────