W5
Intermediate 3 sessions • 6 hours R

Week 5: EDA and ggplot2 Visualisation

.R
Follow along in RStudioDownload the Week 5 R script — every example ready to run with Ctrl+Enter.
Download R Script

See your data: Understanding patterns and relationships

Week 5 Overview

Exploratory Data Analysis (EDA) is your first step after cleaning. GOAL: Understand data before analysis

  • What are the patterns?
  • What are the relationships?
  • Are there anomalies?
  • What hypotheses to test?

EXPLORATORY VS CONFIRMATORY:

  • EDA: Data-driven, visual, exploratory
  • Confirmatory: Hypothesis-driven, statistical

WORKFLOW:

  • Univariate analysis (one variable at a time)
  • Bivariate analysis (two variables)
  • Multivariate analysis (multiple variables)
  • Create visualizations
  • Generate hypotheses

WHY VISUALIZATION? "The simple graph has brought more information to the data analyst's mind than any other device" - John Tukey Visualizations:

  • Show patterns humans can see
  • Reveal outliers and anomalies
  • Communicate findings to others
  • Are more memorable than tables

GGPLOT2: Industry-standard visualization package

  • "Grammar of Graphics"
  • Builds plots layer by layer
  • Flexible and powerful
  • Beautiful defaults
  • Reproducible

THIS WEEK YOU'LL LEARN: ✓ Univariate analysis (distribution, summary stats) ✓ Bivariate analysis (relationships) ✓ Multivariate analysis (complex patterns) ✓ ggplot2 syntax and layers ✓ Common plot types ✓ Customization and themes ✓ Communicating with visualizations

SESSION 1: Univariate and Bivariate Analysis

Duration: 2 hours

1.1 Understanding Distributions and Relationships

UNIVARIATE ANALYSIS - ONE VARIABLE AT A TIME:

NUMERIC VARIABLES:

library(dplyr)

df <- tibble(
age = c(25, 28, 30, 125, 32, 29, 26, 31, 27, 28),
salary = c(50000, 55000, 60000, 200000, 58000, 52000, 51000, 62000, 54000, 57000)
)

# Central tendency
mean(df$age) # [1] 29.6
median(df$age) # [1] 28.5
mode(df$age) # Most frequent (if exists)

# Spread
sd(df$age) # Standard deviation
var(df$age) # Variance
IQR(df$age) # Interquartile range (Q3-Q1)

# Range
min(df$age) # [1] 25
max(df$age) # [1] 125
range(df$age) # [1] 25 125

# Quantiles
quantile(df$age) # 0%, 25%, 50%, 75%, 100%
quantile(df$age, c(0.1, 0.9)) # Custom quantiles

# Complete summary
summary(df$age)
# Min. 1st Qu. Median Mean 3rd Qu. Max.
# 25.0 27.8 28.5 29.6 30.8 125.0

# Skewness and Kurtosis
library(moments)
skewness(df$age) # [-2, 2] normal, <-2 left-skewed, >2 right-skewed
kurtosis(df$age) # >0 more peaked, <0 flatter than normal

# Professional summary
df |>
summarize(
n = n(),
mean = mean(age),
median = median(age),
sd = sd(age),
min = min(age),
max = max(age),
missing = sum(is.na(age))
)

CATEGORICAL VARIABLES:

df <- tibble(
department = c("Sales", "IT", "Sales", "HR", "IT", "Sales", "HR", "Sales", "IT", "Sales"),
level = c("Junior", "Senior", "Mid", "Mid", "Junior", "Mid", "Senior", "Junior", "Senior", "Mid")
)

# Frequency tables
table(df$department)
# HR IT Sales
# 2 3 5

# Proportions
table(df$department) / nrow(df)
# HR IT Sales
# 0.20 0.30 0.50

# Cross-tabulation (two variables)
table(df$department, df$level)
# Junior Mid Senior
# HR 0 1 1
# IT 1 0 2
# Sales 2 2 1

# With dplyr
df |>
group_by(department) |>
summarize(count = n(), percent = n() / nrow(df))

BIVARIATE ANALYSIS - TWO VARIABLES:

NUMERIC vs NUMERIC:

# Correlation
cor(df$age, df$salary) # [1] 0.95 (strong positive)
cor(df$age, df$salary, method="spearman") # Rank correlation

# Correlation matrix
cor(df[, c("age", "salary")])

# Covariance
cov(df$age, df$salary)

# More robust correlation (if outliers)
library(MASS)
huber(df$age, df$salary)

NUMERIC vs CATEGORICAL:

df <- tibble(
salary = c(50000, 55000, 60000, 200000, 58000, 52000, 51000, 62000, 54000, 57000),
department = c("Sales", "IT", "Sales", "IT", "Sales", "HR", "Sales", "IT", "HR", "Sales")
)

