---
title: "Data Visualization Workshop"
author: "Han Yu"
output: pdf_document
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE)
```

## Introduction

Data visualization is a crucial skill in data science and analytics. In this tutorial, we will explore how to create various types of visualizations in R using the ggplot2 package, a powerful tool for creating informative and aesthetically pleasing graphics.

## Installing and Loading Required Packages

Before we begin, ensure you have the necessary packages installed.

```{r}
library(dplyr)
library(tidyverse)
library(ggplot2)
library(ggridges)
library(datasauRus)
library(colorspace)
library(pheatmap)
library(survival)
library(survminer)
library(qqman)
library(plotly)
library(ggridges)
library(gganimate)
library(gifski)
library(palmerpenguins)
library(gapminder)
```

## Module 1: Brief Review of Basic Visualization in R

Let's start with the simple built-in penguins data set to get familiar with the structure of ggplot2.

```{r}
data(penguins)
head(penguins)
penguins <- penguins %>% drop_na()

# Basic scatter plot
ggplot(penguins) +
  geom_point(aes(x = bill_length_mm, 
                 y = bill_depth_mm))
```

For this tutorial, we will mostly use the built-in gapminder dataset, which contains excerpt of the Gapminder data on life expectancy, GDP per capita, and population by country.

Let's explore the gapminder dataset.

```{r}
data(gapminder) # load in dataset
head(gapminder, 5) # check the first 5 rows
```

Let's use the gapminder dataset to build different useful plots.

### 1.1 bar plot

Bar plot is used to visually compare categorical data by representing values with rectangular bars.

```{r}
# bar plot: discrete variable
ggplot(gapminder, aes(x = continent)) +
  geom_bar()
```

### 1.2 histogram/ frequency/ density plot

A histogram, frequency plot, and density plot all visualize the distribution of a dataset, but they differ in how they represent data: a histogram groups data into bins and shows counts, a frequency plot connects points representing counts for each bin, and a density plot estimates the probability density function, providing a smooth representation of distribution shape.

```{r}
# histogram plot: continuous variable
ggplot(gapminder, aes(x = lifeExp, fill = continent)) +
  geom_histogram(binwidth = 1)

# frequency plot
ggplot(gapminder, aes(x = lifeExp, color = continent)) +
  geom_freqpoly()

# density plot (smooth histogram = density plot)
ggplot(gapminder, aes(x = lifeExp, color = continent)) +
  geom_density()
```

### 1.3 box plot

A box plot is used to summarize the distribution of a dataset by displaying its median, quartiles, and potential outliers, helping to identify variability and skewness.

```{r}
# box plot: one discrete variable + one continuous variable
ggplot(gapminder, aes(x = year, y = lifeExp)) +
  geom_boxplot(aes(group = year))
```

### 1.4 violin plot

A violin plot combines aspects of a box plot and a density plot to show the distribution, probability density, and variability of a dataset, making it easier to identify patterns and multimodal distributions.

```{r}
# violin plot
ggplot(gapminder, aes(x = continent, y = lifeExp)) +
  geom_violin(trim = FALSE, alpha = 0.5) +
  stat_summary(
    fun = mean,
    fun.max = function(x) {
      mean(x) + sd(x)
    },
    fun.min = function(x) {
      mean(x) - sd(x)
    },
    geom = "pointrange"
  )
```

### 1.5 Example 1

Let's see an example of how to improve a data visualization plot step by step. We used the built-in lincoln_weather dataset to investigate the weather distribution of Lincoln. Lincoln weather dataset containing weather information from Lincoln, Nebraska, from 2016.

#### 1.5.1 Create the dataset we will use in this example

```{r}
lincoln_df <- lincoln_weather %>%
  mutate(
    month_short = fct_recode(
      Month,
      Jan = "January",
      Feb = "February",
      Mar = "March",
      Apr = "April",
      May = "May",
      Jun = "June",
      Jul = "July",
      Aug = "August",
      Sep = "September",
      Oct = "October",
      Nov = "November",
      Dec = "December"
    )
  ) %>%
  mutate(month_short = fct_rev(month_short)) %>%
  select(Month, month_short, 'Mean Temperature [F]')

