本节简要概述ggplot2包的工作原理。如果您只是简单地寻找代码来创建特定类型的图,那么可以跳过这一部分。然而,这些材料可以帮助你理解这些碎片是如何组合在一。起的。

2.1 一个有效的例子

ggplot2包中的函数构建了一个分层的图形。我们将通过从一个简单的图开始并添加额外的元素,一次添加一个元素来构建一个复杂的图。

这个例子使用了1985年当前人口调查的数据来探索工资和经验之间的关系。

library(tidyverse)
library(mosaicData)
# load data
data(CPS85 , package = "mosaicData")

CPS85 %>% glimpse()
## Observations: 534
## Variables: 11
## $ wage     <dbl> 9.00, 5.50, 3.80, 10.50, 15.00, 9.00, 9.57, 15.00, 11.00, ...
## $ educ     <int> 10, 12, 12, 12, 12, 16, 12, 14, 8, 12, 17, 17, 14, 14, 12,...
## $ race     <fct> W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, NW, NW,...
## $ sex      <fct> M, M, F, F, M, F, F, M, M, F, M, M, M, M, M, M, M, M, M, M...
## $ hispanic <fct> NH, NH, NH, NH, NH, NH, NH, NH, NH, NH, Hisp, NH, Hisp, NH...
## $ south    <fct> NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS, NS...
## $ married  <fct> Married, Married, Single, Married, Married, Married, Marri...
## $ exper    <int> 27, 20, 4, 29, 40, 27, 5, 22, 42, 14, 18, 3, 4, 14, 35, 0,...
## $ union    <fct> Not, Not, Not, Not, Union, Not, Union, Not, Not, Not, Not,...
## $ age      <int> 43, 38, 22, 47, 58, 49, 23, 42, 56, 32, 41, 26, 24, 34, 53...
## $ sector   <fct> const, sales, sales, clerical, const, clerical, service, s...

在构建ggplot2图时,只需要下面描述的前两个函数。其他函数是可选的,可以以任何顺序出现。

2.1.1 ggplot

构建图形的第一个函数是ggplot函数

包含要绘制的数据的数据框

变量到图形可视属性的映射,映射被放置在aes函数。

# specify dataset and mapping
library(ggplot2)
ggplot(data = CPS85,
       mapping = aes(x = exper, y = wage))

为什么图表是空的?我们指定exper变量应该映射到x轴,wage应该映射到y轴,但是我们还没有指定我们想要放置在图上的内容。

2.1.2 几何对象

geoms是可以放置在图形上的几何对象(点、线、条等)。它们是使用以geom开头的函数添加的。在这个例子中,我们将使用 geom_point函数添加点,创建散点图

ggplot2图中,函数使用+符号链接在一起来构建最终的绘图。

# add points
ggplot(data = CPS85,
       mapping = aes(x = exper, y = wage)) +
  geom_point()

图表显示存在一个异常值。 一个人的工资比其他人高得多。在继续之前,我们将删除这个观测值。

CPS85 %>% 
  filter(wage < 40) %>% 
  ggplot(aes(exper,wage)) +
  geom_point()

可以在geom 函数中指定许多参数(选项)。geom_point函数的选项包括colorsizealpha。 它们分别控制点的颜色、大小和透明度。透明度范围从0(完全透明)到1(完全不透明)。 增加一定程度的透明度可以帮助可视化重叠点。

# make points blue, larger, and semi-transparent
CPS85 %>% 
  filter(wage < 40) %>% 
  ggplot(aes(exper,wage)) +
  geom_point(color = "cornflowerblue",
             alpha = .7,
             size = 3)

接下来,让我们添加一行最佳拟合。我们可以用geom_smooth函数来实现。选项控制线条的类型(linear, quadratic, nonparametric),线条的粗细,线条的颜色,以及置信区间的存在与否。这里我们请求一个线性回归线(方法lm)(其中lm代表线性模型)。

CPS85 %>% 
  filter(wage < 40) %>%     # 工资小于40
  ggplot(aes(exper,wage)) +
  geom_point(color = "cornflowerblue",
             alpha = .7,
             size = 3) +
  geom_smooth(method = "lm")
## `geom_smooth()` using formula 'y ~ x'

工资似乎随着经验的增加而增加。

2.1.3 分组

除了将变量映射到x轴y轴之外,变量还可以映射到几何对象的颜色形状大小透明度和其他视觉特征。这使得一组观测数据可以叠加在一个单一的图表中。

让我们在plot中加入性别元素,用color来表现它。

# indicate sex using colol
CPS85 %>% 
  filter(wage < 40) %>%        # 工资小于40
  ggplot(aes(exper,wage,col = sex)) +    # x轴,y轴
  geom_point(alpha = .7,
             size = 3) +
  geom_smooth(method = "lm",
              se = FALSE,
              size = 1.5)
## `geom_smooth()` using formula 'y ~ x'

