本节学习 4 类常见图形:
主要使用两个包:
ggplot2
dplyr
其中:
ggplot2 用来画图;dplyr 用来整理数据、计算均值、计数等。如果还没有安装,可以先运行:
install.packages("ggplot2")
install.packages("dplyr")
加载包:
library(ggplot2)
library(dplyr)
##
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
##
## filter, lag
## The following objects are masked from 'package:base':
##
## intersect, setdiff, setequal, union
我们创建一份学生成绩数据。
set.seed(123)
student <- data.frame(
id = 1:120,
class = rep(c("一班", "二班", "三班"), each = 40),
gender = sample(c("男", "女"), 120, replace = TRUE),
group = sample(c("实验组", "对照组"), 120, replace = TRUE),
score = c(
rnorm(40, mean = 78, sd = 8),
rnorm(40, mean = 84, sd = 7),
rnorm(40, mean = 88, sd = 6)
)
)
student$score <- round(student$score, 1)
student$class <- factor(
student$class,
levels = c("一班", "二班", "三班"),
ordered = TRUE
)
student
## id class gender group score
## 1 1 一班 男 实验组 78.9
## 2 2 一班 男 对照组 70.4
## 3 3 一班 男 实验组 74.1
## 4 4 一班 女 实验组 76.0
## 5 5 一班 男 实验组 92.8
## 6 6 一班 女 对照组 72.8
## 7 7 一班 女 对照组 79.9
## 8 8 一班 女 实验组 78.6
## 9 9 一班 男 实验组 70.3
## 10 10 一班 男 实验组 77.4
## 11 11 一班 女 对照组 89.6
## 12 12 一班 女 实验组 81.6
## 13 13 一班 女 对照组 78.3
## 14 14 一班 男 对照组 74.6
## 15 15 一班 女 对照组 61.6
## 16 16 一班 男 对照组 87.1
## 17 17 一班 女 对照组 66.3
## 18 18 一班 男 对照组 83.9
## 19 19 一班 男 对照组 93.3
## 20 20 一班 男 实验组 66.4
## 21 21 一班 男 对照组 83.6
## 22 22 一班 女 对照组 75.9
## 23 23 一班 男 对照组 65.4
## 24 24 一班 男 实验组 65.9
## 25 25 一班 男 对照组 65.2
## 26 26 一班 男 对照组 73.8
## 27 27 一班 女 对照组 66.3
## 28 28 一班 女 实验组 83.5
## 29 29 一班 男 对照组 94.8
## 30 30 一班 女 对照组 67.7
## 31 31 一班 男 实验组 84.3
## 32 32 一班 女 对照组 84.2
## 33 33 一班 男 实验组 80.7
## 34 34 一班 女 对照组 69.9
## 35 35 一班 女 实验组 77.0
## 36 36 一班 男 实验组 75.8
## 37 37 一班 男 实验组 82.5
## 38 38 一班 男 实验组 75.0
## 39 39 一班 男 实验组 85.8
## 40 40 一班 女 实验组 75.0
## 41 41 二班 男 实验组 91.4
## 42 42 二班 女 对照组 76.7
## 43 43 二班 女 对照组 75.2
## 44 44 二班 男 对照组 106.7
## 45 45 二班 男 实验组 81.1
## 46 46 二班 男 对照组 86.1
## 47 47 二班 男 实验组 88.5
## 48 48 二班 女 实验组 80.6
## 49 49 二班 男 对照组 87.6
## 50 50 二班 男 实验组 86.6
## 51 51 二班 女 对照组 82.5
## 52 52 二班 男 对照组 84.5
## 53 53 二班 男 对照组 83.8
## 54 54 二班 男 对照组 98.9
## 55 55 二班 男 实验组 78.8
## 56 56 二班 女 实验组 76.3
## 57 57 二班 女 实验组 84.3
## 58 58 二班 男 对照组 86.2
## 59 59 二班 女 对照组 87.1
## 60 60 二班 男 对照组 80.8
## 61 61 二班 男 对照组 76.6
## 62 62 二班 女 实验组 92.8
## 63 63 二班 女 实验组 81.6
## 64 64 二班 男 实验组 77.9
## 65 65 二班 男 实验组 82.3
## 66 66 二班 女 对照组 82.6
## 67 67 二班 男 对照组 91.8
## 68 68 二班 男 实验组 84.6
## 69 69 二班 男 实验组 89.3
## 70 70 二班 男 对照组 80.5
## 71 71 二班 女 对照组 85.5
## 72 72 二班 男 实验组 81.7
## 73 73 二班 男 实验组 84.7
## 74 74 二班 男 对照组 77.7
## 75 75 二班 男 对照组 74.8
## 76 76 二班 女 实验组 98.0
## 77 77 二班 女 实验组 88.2
## 78 78 二班 男 对照组 75.2
## 79 79 二班 女 实验组 79.7
## 80 80 二班 女 对照组 75.7
## 81 81 三班 女 对照组 101.2
## 82 82 三班 女 对照组 95.9
## 83 83 三班 男 对照组 86.4
## 84 84 三班 女 实验组 91.3
## 85 85 三班 女 对照组 85.5
## 86 86 三班 女 对照组 85.1
## 87 87 三班 男 对照组 83.3
## 88 88 三班 男 实验组 84.4
## 89 89 三班 女 对照组 97.9
## 90 90 三班 男 对照组 87.7
## 91 91 三班 女 对照组 88.7
## 92 92 三班 女 实验组 89.5
## 93 93 三班 男 实验组 95.4
## 94 94 三班 女 对照组 84.9
## 95 95 三班 女 对照组 82.0
## 96 96 三班 男 对照组 98.1
## 97 97 三班 男 对照组 85.4
## 98 98 三班 女 实验组 83.7
## 99 99 三班 男 对照组 80.6
## 100 100 三班 男 实验组 80.3
## 101 101 三班 男 对照组 84.6
## 102 102 三班 女 实验组 91.7
## 103 103 三班 女 实验组 94.7
## 104 104 三班 男 实验组 92.2
## 105 105 三班 女 对照组 85.8
## 106 106 三班 女 对照组 88.4
## 107 107 三班 男 实验组 83.8
## 108 108 三班 男 实验组 83.7
## 109 109 三班 男 对照组 93.3
## 110 110 三班 女 实验组 81.9
## 111 111 三班 女 实验组 99.7
## 112 112 三班 女 对照组 87.5
## 113 113 三班 女 对照组 89.3
## 114 114 三班 男 实验组 83.6
## 115 115 三班 男 对照组 84.6
## 116 116 三班 女 对照组 80.1
## 117 117 三班 女 对照组 86.9
## 118 118 三班 女 对照组 90.5
## 119 119 三班 男 对照组 89.9
## 120 120 三班 女 实验组 83.3
查看数据结构:
str(student)
## 'data.frame': 120 obs. of 5 variables:
## $ id : int 1 2 3 4 5 6 7 8 9 10 ...
## $ class : Ord.factor w/ 3 levels "一班"<"二班"<..: 1 1 1 1 1 1 1 1 1 1 ...
## $ gender: chr "男" "男" "男" "女" ...
## $ group : chr "实验组" "对照组" "实验组" "实验组" ...
## $ score : num 78.9 70.4 74.1 76 92.8 72.8 79.9 78.6 70.3 77.4 ...
简单查看每个班人数:
table(student$class)
##
## 一班 二班 三班
## 40 40 40
查看性别人数:
table(student$gender)
##
## 男 女
## 65 55
ggplot2 的基本结构是:
ggplot(data = 数据框, aes(x = 横轴变量, y = 纵轴变量)) +
geom_图形类型()
例如:
ggplot(data = student, aes(x = class, y = score)) +
geom_boxplot()
其中:
| 部分 | 含义 |
|---|---|
ggplot() |
创建一个图 |
data = student |
使用 student 数据 |
aes() |
设置横轴、纵轴、颜色、分组等映射 |
geom_boxplot() |
画盒形图 |
+ |
继续添加图层 |
注意:
ggplot2 里面添加图层使用 +,不是
%>%。
均值条形图用于比较不同组的平均值。
比如:
比较一班、二班、三班的平均成绩。
它的横轴通常是分组变量,纵轴是均值。
画均值条形图之前,推荐先把每组均值算出来。
class_mean <- student %>%
group_by(class) %>%
summarise(
mean_score = mean(score),
sd_score = sd(score),
n = n(),
.groups = "drop"
)
class_mean
## # A tibble: 3 × 4
## class mean_score sd_score n
## <ord> <dbl> <dbl> <int>
## 1 一班 77.2 8.39 40
## 2 二班 84.1 6.97 40
## 3 三班 88.1 5.55 40
解释:
| 代码 | 含义 |
|---|---|
group_by(class) |
按班级分组 |
summarise() |
每组汇总 |
mean(score) |
计算平均分 |
sd(score) |
计算标准差 |
n() |
计算人数 |
ggplot(class_mean, aes(x = class, y = mean_score)) +
geom_col()
解释:
geom_col()
表示:
直接使用数据中的 y 值画柱子。
这里 mean_score 已经算好了,所以用
geom_col()。
ggplot(class_mean, aes(x = class, y = mean_score, fill = class)) +
geom_col(width = 0.6) +
labs(
title = "不同班级的平均成绩",
x = "班级",
y = "平均成绩"
) +
theme_minimal()
解释:
| 参数 | 含义 |
|---|---|
fill = class |
按班级填充颜色 |
width = 0.6 |
柱子的宽度 |
labs() |
设置标题和坐标轴名称 |
theme_minimal() |
使用简洁主题 |
ggplot(class_mean, aes(x = class, y = mean_score, fill = class)) +
geom_col(width = 0.6) +
geom_text(
aes(label = round(mean_score, 1)),
vjust = -0.5
) +
labs(
title = "不同班级的平均成绩",
x = "班级",
y = "平均成绩"
) +
ylim(0, 100) +
theme_minimal()
解释:
geom_text()
用来添加文字标签。
vjust = -0.5
表示标签放在柱子上方。
误差线常用来表示标准差或标准误。
这里先计算标准误:
class_mean <- class_mean %>%
mutate(
se_score = sd_score / sqrt(n)
)
class_mean
## # A tibble: 3 × 5
## class mean_score sd_score n se_score
## <ord> <dbl> <dbl> <int> <dbl>
## 1 一班 77.2 8.39 40 1.33
## 2 二班 84.1 6.97 40 1.10
## 3 三班 88.1 5.55 40 0.878
画均值条形图 + 标准误误差线:
ggplot(class_mean, aes(x = class, y = mean_score, fill = class)) +
geom_col(width = 0.6) +
geom_errorbar(
aes(
ymin = mean_score - se_score,
ymax = mean_score + se_score
),
width = 0.2
) +
geom_text(
aes(label = round(mean_score, 1)),
vjust = -1
) +
labs(
title = "不同班级的平均成绩:均值 ± 标准误",
x = "班级",
y = "平均成绩"
) +
ylim(0, 100) +
theme_minimal()
解释:
geom_errorbar()
表示添加误差线。
ymin = mean_score - se_score
ymax = mean_score + se_score
表示误差线的下限和上限。
直方图用于查看连续变量的分布。
比如:
查看学生成绩主要集中在哪个范围。
横轴是分数区间,纵轴是人数或密度。
ggplot(student, aes(x = score)) +
geom_histogram()
## `stat_bin()` using `bins = 30`. Pick better value `binwidth`.
这会画出最基础的直方图。
但默认分箱可能不太好看,所以通常会设置:
binwidth
ggplot(student, aes(x = score)) +
geom_histogram(
binwidth = 5,
color = "white",
fill = "skyblue"
) +
labs(
title = "学生成绩直方图",
x = "成绩",
y = "人数"
) +
theme_minimal()
解释:
| 参数 | 含义 |
|---|---|
binwidth = 5 |
每 5 分一个区间 |
color = "white" |
柱子边框颜色 |
fill = "skyblue" |
柱子填充颜色 |
核密度曲线也是用来看连续变量分布的。
它比直方图更平滑。
ggplot(student, aes(x = score)) +
geom_density(
color = "red",
linewidth = 1
) +
labs(
title = "学生成绩核密度曲线",
x = "成绩",
y = "密度"
) +
theme_minimal()
如果想把直方图和核密度曲线放在一张图里,需要注意:
所以要把直方图纵轴也改成密度:
aes(y = after_stat(density))
完整代码:
ggplot(student, aes(x = score)) +
geom_histogram(
aes(y = after_stat(density)),
binwidth = 5,
color = "white",
fill = "skyblue",
alpha = 0.6
) +
geom_density(
color = "red",
linewidth = 1
) +
labs(
title = "学生成绩分布:直方图 + 核密度曲线",
x = "成绩",
y = "密度"
) +
theme_minimal()
解释:
| 代码 | 含义 |
|---|---|
after_stat(density) |
把直方图高度转换成密度 |
alpha = 0.6 |
设置透明度 |
geom_density() |
添加核密度曲线 |
ggplot(student, aes(x = score, color = class)) +
geom_density(linewidth = 1) +
labs(
title = "不同班级成绩的核密度曲线",
x = "成绩",
y = "密度",
color = "班级"
) +
theme_minimal()
ggplot(student, aes(x = score, fill = class)) +
geom_histogram(
binwidth = 5,
color = "white",
alpha = 0.7
) +
facet_wrap(~ class) +
labs(
title = "不同班级成绩直方图",
x = "成绩",
y = "人数"
) +
theme_minimal()
解释:
facet_wrap(~ class)
表示按照班级分面,每个班单独一张小图。
盒形图用于比较不同组的分布。
它可以显示:
盒形图特别适合回答:
哪个组的成绩更高? 哪个组的成绩波动更大? 有没有异常值?
ggplot(student, aes(x = class, y = score)) +
geom_boxplot()
ggplot(student, aes(x = class, y = score, fill = class)) +
geom_boxplot(width = 0.6, alpha = 0.7) +
labs(
title = "不同班级成绩盒形图",
x = "班级",
y = "成绩"
) +
theme_minimal()
有时候我们希望看到每个学生的真实分数,可以添加散点。
ggplot(student, aes(x = class, y = score, fill = class)) +
geom_boxplot(width = 0.6, alpha = 0.5, outlier.shape = NA) +
geom_jitter(
width = 0.15,
alpha = 0.5,
size = 1.5
) +
labs(
title = "不同班级成绩盒形图 + 散点",
x = "班级",
y = "成绩"
) +
theme_minimal()
解释:
| 代码 | 含义 |
|---|---|
geom_boxplot() |
画盒形图 |
geom_jitter() |
添加抖动散点 |
outlier.shape = NA |
不单独显示盒形图异常点,避免和散点重复 |
width = 0.15 |
控制散点左右抖动范围 |
小提琴图可以看作:
盒形图 + 密度分布形状
它可以展示不同组的数据分布形状。
小提琴越宽,说明该位置的数据越集中。
ggplot(student, aes(x = class, y = score)) +
geom_violin()
ggplot(student, aes(x = class, y = score, fill = class)) +
geom_violin(alpha = 0.7) +
labs(
title = "不同班级成绩小提琴图",
x = "班级",
y = "成绩"
) +
theme_minimal()
这是很常用的一种画法:
ggplot(student, aes(x = class, y = score, fill = class)) +
geom_violin(alpha = 0.6, trim = FALSE) +
geom_boxplot(width = 0.15, fill = "white", outlier.shape = NA) +
labs(
title = "不同班级成绩:小提琴图 + 盒形图",
x = "班级",
y = "成绩"
) +
theme_minimal()
解释:
| 代码 | 含义 |
|---|---|
geom_violin() |
画小提琴图 |
geom_boxplot(width = 0.15) |
在小提琴图中间加一个窄盒形图 |
trim = FALSE |
不截断密度尾部 |
fill = "white" |
盒形图填充白色 |
ggplot(student, aes(x = class, y = score, fill = class)) +
geom_violin(alpha = 0.5, trim = FALSE) +
geom_boxplot(width = 0.15, fill = "white", outlier.shape = NA) +
geom_jitter(
width = 0.12,
alpha = 0.4,
size = 1.3
) +
labs(
title = "不同班级成绩:小提琴图 + 盒形图 + 散点",
x = "班级",
y = "成绩"
) +
theme_minimal()
除了按班级,也可以按性别比较。
ggplot(student, aes(x = gender, y = score, fill = gender)) +
geom_boxplot(alpha = 0.7) +
labs(
title = "不同性别成绩盒形图",
x = "性别",
y = "成绩"
) +
theme_minimal()
ggplot(student, aes(x = gender, y = score, fill = gender)) +
geom_violin(alpha = 0.5, trim = FALSE) +
geom_boxplot(width = 0.15, fill = "white", outlier.shape = NA) +
labs(
title = "不同性别成绩小提琴图 + 盒形图",
x = "性别",
y = "成绩"
) +
theme_minimal()
分组条形图用于比较两个分类变量组合后的数量。
例如:
每个班级中,男生和女生分别有多少人?
这里有两个分类变量:
classgendertable(student$class, student$gender)
##
## 男 女
## 一班 23 17
## 二班 25 15
## 三班 17 23
ggplot(student, aes(x = class, fill = gender)) +
geom_bar(position = "dodge") +
labs(
title = "不同班级的性别人数分布",
x = "班级",
y = "人数",
fill = "性别"
) +
theme_minimal()
解释:
| 代码 | 含义 |
|---|---|
geom_bar() |
自动计数画条形图 |
fill = gender |
按性别填充颜色 |
position = "dodge" |
分组并排显示 |
注意:
geom_bar()
会自动统计每组数量。
如果不写 position = "dodge",默认是堆叠条形图。
ggplot(student, aes(x = class, fill = gender)) +
geom_bar() +
labs(
title = "不同班级的性别人数分布:堆叠条形图",
x = "班级",
y = "人数",
fill = "性别"
) +
theme_minimal()
如果想比较比例,可以用:
position = "fill"
ggplot(student, aes(x = class, fill = gender)) +
geom_bar(position = "fill") +
labs(
title = "不同班级的性别比例分布",
x = "班级",
y = "比例",
fill = "性别"
) +
theme_minimal()
解释:
position = "fill"
表示每个柱子高度都是 1,也就是 100%。
更推荐先计算人数,这样结果更清楚。
class_gender_count <- student %>%
count(class, gender)
class_gender_count
## class gender n
## 1 一班 女 17
## 2 一班 男 23
## 3 二班 女 15
## 4 二班 男 25
## 5 三班 女 23
## 6 三班 男 17
画图:
ggplot(class_gender_count, aes(x = class, y = n, fill = gender)) +
geom_col(position = "dodge", width = 0.7) +
labs(
title = "不同班级男女生人数",
x = "班级",
y = "人数",
fill = "性别"
) +
theme_minimal()
解释:
这里用的是:
geom_col()
因为 n 已经提前算好了。
ggplot(class_gender_count, aes(x = class, y = n, fill = gender)) +
geom_col(position = position_dodge(width = 0.7), width = 0.7) +
geom_text(
aes(label = n),
position = position_dodge(width = 0.7),
vjust = -0.4
) +
labs(
title = "不同班级男女生人数",
x = "班级",
y = "人数",
fill = "性别"
) +
ylim(0, max(class_gender_count$n) + 5) +
theme_minimal()
注意:
如果柱子用了:
position_dodge(width = 0.7)
标签也要用相同的
position_dodge(width = 0.7),否则标签位置可能对不上。
除了人数,也可以画:
每个班级中,男生和女生的平均成绩。
class_gender_mean <- student %>%
group_by(class, gender) %>%
summarise(
mean_score = mean(score),
sd_score = sd(score),
n = n(),
se_score = sd_score / sqrt(n),
.groups = "drop"
)
class_gender_mean
## # A tibble: 6 × 6
## class gender mean_score sd_score n se_score
## <ord> <chr> <dbl> <dbl> <int> <dbl>
## 1 一班 女 75.5 7.41 17 1.80
## 2 一班 男 78.3 9.02 23 1.88
## 3 二班 女 83.1 6.49 15 1.68
## 4 二班 男 84.7 7.30 25 1.46
## 5 三班 女 88.9 5.79 23 1.21
## 6 三班 男 86.9 5.14 17 1.25
ggplot(class_gender_mean, aes(x = class, y = mean_score, fill = gender)) +
geom_col(position = position_dodge(width = 0.7), width = 0.7) +
geom_errorbar(
aes(
ymin = mean_score - se_score,
ymax = mean_score + se_score
),
position = position_dodge(width = 0.7),
width = 0.2
) +
geom_text(
aes(label = round(mean_score, 1)),
position = position_dodge(width = 0.7),
vjust = -0.7
) +
labs(
title = "不同班级、不同性别的平均成绩",
x = "班级",
y = "平均成绩",
fill = "性别"
) +
ylim(0, 100) +
theme_minimal()
这个图很常见,适合表达:
不同班级中,男女生平均成绩是否不同。
可以手动设置颜色。
ggplot(class_gender_count, aes(x = class, y = n, fill = gender)) +
geom_col(position = "dodge", width = 0.7) +
scale_fill_manual(
values = c("女" = "#F8766D", "男" = "#00BFC4")
) +
labs(
title = "不同班级男女生人数",
x = "班级",
y = "人数",
fill = "性别"
) +
theme_minimal()
如果颜色已经很明显,有时候可以去掉图例。
ggplot(class_mean, aes(x = class, y = mean_score, fill = class)) +
geom_col(width = 0.6) +
labs(
title = "不同班级平均成绩",
x = "班级",
y = "平均成绩"
) +
theme_minimal() +
theme(legend.position = "none")
可以用 ggsave() 保存上一张图。
推荐先把图保存成对象:
p1 <- ggplot(class_mean, aes(x = class, y = mean_score, fill = class)) +
geom_col(width = 0.6) +
labs(
title = "不同班级平均成绩",
x = "班级",
y = "平均成绩"
) +
theme_minimal()
p1
保存图片:
ggsave(
filename = "class_mean_bar.png",
plot = p1,
width = 6,
height = 4,
dpi = 300
)
解释:
| 参数 | 含义 |
|---|---|
filename |
文件名 |
plot |
要保存的图 |
width |
图片宽度 |
height |
图片高度 |
dpi |
分辨率 |
这里设置 eval=FALSE,避免 Knit 时自动保存图片。
如果你已经有了 y 值,比如平均分:
mean_score
应该用:
geom_col()
如果你想让 ggplot 自动数人数,应该用:
geom_bar()
对比:
| 情况 | 用哪个 |
|---|---|
| 自动计数 | geom_bar() |
| 已经算好均值、人数、比例 | geom_col() |
错误示例:
ggplot(student, aes(x = score)) +
geom_histogram(binwidth = 5) +
geom_density()
这样直方图纵轴是人数,密度曲线纵轴是密度,二者量纲不一致。
正确写法:
ggplot(student, aes(x = score)) +
geom_histogram(
aes(y = after_stat(density)),
binwidth = 5,
fill = "skyblue",
color = "white",
alpha = 0.6
) +
geom_density(color = "red", linewidth = 1) +
theme_minimal()
如果柱子用了:
position_dodge(width = 0.7)
文字标签也要用同样的:
position_dodge(width = 0.7)
正确示例:
ggplot(class_gender_count, aes(x = class, y = n, fill = gender)) +
geom_col(position = position_dodge(width = 0.7), width = 0.7) +
geom_text(
aes(label = n),
position = position_dodge(width = 0.7),
vjust = -0.4
) +
theme_minimal()
如果报错:
could not find function "ggplot"
说明没有加载 ggplot2。
解决:
library(ggplot2)
如果图标题中文乱码,通常和系统字体有关。
可以先不用特别处理。如果需要指定字体,可以用额外包,比如
showtext,后面再学。
下面创建一个商品销售数据:
set.seed(456)
sales <- data.frame(
id = 1:150,
region = sample(c("华东", "华北", "华南"), 150, replace = TRUE),
category = sample(c("食品", "服装", "电子"), 150, replace = TRUE),
channel = sample(c("线上", "线下"), 150, replace = TRUE),
amount = round(rnorm(150, mean = 500, sd = 120), 1)
)
sales$region <- factor(
sales$region,
levels = c("华东", "华北", "华南"),
ordered = TRUE
)
sales
## id region category channel amount
## 1 1 华东 食品 线上 588.5
## 2 2 华东 食品 线下 809.4
## 3 3 华南 服装 线上 685.6
## 4 4 华北 电子 线上 410.6
## 5 5 华东 电子 线上 548.9
## 6 6 华南 食品 线下 709.0
## 7 7 华东 电子 线下 507.0
## 8 8 华北 电子 线下 572.4
## 9 9 华南 电子 线上 481.1
## 10 10 华北 电子 线下 432.3
## 11 11 华东 电子 线下 416.0
## 12 12 华南 服装 线上 456.3
## 13 13 华北 电子 线上 477.4
## 14 14 华南 电子 线上 340.7
## 15 15 华南 服装 线下 555.8
## 16 16 华南 电子 线下 701.9
## 17 17 华北 食品 线下 596.3
## 18 18 华北 食品 线下 608.8
## 19 19 华东 食品 线上 621.0
## 20 20 华东 食品 线上 620.5
## 21 21 华北 服装 线下 641.5
## 22 22 华南 服装 线上 535.3
## 23 23 华东 服装 线上 604.1
## 24 24 华东 食品 线下 349.1
## 25 25 华北 服装 线下 366.1
## 26 26 华东 食品 线下 701.0
## 27 27 华东 食品 线下 384.4
## 28 28 华北 服装 线上 665.8
## 29 29 华北 服装 线上 728.6
## 30 30 华南 食品 线上 612.5
## 31 31 华北 服装 线上 476.6
## 32 32 华南 服装 线下 619.6
## 33 33 华东 电子 线上 708.6
## 34 34 华北 食品 线下 670.3
## 35 35 华东 食品 线下 441.0
## 36 36 华南 食品 线下 619.8
## 37 37 华北 电子 线下 465.6
## 38 38 华南 食品 线上 489.0
## 39 39 华北 电子 线下 701.1
## 40 40 华北 服装 线下 638.9
## 41 41 华南 食品 线下 600.2
## 42 42 华北 电子 线下 671.4
## 43 43 华南 电子 线上 547.5
## 44 44 华南 食品 线上 405.0
## 45 45 华东 服装 线下 599.5
## 46 46 华东 电子 线上 464.0
## 47 47 华东 服装 线上 506.6
## 48 48 华北 服装 线下 590.0
## 49 49 华北 食品 线上 405.1
## 50 50 华南 电子 线下 680.8
## 51 51 华南 电子 线下 436.4
## 52 52 华北 食品 线上 529.6
## 53 53 华北 服装 线上 522.4
## 54 54 华东 服装 线上 655.4
## 55 55 华南 服装 线上 662.0
## 56 56 华南 食品 线上 281.6
## 57 57 华北 食品 线下 378.4
## 58 58 华南 服装 线下 593.1
## 59 59 华南 食品 线上 470.6
## 60 60 华东 食品 线下 408.5
## 61 61 华南 电子 线下 642.5
## 62 62 华东 电子 线下 568.2
## 63 63 华北 食品 线下 530.2
## 64 64 华东 服装 线下 339.6
## 65 65 华东 服装 线上 361.7
## 66 66 华北 电子 线下 794.5
## 67 67 华南 食品 线上 522.4
## 68 68 华南 电子 线上 447.3
## 69 69 华东 电子 线上 492.9
## 70 70 华东 食品 线上 778.6
## 71 71 华北 电子 线上 577.4
## 72 72 华北 服装 线上 538.6
## 73 73 华南 服装 线下 373.0
## 74 74 华东 电子 线上 277.1
## 75 75 华北 电子 线下 533.8
## 76 76 华南 食品 线上 467.6
## 77 77 华东 食品 线下 575.5
## 78 78 华南 食品 线下 254.1
## 79 79 华北 电子 线下 546.4
## 80 80 华东 服装 线上 612.7
## 81 81 华北 电子 线上 615.4
## 82 82 华北 电子 线下 344.7
## 83 83 华南 食品 线下 523.9
## 84 84 华东 食品 线上 667.4
## 85 85 华北 服装 线上 446.7
## 86 86 华南 食品 线下 458.4
## 87 87 华南 服装 线下 339.3
## 88 88 华北 服装 线下 539.0
## 89 89 华东 服装 线上 541.4
## 90 90 华东 食品 线下 630.6
## 91 91 华东 服装 线下 369.8
## 92 92 华东 服装 线下 638.1
## 93 93 华东 食品 线上 352.2
## 94 94 华东 食品 线上 661.1
## 95 95 华东 电子 线下 383.6
## 96 96 华南 食品 线下 728.8
## 97 97 华北 食品 线下 540.4
## 98 98 华南 服装 线上 333.2
## 99 99 华北 食品 线上 448.4
## 100 100 华南 服装 线下 494.7
## 101 101 华南 服装 线下 466.0
## 102 102 华东 食品 线上 493.8
## 103 103 华东 服装 线上 369.3
## 104 104 华南 食品 线下 431.6
## 105 105 华东 食品 线下 632.6
## 106 106 华南 服装 线上 650.6
## 107 107 华东 服装 线上 321.3
## 108 108 华东 食品 线上 661.7
## 109 109 华东 电子 线上 509.0
## 110 110 华东 电子 线下 306.5
## 111 111 华北 食品 线下 559.0
## 112 112 华东 电子 线下 518.2
## 113 113 华东 服装 线下 562.2
## 114 114 华北 电子 线下 442.8
## 115 115 华北 服装 线下 516.2
## 116 116 华北 食品 线下 516.9
## 117 117 华东 服装 线下 577.8
## 118 118 华北 服装 线上 533.9
## 119 119 华北 电子 线上 500.0
## 120 120 华南 服装 线下 394.3
## 121 121 华东 服装 线上 220.8
## 122 122 华东 电子 线下 643.8
## 123 123 华东 服装 线上 442.0
## 124 124 华北 服装 线上 561.1
## 125 125 华南 食品 线下 530.5
## 126 126 华东 食品 线上 344.5
## 127 127 华东 电子 线下 560.3
## 128 128 华东 服装 线上 626.0
## 129 129 华东 电子 线上 470.9
## 130 130 华东 食品 线下 327.3
## 131 131 华北 服装 线下 391.6
## 132 132 华东 电子 线上 603.4
## 133 133 华南 食品 线下 401.5
## 134 134 华北 食品 线上 323.7
## 135 135 华东 服装 线上 521.5
## 136 136 华南 电子 线下 517.1
## 137 137 华北 服装 线上 373.3
## 138 138 华东 服装 线上 352.7
## 139 139 华北 电子 线上 423.0
## 140 140 华南 电子 线上 527.0
## 141 141 华东 电子 线下 537.8
## 142 142 华东 电子 线上 464.5
## 143 143 华南 食品 线下 300.3
## 144 144 华东 服装 线上 717.2
## 145 145 华北 食品 线上 614.6
## 146 146 华南 电子 线下 392.4
## 147 147 华东 服装 线下 354.4
## 148 148 华北 服装 线上 560.4
## 149 149 华东 食品 线上 606.9
## 150 150 华北 食品 线上 445.5
请完成:
region_mean <- sales %>%
group_by(region) %>%
summarise(
mean_amount = mean(amount),
sd_amount = sd(amount),
n = n(),
se_amount = sd_amount / sqrt(n),
.groups = "drop"
)
region_mean
## # A tibble: 3 × 5
## region mean_amount sd_amount n se_amount
## <ord> <dbl> <dbl> <int> <dbl>
## 1 华东 516. 135. 60 17.4
## 2 华北 527. 106. 47 15.5
## 3 华南 504. 124. 43 18.9
ggplot(region_mean, aes(x = region, y = mean_amount, fill = region)) +
geom_col(width = 0.6) +
geom_errorbar(
aes(
ymin = mean_amount - se_amount,
ymax = mean_amount + se_amount
),
width = 0.2
) +
geom_text(
aes(label = round(mean_amount, 1)),
vjust = -0.7
) +
labs(
title = "不同地区平均销售额",
x = "地区",
y = "平均销售额"
) +
theme_minimal() +
theme(legend.position = "none")
ggplot(sales, aes(x = amount)) +
geom_histogram(
aes(y = after_stat(density)),
binwidth = 50,
fill = "skyblue",
color = "white",
alpha = 0.6
) +
geom_density(
color = "red",
linewidth = 1
) +
labs(
title = "销售额分布:直方图 + 核密度曲线",
x = "销售额",
y = "密度"
) +
theme_minimal()
ggplot(sales, aes(x = region, y = amount, fill = region)) +
geom_boxplot(alpha = 0.7) +
labs(
title = "不同地区销售额盒形图",
x = "地区",
y = "销售额"
) +
theme_minimal() +
theme(legend.position = "none")
ggplot(sales, aes(x = region, y = amount, fill = region)) +
geom_violin(alpha = 0.5, trim = FALSE) +
geom_boxplot(width = 0.15, fill = "white", outlier.shape = NA) +
labs(
title = "不同地区销售额:小提琴图 + 盒形图",
x = "地区",
y = "销售额"
) +
theme_minimal() +
theme(legend.position = "none")
region_channel_count <- sales %>%
count(region, channel)
region_channel_count
## region channel n
## 1 华东 线上 34
## 2 华东 线下 26
## 3 华北 线上 22
## 4 华北 线下 25
## 5 华南 线上 18
## 6 华南 线下 25
ggplot(region_channel_count, aes(x = region, y = n, fill = channel)) +
geom_col(position = position_dodge(width = 0.7), width = 0.7) +
geom_text(
aes(label = n),
position = position_dodge(width = 0.7),
vjust = -0.4
) +
labs(
title = "不同地区、不同渠道的订单数量",
x = "地区",
y = "订单数量",
fill = "渠道"
) +
ylim(0, max(region_channel_count$n) + 5) +
theme_minimal()
region_channel_mean <- sales %>%
group_by(region, channel) %>%
summarise(
mean_amount = mean(amount),
sd_amount = sd(amount),
n = n(),
se_amount = sd_amount / sqrt(n),
.groups = "drop"
)
region_channel_mean
## # A tibble: 6 × 6
## region channel mean_amount sd_amount n se_amount
## <ord> <chr> <dbl> <dbl> <int> <dbl>
## 1 华东 线上 523. 138. 34 23.7
## 2 华东 线下 505. 133. 26 26.2
## 3 华北 线上 508. 98.0 22 20.9
## 4 华北 线下 544. 112. 25 22.4
## 5 华南 线上 495. 113. 18 26.7
## 6 华南 线下 511. 133. 25 26.6
ggplot(region_channel_mean, aes(x = region, y = mean_amount, fill = channel)) +
geom_col(position = position_dodge(width = 0.7), width = 0.7) +
geom_errorbar(
aes(
ymin = mean_amount - se_amount,
ymax = mean_amount + se_amount
),
position = position_dodge(width = 0.7),
width = 0.2
) +
geom_text(
aes(label = round(mean_amount, 1)),
position = position_dodge(width = 0.7),
vjust = -0.7
) +
labs(
title = "不同地区、不同渠道的平均销售额",
x = "地区",
y = "平均销售额",
fill = "渠道"
) +
theme_minimal()
下面是模板代码,所以设置为
eval=FALSE,只展示,不运行。
# 加载包
library(ggplot2)
library(dplyr)
# 1. 计算分组均值
mean_df <- df %>%
group_by(group_var) %>%
summarise(
mean_y = mean(y),
sd_y = sd(y),
n = n(),
se_y = sd_y / sqrt(n),
.groups = "drop"
)
# 2. 均值条形图
ggplot(mean_df, aes(x = group_var, y = mean_y, fill = group_var)) +
geom_col() +
geom_errorbar(
aes(ymin = mean_y - se_y, ymax = mean_y + se_y),
width = 0.2
) +
theme_minimal()
# 3. 直方图
ggplot(df, aes(x = x_var)) +
geom_histogram(binwidth = 5)
# 4. 直方图 + 核密度曲线
ggplot(df, aes(x = x_var)) +
geom_histogram(
aes(y = after_stat(density)),
binwidth = 5,
fill = "skyblue",
color = "white",
alpha = 0.6
) +
geom_density(color = "red", linewidth = 1) +
theme_minimal()
# 5. 盒形图
ggplot(df, aes(x = group_var, y = y_var, fill = group_var)) +
geom_boxplot() +
theme_minimal()
# 6. 小提琴图 + 盒形图
ggplot(df, aes(x = group_var, y = y_var, fill = group_var)) +
geom_violin(alpha = 0.5, trim = FALSE) +
geom_boxplot(width = 0.15, fill = "white") +
theme_minimal()
# 7. 自动计数分组条形图
ggplot(df, aes(x = group_var1, fill = group_var2)) +
geom_bar(position = "dodge") +
theme_minimal()
# 8. 先计数,再画分组条形图
count_df <- df %>%
count(group_var1, group_var2)
ggplot(count_df, aes(x = group_var1, y = n, fill = group_var2)) +
geom_col(position = "dodge") +
theme_minimal()
# 9. 分组均值条形图
mean_df2 <- df %>%
group_by(group_var1, group_var2) %>%
summarise(
mean_y = mean(y_var),
se_y = sd(y_var) / sqrt(n()),
.groups = "drop"
)
ggplot(mean_df2, aes(x = group_var1, y = mean_y, fill = group_var2)) +
geom_col(position = position_dodge(width = 0.7), width = 0.7) +
geom_errorbar(
aes(ymin = mean_y - se_y, ymax = mean_y + se_y),
position = position_dodge(width = 0.7),
width = 0.2
) +
theme_minimal()
这节你最需要记住:
geom_col()
用于已经算好数值的条形图,比如均值条形图。
geom_bar()
用于自动计数条形图。
geom_histogram()
用于直方图。
geom_density()
用于核密度曲线。
geom_boxplot()
用于盒形图。
geom_violin()
用于小提琴图。
最常见组合是:
ggplot(df, aes(x = group, y = value, fill = group)) +
geom_violin() +
geom_boxplot(width = 0.15)
以及:
ggplot(df, aes(x = value)) +
geom_histogram(aes(y = after_stat(density))) +
geom_density()