head(lincoln_df)
```

#### 1.5.2 What's the problem of the following plot?

```{r}
# points-errorbars
lincoln_errbar <- lincoln_df %>%
  ggplot(aes(x = month_short, y = `Mean Temperature [F]`)) +
  stat_summary(
    fun = mean, 
    fun.max = function(x) {
      mean(x) + 2 * sd(x)
    },
    fun.min = function(x) {
      mean(x) - 2 * sd(x)
    }, 
    geom = "pointrange"
  ) +
  xlab("month") +
  ylab("mean temperature (°F)") +
  theme_classic(base_size = 14) +
  theme(
    axis.text = element_text(color = "black", size = 12),
    plot.margin = margin(3, 7, 3, 1.5)
  )

lincoln_errbar
```

#### 1.5.3 Use box/violin plot to show the distribution

```{r}
# box plot
lincoln_box <- lincoln_df %>%
  ggplot(aes(x = month_short, y = `Mean Temperature [F]`)) +
  geom_boxplot(fill = "grey90") +
  xlab("month") +
  ylab("mean temperature (°F)") +
  theme_classic(base_size = 14) +
  theme(
    axis.text = element_text(color = "black", size = 12),
    plot.margin = margin(3, 7, 3, 1.5)
  )

lincoln_box

# violin plot
lincoln_violin <- lincoln_df %>%
  ggplot(aes(x = month_short, y = `Mean Temperature [F]`)) +
  geom_violin(fill = "grey90") +
  xlab("month") +
  ylab("mean temperature (°F)") +
  theme_classic(base_size = 14) +
  theme(
    axis.text = element_text(color = "black", size = 12),
    plot.margin = margin(3, 7, 3, 1.5)
  )

lincoln_violin
```

#### 1.5.4 Use points to show the original data

```{r}
# points
lincoln_points <- lincoln_df %>%
  ggplot(aes(x = month_short, y = `Mean Temperature [F]`)) +
  geom_point(size = 0.75) +
  xlab("month") +
  ylab("mean temperature (°F)") +
  theme_classic(base_size = 14) +
  theme(
    axis.text = element_text(color = "black", size = 12),
    plot.margin = margin(3, 7, 3, 1.5)
  )

lincoln_points

# jitter points
lincoln_jitter <- lincoln_df %>%
  ggplot(aes(x = month_short, y = `Mean Temperature [F]`)) +
  geom_point(position = position_jitter(width = .15, height = 0, seed = 320), size = 0.75) +
  xlab("month") +
  ylab("mean temperature (°F)") +
  theme_classic(base_size = 14) +
  theme(
    axis.text = element_text(
      color = "black",
      size = 12
    ),
    plot.margin = margin(3, 7, 3, 1.5)
  )

lincoln_jitter
```

#### 1.5.5 Use sina plot to combine violin plot and jitter points

```{r}
# sina plot
lincoln_sina <- lincoln_df %>%
  ggplot(aes(x = month_short, y = `Mean Temperature [F]`)) +
  geom_violin(color = "transparent", fill = "gray90") +
  # dviz.supp::stat_sina(size = 0.85) +
  geom_jitter(width = 0.25, size = 0.85) +
  xlab("month") +
  ylab("mean temperature (°F)") +
  theme_classic(base_size = 14) +
  theme(
    axis.text = element_text(
      color = "black",
      size = 12
    ),
    plot.margin = margin(3, 7, 3, 1.5)
  )

lincoln_sina
```

## Module 2: Advanced Customization and Data Transformation

### 2.1 Data Transformations and Faceting

In data visualization, data transformation is a crucial step to ensure that the data is in the right format and structure for effective visual representation.

#### 2.1.1 Log Transformation

There are many different data transformation methods, and the following is an example of log transformation.

We would like to investigate what is the relationship among GDP per capita, life expectancy, and population across different countries.

```{r}
# Step 1: calculate the mean of lifeExp, gdpPercap, pop in different countries
newgapminder <- gapminder %>% 
  group_by(continent, country) %>% 
  summarise(
    across(c(lifeExp, gdpPercap, pop), mean)
  )