性别选项被放置在aes函数中,因为我们将一个变量映射到一个美学上。增加geom_smooth选项(se = FALSE)用来取消置信区间。看起来男人比女人挣的钱多。此外,男性的经验与工资之间的关系可能比女性更为密切。

2.1.4 scales

scales控制变量如何映射到plot的视觉特征。scales函数(以scale_为开始)允许您修改此映射。在下一个图中,我们将更改x 轴y 轴scales以及使用的颜色。

# modify the x and y axes and specify the colors to be used
CPS85 %>% 
  filter(wage < 40) %>%        # 工资小于40
  ggplot(aes(exper,wage,col = sex)) +    # x轴,y轴
  geom_point(alpha = .7,
             size = 3) +
  geom_smooth(method = "lm",
              se = FALSE,
              size = 1.5)->p1

CPS85 %>% 
  filter(wage < 40) %>%        # 工资小于40
  ggplot(aes(exper,wage,col = sex)) +    # x轴,y轴
  geom_point(alpha = .7,
             size = 3) +
  geom_smooth(method = "lm",
              se = FALSE,
              size = 1.5) +
  scale_x_continuous(breaks = seq(0,60,10)) +
  scale_y_continuous(breaks = seq(0,30,5),labels = paste("$",seq(0,30,5),sep = "")) +
  scale_color_manual(values = c("indianred3", 
                                "cornflowerblue"))->p2
gridExtra::grid.arrange(p1,p2,ncol = 2)
## `geom_smooth()` using formula 'y ~ x'
## `geom_smooth()` using formula 'y ~ x'

X轴和y轴上的数字更好,y轴使用美元符号,而且颜色更有吸引力(IMHO)。

我有个问题: 经验工资性别之间的关系是否在每个工作部门都是相同的?为了探究这个问题,让我们在每个职业领域重复一次这个图表。

2.1.5 分面

# reproduce plot for each level of job sector
psych::describe(CPS85)
##           vars   n  mean    sd median trimmed   mad min  max range  skew
## wage         1 534  9.02  5.14   7.78    8.28  4.12   1 44.5  43.5  1.69
## educ         2 534 13.02  2.62  12.00   13.05  1.48   2 18.0  16.0 -0.20
## race*        3 534  1.87  0.33   2.00    1.97  0.00   1  2.0   1.0 -2.25
## sex*         4 534  1.54  0.50   2.00    1.55  0.00   1  2.0   1.0 -0.16
## hispanic*    5 534  1.95  0.22   2.00    2.00  0.00   1  2.0   1.0 -4.09
## south*       6 534  1.29  0.46   1.00    1.24  0.00   1  2.0   1.0  0.91
## married*     7 534  1.34  0.48   1.00    1.31  0.00   1  2.0   1.0  0.65
## exper        8 534 17.82 12.38  15.00   16.75 11.86   0 55.0  55.0  0.68
## union*       9 534  1.18  0.38   1.00    1.10  0.00   1  2.0   1.0  1.66
## age         10 534 36.83 11.73  35.00   36.07 11.86  18 64.0  46.0  0.55
## sector*     11 534  4.63  2.35   5.00    4.67  2.97   1  8.0   7.0 -0.19
##           kurtosis   se
## wage          4.90 0.22
## educ          0.81 0.11
## race*         3.09 0.01
## sex*         -1.98 0.02
## hispanic*    14.76 0.01
## south*       -1.17 0.02
## married*     -1.58 0.02
## exper        -0.40 0.54
## union*        0.77 0.02
## age          -0.60 0.51
## sector*      -1.14 0.10
CPS85 %>% 
  filter(wage < 40) %>%        # 工资小于40
  ggplot(aes(exper,wage,col = sex)) +    # x轴,y轴
  geom_point(alpha = .7,
             size = 3) +
  geom_smooth(method = "lm",
              se = FALSE,
              size = 1.5) +
  scale_x_continuous(breaks = seq(0,60,10)) +
  scale_y_continuous(breaks = seq(0,30,5),labels = paste("$",seq(0,30,5),sep = "")) +
  scale_color_manual(values = c("indianred3", 
                                "cornflowerblue")) +
  facet_wrap(~sector)         # 相当于研究四个变量
## `geom_smooth()` using formula 'y ~ x'

男人和妇女之间的差别似乎取决于所考虑的就业部门

2.1.6 labels

图表应该易于解释,信息标签是实现这一目标的关键因素。labs函数为轴和图例提供自定义标签。此外,还可以添加自定义标题副标题caption

