Introduction to R

Tidyverse

Yu Cheng Hsu

Learning objectives

By the end of this session, you will

  • Understand the basic grammar and layered structure of ggplot2
  • Create simple plots using data, aesthetics, and geometric layers
  • Understand how to read and write data in R
  • Reflect on goodcoing practice in data wrangling

We DO NOT expect:

  • Memorize functions in the ggplot2 or dplyr

Package

  • A package is a bundle of functions, data, and documentation. You can import them through
library(package_name)
  • while using functions under the package you can use
package_name::function_name()

or

library(package_name)
function_name()
  • You can check what packages has been installed, imported to your environment using the tab Packages

Checking packages

Checking packages

Installing package

Golden rule: refers to the documentation of the specific package

  • CRAN (the Comprehensive R Archive Network) (Major package channel)
install.packages("ggplot2")
install.packages("tidyverse")
  • Bioconductor (Biomedical packages)
BiocManager::install("DESeq2")
  • Github (Codes at developmental stage)
devtools::install_github("tidyverse/ggplot2")

Example data - Gene Expression

  • Source: Blackmore et al. (2017), The effect of upper-respiratory infection on transcriptomic changes in the CNS.
  • Goal: Determine the effect of an upper-respiratory infection on changes in RNA transcription occuring in the cerebellum and spinal cord post infection
  • Experiment sample: Gender matched eight week old C57BL/6 mice
  • Exposure: With or without Influenza A infection
  • Observations: RNA-seq count of genes in the cerebellum and spinal cord tissues
  • Observation time: days 0 (non-infected), 4 and 8.

Task - Read two dataframe

Please read the data to your RStudio for further analysis

# Title: Read files and inspect data
# Read the count matrix and metadata, then preview the first few rows.
# --- Your code starts here ---

# rnaseq <- read.csv("WRITE DOWN YOUR PATH HERE")
# meta <- read.csv("WRITE DOWN YOUR PATH HERE")

# --- Your code ends here ---

# Show the first 3 rows and first 6 sample columns of the expression data.
head(rnaseq[, 1:6], 3)
     gene GSM2545336 GSM2545337 GSM2545338 GSM2545339 GSM2545340
1     Asl       1170        361        400        586        626
2    Apod      36194      10347       9173      10620      13021
3 Cyp2d22       4060       1616       1603       1901       2171
head(meta, 3)
      sample     organism age    sex   infection  strain time     tissue mouse
1 GSM2545336 Mus musculus   8 Female  InfluenzaA C57BL/6    8 Cerebellum    14
2 GSM2545337 Mus musculus   8 Female NonInfected C57BL/6    0 Cerebellum     9
3 GSM2545338 Mus musculus   8 Female NonInfected C57BL/6    0 Cerebellum    10

Discussion question

  • What are the pros and cons using absolute path and relative path ?
  • What will be the best practce to put the data file ?

Transforming data with dplyr

dplyr is a package for making tabular data manipulation easier. You can choose using

# Title: Load dplyr package
# dplyr provides functions for easy data frame manipulation.
library(dplyr)

or

using tidyverse an “umbrella-package” that installs several packages useful for data analysis which work together well such as tidyr, dplyr, ggplot2, tibble, etc.

# Title: Load tidyverse packages
# tidyverse loads multiple data science packages like ggplot2, dplyr, and tidyr.
library(tidyverse)

Pipeline mindset

Without dplyr, if we want to manipulate data, we can still using 0the following vanila way to perform the task :

# Title: Base R data manipulation example
# Show how row selection, counting, and column updates work without dplyr.

# Select rows 2 to 5 from the metadata.
meta <- meta[2:5,]

# Count how many female and male mice are in the selected rows.
sum(meta$sex == 'Female')
sum(meta$sex == 'Male')

# Overwrite the sex column with a logical value indicating male samples.
meta$sex = meta$sex == 'Male'

Discussion question

  • What are the common features for these operations
  • What are the pitfalls in using this approach ?

Pipe operator %>%

To prevent us from keep doing like a matryoshka doll

# Title: Nested function call example
# This shows the same work as a pipeline, but in nested form.
h(g(f(data_frame)))