newgapminder

# Step 2: draw the plot
newgapminder %>% 
  ggplot(aes(x = gdpPercap, y = lifeExp)) +
    geom_point(aes(color = continent, size = pop))

# Step 3: log transformation of x-axis(gdpPercap)
newgapminder %>% 
  ggplot(aes(x = gdpPercap, y = lifeExp)) +
    geom_point(aes(color = continent, size = pop)) +
    scale_x_log10()

# Step 4 (Optional): add dollar format or unit format to label the axis for better interpretation
newgapminder %>% 
  ggplot(aes(x = gdpPercap, y = lifeExp)) +
    geom_point(aes(color = continent, size = pop)) +
    scale_x_log10(breaks = c(500, 1000, 3000, 10000, 30000),
                  labels = scales::dollar)

newgapminder %>% 
  ggplot(aes(x = gdpPercap, y = lifeExp)) +
    geom_point(aes(color = continent, size = pop)) +
    scale_x_log10(
      name = "GDP per capita",
      breaks = c(500, 1000, 3000, 10000, 30000),
      labels = scales::unit_format(unit = "dollar"))
```

#### 2.1.2 Faceting

Data faceting is used in data visualization to break down data into subsets and display them across multiple smaller plots. This technique helps in revealing patterns, trends, and relationships that might be obscured in a single, aggregated plot. The following is an example of data faceting.

```{r}
# facet_grid()：create a grid of graphs, by rows and columns
gapminder %>%
  ggplot(aes(x = lifeExp)) +
  geom_density() +
  facet_grid(. ~ continent)

gapminder %>%
  ggplot(aes(x = lifeExp)) +
  geom_histogram() +
  facet_grid(continent ~ .)

# facet_wrap()：create small multiples by “wrapping” a series of plots
gapminder %>%
  ggplot(aes(x = gdpPercap, y = lifeExp)) +
  geom_point(show.legend = FALSE) +
  scale_x_log10() +
  facet_wrap(~continent)
```

### 2.2 Advanced Aesthetic Customization - color

Color plays a crucial role in data visualization. We will explore how to use ColorSpace to create plots tailored to different data types.

Show all available color in colorspace.

```{r}
hcl_palettes(plot = TRUE)

hcl_palettes("qualitative", plot = TRUE)
hcl_palettes("sequential (single-hue)", n = 7, plot = TRUE)
hcl_palettes("sequential (multi-hue)", n = 7, plot = TRUE)
hcl_palettes("diverging", n = 7, plot = TRUE)
```

#### 2.2.1 qualitative colors

Qualitative (iris dataset) : create a scatter plot of Sepal.Length vs. Sepal.Width, coloring points by Species.

```{r}
# qualitative colors (Categorical Data)
ggplot(iris, aes(x = Sepal.Length, y = Sepal.Width, color = Species)) +
  geom_point(size = 3) +
  scale_color_discrete_qualitative(palette = "Dark 3") +  # Using discrete qualitative color scale
  ggtitle("Qualitative Colors Example: Iris Species")
```

#### 2.2.2 Sequential(single-hue) colors

Sequential single-hue (mtcars dataset): create a bar plot of car models (rownames(mtcars)) based on their miles per gallon (mpg), using a single-hue sequential color gradient.

```{r}
# sequential (single-hue) colors (Ordered Data)
# Order data by mpg
mtcars$car <- rownames(mtcars)
mtcars <- mtcars[order(mtcars$mpg), ]
# Create a bar plot
ggplot(mtcars, aes(x = reorder(car, mpg), y = mpg, fill = mpg)) +
  geom_bar(stat = "identity") +
  scale_fill_continuous_sequential(palette = "Blues 3") +  # Sequential color for fill
  coord_flip() + # Flip for better readability
  ggtitle("Sequential (Single-Hue) Colors Example: MPG in mtcars") +
  theme_minimal()