# add informative labels
CPS85 %>% 
  filter(wage < 40) %>% 
  ggplot(aes(exper,wage,col = sex)) +
  geom_point(size = 2,alpha = 0.7) +
  geom_smooth(method = "lm",se = FALSE,size = 1.5) +
  scale_x_continuous(breaks = seq(0,60,10)) +        # 划分X轴坐标数据
  scale_y_continuous(breaks = seq(0, 30, 5),
                     label = scales::dollar) +
  scale_color_manual(values = c("indianred3", 
                                "cornflowerblue")) +
  facet_wrap(~sector) +
  labs(title = "Relationship between wages and experience",  # 标题
       subtitle = "Current Population Survey",               # 副标题
       caption = "source: http://mosaic-web.org/",           # 注释
       x = "Years of Experience",
       y = "Hourly Wage",
       color = "Gender") +
  theme(plot.title = element_text(hjust = 0.5))
## `geom_smooth()` using formula 'y ~ x'

现在,读者不需要猜测 exprwage 的含义,也不需要猜测数据来自哪里。

2.1.7 主题

最后,我们可以使用主题微调图形的外观。主题函数(以主题开始)控制图形的背景颜色字体网格线图例放置和其他与数据无关的特性。 让我们使用一个干净的主题。

CPS85 %>% 
  filter(wage < 40) %>% 
  ggplot(aes(exper,wage,col = sex)) +
  geom_point(size = 2,alpha = 0.7) +
  geom_smooth(method = "lm",se = FALSE,size = 1.5) +
  scale_x_continuous(breaks = seq(0,60,10)) +        # 划分X轴坐标数据
  scale_y_continuous(breaks = seq(0, 30, 5),
                     label = scales::dollar) +
  scale_color_manual(values = c("indianred3", 
                                "cornflowerblue")) +
  facet_wrap(~sector) +
  labs(title = "Relationship between wages and experience",  # 标题
       subtitle = "Current Population Survey",               # 副标题
       caption = "source: http://mosaic-web.org/",           # 注释
       x = "Years of Experience",
       y = "Hourly Wage",
       color = "Gender") +
  theme(plot.title = element_text(hjust = 0.5)) +
  theme_minimal()
## `geom_smooth()` using formula 'y ~ x'

现在我们有了一些东西。在management, manufacturing, sales和“other” category中,男性似乎比女性挣得多。他们在clerical, professional和service positions最为相似。 这些数据没有包括建筑业的妇女。 就construction职位而言,男性的工资似乎与经验有关,而女性的工资则与经验无关(这可能是最有趣的发现)。对于销售来说也是如此。

当然,这些发现都是试验性的。它们基于有限的样本规模,不涉及统计测试,以评估差异是否可能是由于偶然变化。

2.2 放置data和mapping选项

使用ggplot2创建的图形总是从ggplot函数开始。在上面的示例中,数据映射选项被放置在这个函数中。 在这种情况下,它们应用于后面的每个geom函数。您还可以将这些选项直接放置在geom中。在这种情况下,它们只适用于特定的geom。 考虑下面的图表。

# placing color mapping in the ggplot function
CPS85 %>% 
  filter(wage < 40) %>% 
  ggplot(aes(x = exper, 
             y = wage,
             color = sex)) +
  geom_point(alpha = .7,
             size = 3) +
  geom_smooth(method = "lm",
              formula = y ~ poly(x,2),
              se = FALSE, 
              size = 1.5)

由于性别映射出现在ggplot函数中,因此它既适用于geom_point(),也适用于geom_smooth()。点的颜色表示性别,并且为男性和女性生成一条单独的颜色趋势线。

比较一下

# placing color mapping in the geom_point function
CPS85 %>%
  filter(wage < 40) %>%
  ggplot(aes(x = exper,
             y = wage)) +
  geom_point(aes(color = sex),   
             alpha = .7,
             size = 3) +
  geom_smooth(
    method = "lm",
    formula = y ~ poly(x, 2),
    se = FALSE,
    size = 1.5
  )

由于性别映射只出现在geom_point函数中,因此只能在geom_point函数中使用。为所有的观察数据创建一条单一的趋势线。

本书中的大多数示例都将数据data映射mapping选项放在ggplot函数中。 此外,短语数据data和映射mapping被省略,因为第一个选项总是引用数据,而第二个选项总是引用映射。

2.3 图表作为对象

ggplot2图可以保存为命名的r对象(类似于数据框) ,进一步操作,然后打印或保存到磁盘。

# prepare data
data(CPS85 , package = "mosaicData")
plotdata <- CPS85[CPS85$wage < 40, ]

# create scatterplot and save it
myplot <- ggplot(data = plotdata,
                 aes(x = exper, y = wage)) +
  geom_point()

# print the graph
myplot

# make the points larger and blue then print the graph
myplot <- myplot + geom_point(size = 3, color = "blue")
myplot

# print the graph with a title and line of best fit but don't save those changes
myplot +
  geom_smooth(method = "lm") +
  labs(title = "Mildly interesting graph")
## `geom_smooth()` using formula 'y ~ x'
# print the graph with a black and white theme but don't save those changes
myplot + theme_bw()

这可以真正节省时间。当以编程方式保存图形时,它也很方便。现在是时候尝试其他类型的图表了。