Libraries under tidyverse support Pipe operator %>%

We can rewrite the above code as

# Title: Pipe operator example
# Use the pipe operator to write the same steps in a clearer, left-to-right order.
data_frame %>% 
  f() %>% 
  g() %>% 
  h()

%>% takes the output from the left hand side and then pipes it to be the first argument of the right function.

Discussion question

  • What are the benefits of using pipe?

Select columns or rows

  • Select the designated columns by select(COLUMN_NAMES)
# Title: Select metadata columns
meta %>%
    select(age, sex, infection) %>% # Keep only the age, sex, and infection columns.
    head(3) # This line is for display control, you can omit it and the previous %>%.
  age    sex   infection
1   8 Female  InfluenzaA
2   8 Female NonInfected
3   8 Female NonInfected
  • Select the designated rows by filter(CONDITION)
# Title: Filter rows by sex
meta %>%
    filter(sex == 'Female') %>% # Keep only female samples.
    head(3) # This line is for display control, you can omit it and the previous %>%.
      sample     organism age    sex   infection  strain time     tissue mouse
1 GSM2545336 Mus musculus   8 Female  InfluenzaA C57BL/6    8 Cerebellum    14
2 GSM2545337 Mus musculus   8 Female NonInfected C57BL/6    0 Cerebellum     9
3 GSM2545338 Mus musculus   8 Female NonInfected C57BL/6    0 Cerebellum    10

Common error

filter() function has different meaning in other R default library. If the error message occurs, you can try replaced it by dplyr::filter() instead

Manipulating columns

  • You can rename column by rename()
# Title: Rename columns
meta %>% # Rename selected metadata columns to make them more readable.
    rename(
              Sex = sex,
              Time = time,
              Infection = infection
              ) %>%
    head(3)
      sample     organism age    Sex   Infection  strain Time     tissue mouse
1 GSM2545336 Mus musculus   8 Female  InfluenzaA C57BL/6    8 Cerebellum    14
2 GSM2545337 Mus musculus   8 Female NonInfected C57BL/6    0 Cerebellum     9
3 GSM2545338 Mus musculus   8 Female NonInfected C57BL/6    0 Cerebellum    10
  • You can create new column by mutate()
# Title: Create or modify columns

meta %>%
    mutate( # Use mutate() to update the time column and add a new infection flag.
              time = time + age,
              infection  = "No"
    ) %>%
    head(3)
      sample     organism age    sex infection  strain time     tissue mouse
1 GSM2545336 Mus musculus   8 Female        No C57BL/6   16 Cerebellum    14
2 GSM2545337 Mus musculus   8 Female        No C57BL/6    8 Cerebellum     9
3 GSM2545338 Mus musculus   8 Female        No C57BL/6    8 Cerebellum    10

Perform summary statistics

Frequently we will need to provide summary statistics for your data, we can use summarize() to do it

# Title: Compute summary statistics

meta %>%
    summarize( # Summarize the metadata with count, mean, standard deviation, and female sample count.
              n=n(),
              mean_time=mean(time),
              sd_time=sd(time),
              num_female=sum(sex=='Female')
    )
   n mean_time  sd_time num_female
1 22         4 3.265986         12

Some common summary function could be

  • mean()
  • sd()
  • max(), min()
  • sum()
  • n()

Summary statistics by group

  • group_by() and summarizep rovide summary statistics by group.
  • You can group by multiple groups together, but noted that they need to be stated in the same group_by() brackets
# Title: Grouped summary by infection

meta %>%
    group_by(infection) %>% # Group the metadata by infection status,
    summarize(      #  and compute summary statistics for each group.
              n=n(),
              mean_time=mean(time),
    )
# A tibble: 2 × 3
  infection       n mean_time
  <chr>       <int>     <dbl>
1 InfluenzaA     15      5.87
2 NonInfected     7      0   
# Title: Multi-group summary
meta %>%
    group_by(infection, sex) %>% # Group by both infection status and sex,
    summarize(                   #  then summarize the combined groups.
              n=n(),
              mean_time=mean(time),
              sd_time=sd(time),
    )

Condition modification