```

#### 2.2.3 Sequential (multi-hue) colors

Sequential multi-hue (volcano dataset): The volcano heatmap provides a 2D topographic view of elevation data using color intensity. It highlights how terrain elevation changes over space.

```{r}
# sequential (multi-hue) colors (Ordered Data)
# Convert volcano dataset to long format for ggplot
volcano_df <- as.data.frame(as.table(volcano))
colnames(volcano_df) <- c("x", "y", "elevation")
# Plot the heatmap
ggplot(volcano_df, aes(x = x, y = y, fill = elevation)) +
  geom_tile() +
  scale_fill_continuous_sequential(palette = "YlOrRd") +  # Sequential multi-hue color scale
  ggtitle("Sequential (Multi-Hue) Colors Example: Volcano Elevation") +
  xlab("X Coordinate") +  # Add x-axis label
  ylab("Y Coordinate") +  # Add y-axis label
  theme_minimal() +
  theme(
    axis.text = element_blank(),   # Remove axis text
    axis.ticks = element_blank()   # Remove axis ticks
  )
```

#### 2.2.4 Diverging colors

Diverging (USArrests dataset): map UrbanPop deviation from the median across states using a diverging color scale. The goal is to understand how each state's urban population percentage compares to the median urban population percentage. A positive value means the state's urban population is above the median, while a negative value means it is below the median.

```{r}
# diverging colors (USArrest Dataset)
# Compute deviation from median
USArrests$State <- rownames(USArrests)
USArrests$UrbanPop_dev <- USArrests$UrbanPop - median(USArrests$UrbanPop)
# Create a bar plot
ggplot(USArrests, aes(x = reorder(State, UrbanPop_dev), y = UrbanPop_dev, fill = UrbanPop_dev)) +
  geom_bar(stat = "identity") +
  scale_fill_continuous_divergingx(palette = "RdBu") +
  coord_flip() +
  ggtitle("Diverging Colors Example: UrbanPop Deviation in USArrests") +
  theme_minimal()
```

### 2.3 Example 2

Compare the before-and-after heatmaps below to see how color choice enhance readability and perceptual accuracy in data visualization.

#### 2.3.1 Before

Use the default color to create the heatmap.

```{r}
# before
gapminder %>% 
  group_by(continent, year) %>%
  summarise(mean_lifeExp = mean(lifeExp)) %>%
  ggplot(aes(x = year, y = continent, fill = mean_lifeExp)) +
  geom_tile() +
  theme_classic()
```

#### 2.3.2 After

Use the Sequential color to show the heatmap.

```{r}
# color choice 1 (Viridis)
gapminder %>% 
  group_by(continent, year) %>%
  summarise(mean_lifeExp = mean(lifeExp)) %>%
  ggplot(aes(x = year, y = continent, fill = mean_lifeExp)) +
  geom_tile() +
  theme_classic() +
  scale_fill_continuous_sequential(palette = "Viridis", rev = FALSE)

# color choice 2 (Inferno)
gapminder %>% 
  group_by(continent, year) %>%
  summarise(mean_lifeExp = mean(lifeExp)) %>%
  ggplot(aes(x = year, y = continent, fill = mean_lifeExp)) +
  geom_tile() +
  theme_classic() +
  scale_fill_continuous_sequential(palette = "Inferno", begin = 0.15, rev = FALSE)
```

### 2.4 Size & Transparency

Explore how to adjust the size and transparency of the points to enhance readability.

```{r}
ggplot(penguins) +
  geom_point(aes(x = bill_length_mm, 
                 y = bill_depth_mm, 
                 color = species), 
             size=2, 
             alpha = 0.4)

ggplot(penguins) +
  geom_point(aes(x = bill_length_mm, 
                 y = bill_depth_mm, 
                 color = species), 
             size=4, 
             alpha = 0.8)