# Compare groups
df |>
group_by(department) |>
summarize(
n = n(),
mean_salary = mean(salary),
median_salary = median(salary),
sd_salary = sd(salary),
min_salary = min(salary),
max_salary = max(salary)
)

# Output:
# department n mean_salary median_salary sd_salary min_salary max_salary
# HR 2 56500 56500 7778. 51000 62000
# IT 3 105667 200000 82789 55000 200000
# Sales 5 55000 54000 4243. 50000 60000

# Statistical test (covered in Week 6)
t.test(salary ~ department, df)

CATEGORICAL vs CATEGORICAL:

df <- tibble(
product = c("A", "A", "B", "B", "C", "A", "B", "C", "A", "B"),
purchase = c("Yes", "Yes", "No", "Yes", "Yes", "No", "Yes", "No", "Yes", "Yes")
)

# Contingency table
table(df$product, df$purchase)
# No Yes
# A 1 3
# B 1 3
# C 1 1

# Proportions by row
prop.table(table(df$product, df$purchase), margin=1)
# Shows: Of product A, 75% purchased (3/4)

1.2 Introduction to ggplot2

GGPLOT2 - GRAMMAR OF GRAPHICS:

Philosophy: Build plot layer by layer

BASIC SYNTAX:

ggplot(data, aes(x = var1, y = var2)) +
geom_type() +
labs(title = "...", x = "...", y = "...") +
theme_minimal()

LAYERS:

  • Data (what data to plot)
  • Aesthetics (aes: what maps to x, y, color, etc.)
  • Geometry (geom: what shape - point, line, bar, etc.)
  • Statistics (stat: calculations)
  • Scales (color, size ranges)
  • Coordinates (coordinate system)
  • Themes (overall look)

EXAMPLE:

library(ggplot2)

df <- tibble(
x = 1:10,
y = c(2, 3, 5, 7, 11, 13, 17, 19, 23, 29)
)

# Basic scatter plot
ggplot(df, aes(x = x, y = y)) +
geom_point()

# Add title and labels
ggplot(df, aes(x = x, y = y)) +
geom_point() +
labs(title = "My First ggplot",
x = "X Variable",
y = "Y Variable")

# Change point size and color
ggplot(df, aes(x = x, y = y)) +
geom_point(size = 3, color = "blue")

# Add line
ggplot(df, aes(x = x, y = y)) +
geom_point(size = 3) +
geom_line(color = "blue")

# Add smooth curve (trend line)
ggplot(df, aes(x = x, y = y)) +
geom_point() +
geom_smooth(method = "lm", se = FALSE) # se=FALSE removes confidence band

COMMON PLOT TYPES:

HISTOGRAM (Distribution of numeric):

ggplot(df, aes(x = age)) +
geom_histogram(bins = 20, fill = "steelblue") +
labs(title = "Age Distribution",
x = "Age", y = "Count")

BOX PLOT (Comparing groups):

ggplot(df, aes(x = department, y = salary)) +
geom_boxplot(fill = "lightblue") +
labs(title = "Salary by Department",
x = "Department", y = "Salary")

SCATTER PLOT (Two numeric variables):

ggplot(df, aes(x = age, y = salary)) +
geom_point(size = 3, color = "blue") +
labs(title = "Age vs Salary")

BAR PLOT (Categorical counts):

df |>
count(department) |>
ggplot(aes(x = department, y = n)) +
geom_bar(stat = "identity", fill = "coral") +
labs(title = "Count by Department",
x = "Department", y = "Count")

COLOR AND AESTHETICS:

# Color by group
ggplot(df, aes(x = age, y = salary, color = department)) +
geom_point(size = 3)

# Size by variable
ggplot(df, aes(x = age, y = salary, size = years_employed)) +
geom_point(color = "blue")

# Facet (small multiples) - separate panels
ggplot(df, aes(x = age, y = salary)) +
geom_point() +
facet_wrap(~department) # One panel per department

THEMES AND CUSTOMIZATION:

# Built-in themes
theme_minimal() # Clean, minimal
theme_classic() # Classic with axes
theme_bw() # Black and white
theme_dark() # Dark background

# Apply theme
ggplot(df, aes(x = age, y = salary)) +
geom_point() +
theme_minimal() +
theme(text = element_text(size = 12))

# Full customization
ggplot(df, aes(x = age, y = salary, color = department)) +
geom_point(size = 3) +
labs(title = "Professional Scatter Plot",
x = "Age (years)",
y = "Annual Salary ($)",
color = "Department") +
theme_minimal() +
theme(
plot.title = element_text(size = 14, face = "bold"),
axis.text = element_text(size = 10),
legend.position = "right"
)