Using if_else(CONDITION, TRUE, FALSE) or case_when(CONDITION ~ OUTCOME, ...) to rematch condition into certain value/ ordinal items

  • if_else is only for dichotomous processing
  • case_when can handle sequential logical control
# Title: Create binary infection status
# Use if_else() to add a new infection_status column based on time value.
meta %>%
    select(sample, age, sex, time) %>%
    mutate( infection_status = if_else(
        time == 0, "Not infected", "Infected"
    )) %>%
    head(3)
      sample age    sex time infection_status
1 GSM2545336   8 Female    8         Infected
2 GSM2545337   8 Female    0     Not infected
3 GSM2545338   8 Female    0     Not infected
# Title: Create categorical infection stage
# Use case_when() to assign descriptive labels for each time point.
meta %>%
    select(sample, age, sex, time) %>%
    mutate( infection_status = case_when(
        time == 0 ~ "Not infected",
        time == 4 ~ "Early stage",
        time == 8 ~ "Late stage"
    ))%>%
    head(3)
      sample age    sex time infection_status
1 GSM2545336   8 Female    8       Late stage
2 GSM2545337   8 Female    0     Not infected
3 GSM2545338   8 Female    0     Not infected

Combining Dataframes

Consider we have data_frame sample_metadata.csv and count_matrix.csv. If we want to combine them together. We can use join()

There are four options

  • inner_join(x, y): Keeps rows present in both tables.
  • left_join(x, y): Keeps all rows from x, matching rows from y.
  • right_join(x, y): Keeps all rows from y, matching rows from x.
  • full_join(x, y): Keeps all rows from both tables.

Combining Dataframes

First I transpose the RNA count matrix. (The below code chunk is out of the scope, you might ignore why it works)

# Title: Transform RNA count matrix layout
# Convert the RNA data from wide to long and back to wide format by gene.
rnaseq <- rnaseq %>% 
           pivot_longer(cols=-gene, names_to="sample", values_to = "value") %>%
  pivot_wider(names_from = gene, values_from = value)

You can combine meta data and rna sequence data together.

# Title: Join metadata with RNA data
# Combine the sample metadata with the RNA expression table by sample ID.
meta %>% left_join(rnaseq, by = 'sample') %>% select(1:12) %>% head(3)
      sample     organism age    sex   infection  strain time     tissue mouse
1 GSM2545336 Mus musculus   8 Female  InfluenzaA C57BL/6    8 Cerebellum    14
2 GSM2545337 Mus musculus   8 Female NonInfected C57BL/6    0 Cerebellum     9
3 GSM2545338 Mus musculus   8 Female NonInfected C57BL/6    0 Cerebellum    10
   Asl  Apod Cyp2d22
1 1170 36194    4060
2  361 10347    1616
3  400  9173    1603

Best practice of data wrangling

  1. The “Read-Write-Verify” Loop

    • Write a step
    • Run it immediately
    • Verify the output

    DO NOT propogate error and raise your hand

  2. Look up documentations

  3. Make your code readable

  4. Try and error

Introduction to ggplot2

About ggplot2

  • Visualization library in R for creating elegant and complex visualizations.
  • ggplot2 is part of the tidyverse collection of R packages.
  • Developed by Hadley Wickham [@wickham2010layered]. He received the COPSS Presidents’ Award for his contributions to the tidyverse collection.
  • Flexible and intuitive once you understand its core concepts.
library(ggplot2)

About the data

iris is a dataset introduced by Ronald Fisher in 1936 collect the iris flower morphology measurements from 3 species

  • Setosa,
  • Versicolor
  • Virginica

it measures the length and width of iris’s sepal and petal

data(iris)
head(iris)
  Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1          5.1         3.5          1.4         0.2  setosa
2          4.9         3.0          1.4         0.2  setosa
3          4.7         3.2          1.3         0.2  setosa
4          4.6         3.1          1.5         0.2  setosa
5          5.0         3.6          1.4         0.2  setosa
6          5.4         3.9          1.7         0.4  setosa

Iris flower

Iris flower

ggplot2 example

  • What are the elements in a graph ?

Components in the graph