```

### 2.5 Legend

The legend plays a crucial role in interpreting the plot. Below, we will explore how to adjust its appearance, create different legends for different layers, and learn how to delete or merge legends effectively.

```{r}
ggplot(penguins, aes(x = bill_length_mm, y = bill_depth_mm, color = species)) +
  geom_point(size = 1, alpha = 0.4)
```

#### 2.5.1 Adjust the appearance of the legend

```{r}
# Adjust the appearance of the legend
ggplot(penguins, aes(x = bill_length_mm, y = bill_depth_mm, color = species)) +
  geom_point(size = 1, alpha = 0.4) +
  guides(color = guide_legend(override.aes = list(size = 3,alpha=1)))
```

#### 2.5.2 Generate different legends for different layers

```{r}
# Generate different legends for different layers
ggplot(penguins, aes(x = bill_length_mm, y = bill_depth_mm, color = species)) +
  geom_point(size  = 3) +
  geom_smooth(method = "lm", se = FALSE)

ggplot(penguins, aes(x = bill_length_mm, y = bill_depth_mm, color = species)) +
  geom_point(aes(alpha = "Observed"), size = 3) +
  geom_smooth(method = "lm", se = FALSE, aes(alpha = "Fitted")) +
  scale_alpha_manual(
    name = NULL,
    values = c(1, 1),
    breaks = c("Observed", "Fitted")
  ) +
  guides(alpha = guide_legend(override.aes = list(
    linetype = c(0, 1),
    shape = c(16, NA),
    color = "black"
  )))
```

#### 2.5.3 Delete legend

```{r}
# delete legend
penguins %>%
  ggplot(aes(x = bill_length_mm, y = bill_depth_mm, color = species, size = body_mass_g)) +
  geom_point()

penguins %>%
  ggplot(aes(x = bill_length_mm, y = bill_depth_mm, color = species, size = body_mass_g)) +
  geom_point() +
  guides(
    color = guide_legend("species"),      # keep
    size = "none"                      # remove
  )
```

#### 2.5.4 Merge legend

```{r}
# merge legend
penguins %>% 
  ggplot(
    aes(x = bill_length_mm, y = bill_depth_mm, 
      color = body_mass_g, size = body_mass_g)
  ) +
  geom_point(alpha = 0.6) +
  scale_color_viridis_c()

# method 1
penguins %>% 
  ggplot(
    aes(x = bill_length_mm, y = bill_depth_mm, 
      color = body_mass_g, size = body_mass_g)
  ) +
  geom_point(alpha = 0.6) +
  scale_color_viridis_c() +
  guides(
    color = guide_legend(), 
    size = guide_legend()
  )

# method 2
penguins %>% 
  ggplot(
    aes(x = bill_length_mm, y = bill_depth_mm, 
      color = body_mass_g, size = body_mass_g)
  ) +
  geom_point(alpha = 0.6) +
  scale_color_viridis_c(guide = "legend") 
```

### 2.6 Theme

Explore how to change the theme of the plots.

```{r}
df <- data.frame(x = 1:3, y = 1:3)
base <- ggplot(df, aes(x, y)) + geom_point()
# default theme
base + theme_grey() + ggtitle("theme_grey()")
# other built-in themes in ggplot2
base + theme_bw() + ggtitle("theme_bw()")
base + theme_linedraw() + ggtitle("theme_linedraw()")
base + theme_light() + ggtitle("theme_light()")
base + theme_dark() + ggtitle("theme_dark()")
base + theme_minimal()  + ggtitle("theme_minimal()")
base + theme_classic() + ggtitle("theme_classic()")
base + theme_void() + ggtitle("theme_void()")
```

### 2.7 Exercise 1

Improve the plot based on the following requirements.

1.  Improving the axes and legend labels
2.  Adding a title for the plot
3.  Tweaking the color scale to your favorite palette
4.  The background should be white, not pale grey
5.  Major gridlines should be a pale grey and minor gridlines should be removed
6.  The plot title should be centered in 12pt bold text

```{r}
ggplot(penguins, aes(x = bill_length_mm, y = bill_depth_mm, color = species)) +
  geom_point()
```

The answer is as follows. Do not open the answer code block before completing this exercise.

```{r}
# answer

