R: R Descriptive Statistics
Last updated: 2026-08-26
In the previous 22 lessons, we learned about R’s “tools”—vectors, data frames, and ggplot2. Now we’ll move on to R’s true “value”—statistical analysis. In this lesson, we’ll start with the most basic concept: “descriptive statistics.” We’ll use R to calculate key metrics such as the mean, median, and standard deviation, and “summarize” the data into a few numbers.
After completing this lesson, you'll be able to generate a "statistical report" for any dataset using just three lines of R code.
1. What You'll Learn
- Descriptive statistics: 3 major categories—measure of central tendency, measure of dispersion, and distribution shape
- Measures of Central Tendency: mean/median/mode
- Dispersion: sd/var/IQR/range/mad
- Quantiles: quantile/median/Q1/Q3
- summary() / skimr: Quick Statistics
- dplyr + group_by: A Guide to Grouping
- Hands-On: 1,000-Line Student Grade Report
2. The Story of a Student’s Report Card
(1) Challenge: How do you "summarize" 1,000 students?
At the end of the semester, Professor Bob needs to generate grade reports for 1,000 students. Each student is taking 5 courses, and for each course, he needs to calculate:
- Mean, Median (Measures of Central Tendency)
- Standard deviation, quartiles (measures of dispersion)
- Highest score, lowest score (extreme values)
- Skewness, Kurtosis (Distribution Shape)
Manual calculations? Excel formulas? That takes 2 hours; using R summary()——
(2) Solution using R
# 1. One line summary shows 5 key metrics
summary(scores$math)
# Min. 1st Qu. Median Mean 3rd Qu. Max.
# 45.0 72.0 82.0 81.5 92.0 99.0
# 2. One line skimr View the full report
library(skimr)
skim(scores$math)
# ── Variable type: numeric ──
# min max mean sd p25 p50 p75 hist
# 45 99 81.5 12 72 82 92 ▇▇▇▇▇
# 3. One line dplyr Grouped Statistics
scores |> group_by(class) |> summarise(
Average Score = mean(math),
Median = median(math),
Standard Deviation = sd(math),
Highest = max(math),
Lowest = min(math),
Number of people = n()
)
3 lines of code → a complete grade report.
3. Three Major Categories of Descriptive Statistics
(1) Overview of the Three Major Categories
graph TB
A[Descriptive Statistics] --> B[Central Tendency<br/>Central Tendency]
A --> C[Degree of dispersion<br/>Dispersion]
A --> D[Distribution Patterns<br/>Shape]
B --> E[mean Median mode]
C --> F[sd var range IQR]
D --> G[Skewness skewness<br/>Fengdu kurtosis]
style A fill:#fff3cd
style B fill:#d4edda
style C fill:#cce5ff
style D fill:#f8d7da
(2) Quick Reference Table
| Category | Function | Meaning |
|---|---|---|
| Central Tendency | mean() |
Arithmetic Mean |
| Central Tendency | median() |
Median (50th percentile) |
| Centralized | mode() |
Mode (requires DescTools package) |
| Discrete | sd() |
Standard Deviation |
| Discrete | var() |
Variance |
| Discrete | range() |
Extreme Values (min/max) |
| Discrete | IQR() |
Interquartile Range (Q3-Q1) |
| Discrete | mad() |
Median Absolute Deviation |
| Distribution | skewness() |
Skewness (requires e1071) |
| Distribution | kurtosis() |
Kurtosis (requires e1071) |
4. Central Tendency
(1) mean() Mean
x <- c(85, 90, 78, 92, 88)
mean(x) # [1] 86.6
mean(x, na.rm = TRUE) # Skip NA
mean(x, trim = 0.1) # Truncated Mean (Remove the top and bottom 10%)
(2) median() Median
median(c(85, 90, 78, 92, 88)) # [1] 88
median(c(85, 90, 78, 92, 10000)) # [1] 90 ← Not affected by 10000 Impact
(3) Mode (Special Functions)
# R does not have a built-in mode(), use the DescTools package
install.packages("DescTools")
library(DescTools)
Mode(c(1, 2, 2, 3, 3, 3, 4)) # [1] 3
5. Degree of Dispersion
(1) sd() / var() Standard Deviation / Variance
x <- c(85, 90, 78, 92, 88)
sd(x) # [1] 5.32 ← Standard Deviation
var(x) # [1] 28.3 <- Variance (sd^2)
Standard Deviation = the "average distance" of data points from the mean. The larger the value, the more scattered the data is.
(2) Extreme Values of range()
x <- c(85, 90, 78, 92, 88)
range(x) # [1] 78 92 ← Minimum and Maximum
diff(range(x)) # [1] 14 ← Range
(3) IQR() Interquartile Range
x <- c(85, 90, 78, 92, 88, 70, 95, 65, 88, 92)
IQR(x) # [1] 11.5 ← Q3 - Q1
quantile(x, c(0.25, 0.5, 0.75))
# 25% 50% 75%
# 78.25 88.0 89.75
Q1 - 1.5*IQR ~ Q3 + 1.5*IQR).
(4) Absolute deviation of the median in mad()
mad(c(85, 90, 78, 92, 10000)) # Extremely Robust Discrete Measures
# [1] 4.45
6. Quantiles
(1) quantile() Quantile
x <- 1:100
# Default 0%, 25%, 50%, 75%, 100%
quantile(x)
# 0% 25% 50% 75% 100%
# 1.0 25.75 50.5 75.25 100.0
# Custom Percentiles
quantile(x, probs = c(0.1, 0.5, 0.9))
# 10% 50% 90%
# 10.9 50.5 90.1
(2) Common Percentiles
| Percentile | Function | Meaning |
|---|---|---|
| Q0 (0%) | min(x) |
Minimum |
| Q1 (25%) | quantile(x, 0.25) |
Lower quartile |
| Q2 (50%) | median(x) |
Median |
| Q3 (75%) | quantile(x, 0.75) |
Upper quartile |
| Q4 (100%) | max(x) |
Maximum |
7. summary()—A Single Line of Summary Statistics
x <- c(85, 90, 78, 92, 88, NA, 75, 95)
summary(x)
# Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
# 75.00 81.50 88.00 87.62 90.50 95.00 1
6 Key Metrics + NA Count—All in One Line!
# Data Frame
df <- data.frame(
age = c(20, 25, 30, 35),
score = c(85, 90, 78, 92)
)
summary(df)
# age score
# Min. :20.00 Min. :78.00
# 1st Qu.:23.75 1st Qu.:83.25
# Median :27.50 Median :87.50
# Mean :27.50 Mean :86.25
# 3rd Qu.:31.25 3rd Qu.:90.50
# Max. :35.00 Max. :92.00
8. skimr: Advanced Comprehensive Statistics
install.packages("skimr")
library(skimr)
x <- c(85, 90, 78, 92, 88, 70, 95, 65, 88, 92)
skim(x)
# ── Data Summary ────────────────────────
# Values x
# Number of rows 10
# Number of distinct 8
# Mean 84.2
# Standard deviation 10.13
# Min 65
# Max 95
# Median 87.5
# ... (More Metrics)
skim() Provides over 20 metrics—10 times more detailed than summary().
9. Descriptive Statistics by Group
(1) dplyr + group_by + summarise
library(dplyr)
# Simulation 3 Cls × 5 Student Grades
scores <- tibble(
class = rep(c("1Cls", "2Cls", "3Cls"), each = 5),
student = paste0("S", 1:15),
math = c(85, 78, 92, 65, 88, # 1 Cls
90, 75, 80, 95, 70, # 2 Cls
82, 88, 76, 91, 85), # 3 Cls
english = c(78, 85, 88, 70, 92,
82, 80, 90, 88, 75,
88, 90, 78, 92, 85)
)
# Class Statistics
class_stats <- scores |>
group_by(class) |>
summarise(
Number of people = n(),
Mathematical Average = mean(math),
Median in Mathematics = median(math),
Mathematical Standard Deviation = sd(math),
Top in Math = max(math),
Lowest in Math = min(math),
MathematicsIQR = IQR(math),
Average English Score = mean(english)
)
print(class_stats)
# A tibble: 3 × 9
# class Number of people Mathematical Average Median in Mathematics Mathematical Standard Deviation Top in Math Lowest in Math MathematicsIQR Average English Score
# <chr> <int> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
# 1 1Cls 5 81.6 85 11.4 92 65 17 82.6
# 2 2Cls 5 82 80 10.5 95 70 20 83
# 3 3Cls 5 84.4 85 6.02 91 76 12 86.6
(2) Multi-Indicator Breakdown (across)
# Calculate statistics for all numeric columns at once
scores |>
group_by(class) |>
summarise(across(where(is.numeric), list(
mean = mean,
sd = sd,
median = median
)))
(3) Old syntax for tapply (for reference)
# Use tapply
tapply(scores$math, scores$class, mean)
# 1Cls 2Cls 3Cls
# 81.6 82.0 84.4
10. Hands-On Exercise: Comprehensive Grade Report for 1,000 Students
Below is an example of a complete workflow that ties together all the descriptive statistics covered in this lesson.
▶ Example: Comprehensive Report on 1,000 Students and 5 Courses
# ============================================
# 1000 Student 5 Course Comprehensive Grade Report
# Features: Complete Descriptive Statistics + Grouping + Ranking
# ============================================
library(dplyr)
library(tidyr)
library(ggplot2)
# 1. Prepare data
set.seed(42)
n <- 1000
students <- tibble(
id = 1:n,
class = sample(c("1Cls", "2Cls", "3Cls", "4Cls", "5Cls"), n, replace = TRUE),
gender = sample(c("M", "F"), n, replace = TRUE),
math = round(rnorm(n, 80, 12)),
english = round(rnorm(n, 78, 15)),
physics = round(rnorm(n, 75, 14)),
chemistry = round(rnorm(n, 76, 13)),
biology = round(rnorm(n, 80, 10))
)
cat("=== Data Volume:", nrow(students), "rows ===\n")
# 2. University-wide Comprehensive Statistics
cat("\n=== University-wide Comprehensive Statistics ===\n")
overall <- students |>
summarise(across(c(math, english, physics, chemistry, biology),
list(mean = mean, sd = sd, median = median),
.names = "{.col}_{.fn}"))
print(overall)
# 3. Statistics by Class
cat("\n=== Math Statistics for Each Class ===\n")
class_stats <- students |>
group_by(class) |>
summarise(
Number of people = n(),
Average = round(mean(math), 2),
Median = median(math),
Standard Deviation = round(sd(math), 2),
Highest = max(math),
Lowest = min(math),
Q1 = quantile(math, 0.25),
Q3 = quantile(math, 0.75),
IQR = round(IQR(math), 2)
) |>
arrange(desc(Average))
print(class_stats)
# 4. Grouped by gender
cat("\n=== All genders 5 Class Average ===\n")
gender_stats <- students |>
group_by(gender) |>
summarise(across(c(math, english, physics, chemistry, biology),
mean, .names = "{.col}_Average"))
print(gender_stats)
# 5. Each Student's Total Score and Rank
cat("\n=== Top 10 Students (Total Score) ===\n")
students <- students |>
mutate(total = math + english + physics + chemistry + biology,
average = round(total / 5, 2))
top10 <- students |>
arrange(desc(total)) |>
head(10) |>
select(id, class, gender, total, average)
print(top10)
# 6. Identify the outliers (IQR method)
cat("\n=== Students with Outliers in Math Scores ====\n")
math_q1 <- quantile(students$math, 0.25)
math_q3 <- quantile(students$math, 0.75)
math_iqr <- IQR(students$math)
lower <- math_q1 - 1.5 * math_iqr
upper <- math_q3 + 1.5 * math_iqr
outliers <- students |>
filter(math < lower | math > upper) |>
select(id, class, math)
cat("Normal Range:", lower, "-", upper, "\n")
cat("Number of outliers:", nrow(outliers), "\n")
print(head(outliers, 5))
# 7. Use summary() to view by subject
cat("\n=== Mathematics summary ===\n")
print(summary(students$math))
# 8. Use skimr for advanced report
library(skimr)
cat("\n=== skimr Report ===\n")
print(skim(students |> select(math, english, physics)))
# 9. Write a report
write_csv(students, "student_report.csv")
write_csv(class_stats, "class_stats.csv")
cat("\n=== The report has been generated ===\n")
Expected Output (Excerpt):
=== Math Statistics for Each Class ===
# A tibble: 5 × 9
class Number of people Average Median Standard Deviation Highest Lowest Q1 Q3 IQR
<chr> <int> <dbl> <dbl> <dbl> <int> <int> <dbl> <dbl> <dbl>
1 5Cls 201 81.0 81 12.0 114 44 72 90 18
2 1Cls 195 80.6 81 11.6 115 47 73 88 15
3 3Cls 203 79.8 80 11.8 110 49 71 88 17
4 2Cls 201 79.4 80 12.1 109 44 70 88 18
5 4Cls 200 79.1 79 12.4 116 45 70 89 19
❓ FAQ
summary() or skim()?summary is concise (6 metrics), while skim is detailed (20+ metrics).na.rm = TRUE to all functions to skip NA. Otherwise, the results for vectors containing NA will all be NA.e1071::skewness() and e1071::kurtosis(). If skewness is > 0, the distribution is right-skewed; if < 0, it is left-skewed. If kurtosis is > 0, the distribution is more peaked than a normal distribution; if < 0, it is flatter.📖 Summary
- Descriptive statistics are divided into three major categories: Central Tendency (mean/median/mode), Measure of Dispersion (sd/var/IQR), and Distribution Shape (skewness/kurtosis)
- The mean is influenced by outliers, while the median is more robust—for skewed distributions, use the median first
- sd is the standard deviation in the same units as the original data, and var is sd²
- IQR = Q3 - Q1, a robust measure of dispersion commonly used for outlier detection
summary(x)6 metrics per row, most commonly used;skim(x)20+ metrics, detailed report- Group Description:
dplyr::group_by() |> summarise(across(where(is.numeric), mean)) - NA Processing: Add
na.rm = TRUEto all functions - Outliers: < Q1 - 1.5×IQR or > Q3 + 1.5×IQR (robust)
📝 Exercises
-
Basic Problem: Construct a vector
x <- c(85, 90, 78, 92, 88, NA, 75, 95), compute the values usingmean(),median(),sd(), andvar()(notena.rm = TRUE), and verify the 8 results. -
Basic Exercise: Use
summary()andquantile(x, c(0, 0.25, 0.5, 0.75, 1))to calculate the statistical metrics for the same vector, and compare the two results. -
Basic Problem: Create a data frame (3 classes × 5 students × 2 subjects: Math and English), and use
dplyr::group_by + summarise + acrossto calculate the mean and standard deviation for all subjects in all classes in a single operation. -
Advanced Exercise: Simulate the grades of 1,000 students in 5 courses, use
skimr::skim()to generate a complete report, identify outliers in math scores (using the IQR method), and calculate the percentage of students with outlier scores. -
Challenge: Complete workflow—simulate the grades for 1,000 students across 5 courses: ① Overall description ② Description grouped by class ③ Description grouped by gender ④ Ranking ⑤ Outlier detection ⑥ Generate a CSV report + a Markdown text report (use
knitr::kable()to render tables). Save screenshots of the process.