Common components of a figure, figure from @ggplot2

Common components of a figure, figure from @ggplot2

Choosing the Visualization

Thinking process and decision flowchart will help you determine which type of graph to use (at least within the scope of this course):

  1. What is the purpose of displaying the graph?
  2. What types of data are you going to present?

flowchart LR
  A{Data type} -->|Continuous| B{Purpose}
  A{Data type}  -->|Discrete| C{Purpose}

  B{Purpose}-->|Exploration| D((Histogram/Boxpot))
  B{Purpose} -->|Association| E((Scatter plot))
  B{Purpose} -->|Association+time| T((Line plot))

  C{Purpose}-->|Exploration| F((Bar chart))
  C{Purpose} -->|Association| G((Tree map))

Overview of ggplot2 logics

ggplot2 desgined as you are plotting by hands, so the process will looks like

  1. Defining coordinate systems
  2. Plotting dots, lines, bars, and etc.
  3. Adding legend and axis informations

Layer features of ggplot2

Layer features of ggplot2

Layer 1: Coordinate system

First, we define the coordinate system for our iris data in R

The function is ggplot(), which takes two arguments 1

  • data: The data you are going to use
  • mapping: Should be wrapped in aes() specifying how each column maps to the coordinate.
# Initialize a ggplot object with 
# data and aesthetic mappings
p <- ggplot(data=iris, 
  mapping = aes(x = Sepal.Length))
p  # Display the base plot (no layers added yet)

# Alternative 
# p <- ggplot(iris, aes(Sepal.Length))
# p
Figure 1: Showing coordinate systems in ggplot2

Layer 2: Plotting data

The general format for plotting items will be

geom_XXX(mapping=aes())

In aes(), you can map anything to the figure, common terms could be

  • x
  • y
  • color, or colour
  • fill, shape, etc.

These functions are named in the format geom_XXXX:

Function name
Histogram geom_histogram()
Box chart geom_boxplot()
Bar chart geom_bar()
Scatter chart geom_point()
Line chart geom_line()

Layer 2: Example

p+geom_point(aes(y = Petal.Width, color=Species))

Scatter plot for Sepal length and Pedal width

Scatter plot for Sepal length and Pedal width
p+geom_boxplot()

Box plot for Sepal length

Box plot for Sepal length
  • + operator to stack on the original ggplot()
  • Adding additional mapping in the geom_XXX()
  • Overwriting mapping in the ggplot()

More examples

ggplot(data=iris)+
  geom_histogram(aes(x=Petal.Length, color=Species))

Histogram for Petal length

Histogram for Petal length
ggplot(data=iris)+
  geom_bar(aes(x=Species,fill=Species))

Histogram for Petal length

Histogram for Petal length

Some notes 1: color affects the border color (dots/lines), fill affects the rectangle color

Layer 3: Legends and axis

Scales are functions indicate how each variable behave and presents on the graph. The function usually in the format scale_(AES)_(datatype):

Discrete Continuous
X scale_x_discrete() scale_x_continuous()
Y scale_y_discrete() scale_y_continuous()
color scale_color_discrete() scale_color_continuous()

Most of time you only need to handle the presentation in Elements in the scale

Argument name Axis Legend
name Label Title
breaks Ticks Key
labels Tick label Key Label

Example

# Create a scatter plot with customized axis and color legend labels
   p +
  geom_point(aes(y = Petal.Width, color=Species)) +  # Add points layer for data representation
  scale_x_continuous(name="Sepal Length") +  # Customize x-axis label
  scale_y_continuous(name="Petal Width") + # Customize y-axis label
  scale_colour_discrete(name="Species name")  # Customize color legend title
Figure 2: Scatter plot of MPG vs cylinders with customized axis and legend labels.

Save figure

Add the function ggsave("FILE_PATH+FILE_NAME.png") after your ggplot instructions:

# Saving plots as figure_theme_grey.png at the folder figure
ggsave("figure_1.png",p)
  • It support most of the common image formats like pdf, jpeg, png

Saved figure location

Saved figure location

Final notes

  • There are far more things you can do in ggplot2
  • Try to look up on the reference (or even use AI) when you need it.