# 1.Improving the axes and legend labels

# 2.Adding a title for the plot

# 3.Tweaking the color scale to your favorite palette

# 4.The background should be white, not pale grey

# 5.Major gridlines should be a pale grey and minor gridlines should be removed

# 6.The plot title should be centered in 12pt bold text

ggplot(penguins, aes(x = bill_length_mm, y = bill_depth_mm, color = species)) +
  geom_point() +
  labs(
    title = "Penguin Bill Length vs. Depth",  # Add title
    x = "Bill Length (mm)",  # Improve x-axis label
    y = "Bill Depth (mm)",   # Improve y-axis label
    color = "Species"        # Improve legend label
  ) +
  scale_color_discrete_qualitative(palette = "Dark 3") + # Choose a favorite color palette
  theme_minimal(base_size = 12) +  # Set white background and base size
  theme(
    legend.justification = c(0.5, 0.5),  # Adjust legend positioning
    legend.title = element_text(size = 10),  # Set legend title font size
    legend.text = element_text(size = 9),   # Set legend text font size
    plot.title = element_text(size = 12, face = "bold", hjust = 0.5),  # Title centered in 12pt bold
    panel.grid.major = element_line(color = "grey90"),  # Pale grey major gridlines
    panel.grid.minor = element_blank(),  # Remove minor gridlines
    panel.background = element_blank()  # Set background to white
  )

```

## Module 3. Advanced Visualization Techniques

Next, we will explore some specialized plots and learn which types of data are best suited for each.

### 3.1 Specialized plot - heatmaps

```{r}
# base R: heatmap() function
data <- matrix(rnorm(100), nrow = 10)
rownames(data) <- paste("Row", 1:10)
colnames(data) <- paste("Col", 1:10)
heatmap(data)

# ggplot2 with geom_tile()
data <- expand.grid(Row = LETTERS[1:5], Column = LETTERS[6:10])
data$Value <- runif(nrow(data), 1, 100)
ggplot(data, aes(x = Column, y = Row, fill = Value)) +
  geom_tile() +
  scale_fill_continuous_sequential(palette = "Inferno", rev = FALSE) +
  labs(title = "Heatmap using ggplot2", x = "Columns", y = "Rows", fill = "Value") +
  theme_minimal()

# pheatmap for annotated heatmaps
data <- matrix(rnorm(100), nrow = 10)
rownames(data) <- paste("Gene", 1:10)
colnames(data) <- paste("Sample", 1:10)
pheatmap(data, color = colorRampPalette(c("blue", "white", "red"))(50))
```

### 3.2 Specialized plot - KM Curve

```{r}
# Load the lung dataset
data("lung")

# Create a survival object
surv_object <- Surv(time = lung$time, event = lung$status)

# Fit a Kaplan-Meier survival curve by group
km_fit_group <- survfit(surv_object ~ sex, data = lung)

# Plot the Kaplan-Meier curves for groups
ggsurvplot(
  km_fit_group,
  data = lung,
  conf.int = TRUE,
  pval = TRUE,              # Add p-value from log-rank test
  xlab = "Time (days)",
  ylab = "Survival Probability",
  title = "Kaplan-Meier Curve by Sex",
  palette = c("blue", "red"), # Different colors for groups
  legend.labs = c("Male", "Female"), # Custom legend labels
  ggtheme = theme_minimal()
)
```

### 3.3 Specialized plot - Manhattan plot

```{r}
# Load the geuvadis dataset
data("gwasResults")

# Inspect the first few rows
head(gwasResults)

# Structure of the data
# SNP: SNP identifier
# CHR: Chromosome
# BP: Base-pair position
# P: P-value

manhattan(
  gwasResults,
  chr = "CHR",       # Chromosome column
  bp = "BP",         # Base-pair position column
  p = "P",           # P-value column
  snp = "SNP",       # SNP column
  genomewideline = -log10(5e-8),  # Genome-wide significance threshold
  suggestiveline = -log10(1e-5),  # Suggestive significance threshold
  main = "Manhattan Plot", # Title
  annotatePval = 0.01
)

```

### 3.4 Specialized plot - 2d density plot

```{r}
# Scatter plot
ggplot(diamonds, aes(x = carat, y = price)) +
  geom_point() +
  theme_minimal() +
  labs(title = "Simple 2D Density Plot",
       x = "Carat",
       y = "Price")

# 2D density plot
ggplot(diamonds, aes(x = carat, y = price)) +
  geom_bin2d(bins = 100) +
  scale_fill_continuous(type = "viridis") +
  theme_minimal() +
  labs(title = "Simple 2D Density Plot",
       x = "Carat",
       y = "Price")

```

### 3.5 Interactive plot - plotly

Interactive plots allow users to explore data more deeply through actions like clicking, zooming, or hovering, providing richer insights and making the data more understandable and engaging. Next, we will explore how to make plots interactive.

#### 3.5.1 ggplotly

```{r}
p <- ggplot(penguins) +
  geom_point(aes(x = bill_length_mm, 
                 y = bill_depth_mm, 
                 color = species))

ggplotly(p)
```

#### 3.5.2 Customize tooltip

What if we are interested in which island the penguin lives or any other information?

```{r}
penguins <- penguins %>%
  mutate(text = paste(
    "Island: ", island,
    "\nSpecies: ", species,
    "\nBill Length (mm): ", bill_length_mm,
    "\nBill Depth (mm): ", bill_depth_mm,
    "\nFlipper Length (mm): ",flipper_length_mm,
    "\nBody Mass (g): ", body_mass_g, sep=""
  ))
p <- ggplot(penguins) +
  geom_point(aes(x = bill_length_mm, 
                 y = bill_depth_mm, 
                 color = species,
                 text = text)) +
  theme(legend.position = "none")
ggplotly(p, tooltip="text")
```

#### 3.5.3 More advanced customization

Adjust the tooltip based on the following requests.

1.  Use bold text to make the tooltip more readable

2.  Add a title to the tooltip

3.  Change the background color of the tooltip to white

4.  Change the font color of the tooltip to black

```{r}
penguins <- penguins %>%
  mutate(text = paste(
    "<span style='font-size:16px;'><b>", species, "</b></span><br>",
    "<br><b>Island:</b> ", island,
    "<br><b>Bill Length (mm):</b> ", bill_length_mm,
    "<br><b>Bill Depth (mm):</b> ", bill_depth_mm,
    "<br><b>Flipper Length (mm):</b> ",flipper_length_mm,
    "<br><b>Body Mass (g):</b> ", body_mass_g, 
    sep=""
  ))
p <- ggplot(penguins) +
  geom_point(aes(x = bill_length_mm, 
                 y = bill_depth_mm, 
                 color = species,
                 text = text)) +
  theme(legend.position = "none")
pp <- ggplotly(p, tooltip="text")
pp <- pp %>%
  layout(
    hoverlabel = list(bgcolor = "white",
                      font = list(size = 12, color = "black"))
  )
pp
```

### 3.6 Animated plot - gganimate

Using gganimate to create animated plots helps visualize changes over time, making it easier to understand dynamic patterns and trends in data. It adds an extra layer of clarity by showing how variables evolve, which can be especially useful for time-series data.

```{r}
# Create the plot
plot <- ggplot(gapminder, aes(gdpPercap, lifeExp, size = pop, colour = country)) +
  geom_point(alpha = 0.7, show.legend = FALSE) +
  scale_colour_manual(values = country_colors) +
  scale_size(range = c(2, 12)) +
  scale_x_log10() +
  facet_wrap(~continent) +
  # Here comes the gganimate specific bits
  labs(title = 'Year: {frame_time}', x = 'GDP per capita', y = 'life expectancy') +
  transition_time(year) +
  ease_aes('linear')

# Save at gif:
animate(plot, width = 500, height = 357, renderer = gifski_renderer())
anim_save("output.gif")
```

### 3.7 Shiny app

Please open the "shiny app" folder.
