跳至主要内容

R 中的 ggplot2 分面

在本教程中,您将学习如何充分利用 ggplot 的分面函数。
· 8分钟 阅读

使用 AI 探索

简介

通过分面,您可以创建多面板图,并控制一个面板的坐标刻度如何与另一个面板的刻度关联。

分面的简单用法

如果您对 ggplot2 稍有了解,就会知道调用 ggplot() 函数的基本结构。关于 ggplot2 的入门,您可以查看我们的 ggplot2 课程。调用 ggplot 时,您需要提供一个数据源,通常是一个数据框,然后让 ggplot 将数据源中的不同变量映射到不同的美学属性上,例如 x 或 y 轴的位置,或者点或柱的颜色。借助分面,您还能以另一种方式映射变量。为演示这一点,您将使用下列数据集,其中包含若干国家的经济指标。大多数变量都是关于各国 GDP(国内生产总值)的不同变体。

 print(econdata)
##   Country  GDP_nom  GDP_PPP GDP_nom_per_capita GDP_PPP_per_capita
## 1     USA 19390600 19390600              59501              59495
## 2  Canada  1652412  1769270              45077              48141
## 3   China 12014610 23159107               8643              16807
## 4   Japan  4872135  5428813              38440              42659
## 5  France  2583560  2835746              39869              43550
## 6 Germany  3684816  4170790              44550              50206
## 7  Sweden  3684816   520937              53218              51264
## 8 Ireland   333994   343682              70638              72632
##   GNI_per_capita        Region
## 1          58270 North America
## 2          42870 North America
## 3           8690          Asia
## 4          38550          Asia
## 5          37970        Europe
## 6          43490        Europe
## 7          52590        Europe
## 8          55290        Europe

包含以下变量:

Country:不言自明!

GDP_nom:以美元计的名义国内生产总值

GDP_PPP:经购买力平价调整的国内生产总值

GDP_nom_per_capita:以美元计的人均名义国内生产总值

GDP_PPP_per_capita:经购买力平价调整的人均国内生产总值

GNI_per_capita:各国的人均国民总收入。

Region:该国家所在的世界区域。

首先,让我们绘制各国名义 GDP 的简单柱状图。

ggplot(econdata, aes(x=Country, y=GDP_nom))+
  geom_bar(stat='identity', fill="forest green")+
  ylab("GDP (nominal)")
plot

您也可以绘制另一个变量,即经 PPP 调整的 GDP。

ggplot(econdata, aes(x=Country, y=GDP_PPP))+
  geom_bar(stat='identity', fill="forest green")+
  ylab("GDP (PPP)")
plot

这会生成第二个独立图,与上一个类似,但使用了不同变量。假设您想将名义 GDP 和 PPP GDP 放在一起绘制。您将使用分面来实现。首先,需要将数据从“宽表”格式(每个变量一列)改为“长表”格式,即用一列存放度量值,另一列作为键变量,指示每一行所使用的度量。

econdatalong <- gather(econdata, key="measure", value="value", c("GDP_nom", "GDP_PPP"))

一旦数据转成这种格式,就可以利用键变量来绘制分面图。我们来构建一个简单的图,同时展示名义 GDP(来自第一个图)和 GDP(PPP)(来自第二个图)。为此,您只需在代码中添加 +facet_wrap(),并指定使用 ~measure(我们的键变量)进行分面。

ggplot(econdatalong, aes(x=Country, y=value))+
  geom_bar(stat='identity', fill="forest green")+
  facet_wrap(~measure)
plot

这确实可行,但您会注意到国家名称被挤得很紧。让我们调整面板布局。

自定义布局与重新排序

facet_wrap() 会自动决定使用多少列。您也可以通过 ncol= 直接指定,如下所示:

ggplot(econdatalong, aes(x=Country, y=value))+
  geom_bar(stat='identity', fill="forest green")+
  facet_wrap(~measure,  ncol=1)
plot

您可能注意到上图 x 轴上的国家按字母顺序排列。如果想更改这一点,最简单的方法是设置 Country 因子的水平。让我们按照名义 GDP 总量对国家重新排序。

econdata$Country <- factor(econdata$Country, levels= econdata$Country[order(econdata$GDP_nom)])


econdatalong <- gather(econdata, key="measure", value="value", c("GDP_nom", "GDP_PPP"))


ggplot(econdatalong, aes(x=Country, y=value))+
  geom_bar(stat='identity', fill="forest green")+
  facet_wrap(~measure,  ncol=1)
plot

您还可以进行一些额外的自定义,比如通过 strip.position 参数将分面标签移动到左侧。

ggplot(econdatalong, aes(x=Country, y=value))+
  geom_bar(stat='identity', fill="forest green")+
  facet_wrap(~measure,  ncol=1, strip.position = "left")
plot

为分面添加标签

您可能已经注意到,分面的简短标题来自因子 measure 的水平。让我们整理一下,为分面提供更友好的标签。为此,您将编写一个简单的标注函数 variable_labeller,当输入 variable_names 的某个值时,它会返回相应名称。然后将该函数传递给 facet_wrap 的 labeller 参数。

variable_names <- list(
  "GDP_nom" = "GDP (nominal)" ,
  "GDP_PPP" = "GDP (purchasing power parity)"
)


variable_labeller <- function(variable,value){
  return(variable_names[value])
}


ggplot(econdatalong, aes(x=Country, y=value))+
  geom_bar(stat='identity', fill="forest green")+
  facet_wrap(~measure,  ncol=1, labeller=variable_labeller)
plot

调整刻度与空间

让我们构建一个更大的分面图,使用所有经济度量。

econdatalong <- gather(econdata, key="measure", value="value", c( "GDP_nom" , "GDP_PPP" ,"GDP_nom_per_capita", "GDP_PPP_per_capita" ,"GNI_per_capita"))

variable_names <- list(
  "GDP_nom" = "GDP (nominal)" ,
  "GDP_PPP" = "GDP (purchasing power parity)",
  "GDP_nom_per_capita" = "GDP (nominal) per capita",
  "GDP_PPP_per_capita" = "GDP (purchasing power parity) per capita",
  "GNI_per_capita"  = "GNI per capita"
)


variable_labeller <- function(variable,value){
  return(variable_names[value])
}



ggplot(econdatalong, aes(x=Country, y=value))+
  geom_bar(stat='identity', fill="forest green")+
  facet_wrap(~measure,  ncol=1, labeller= variable_labeller)+
scale_y_continuous(breaks = pretty(econdatalong$value, n = 10))
plot

这完全不行!您看不到其中三个面板的取值。为什么?让我们看看原始数据找原因。

summary(econdata)
##     Country     GDP_nom            GDP_PPP         GDP_nom_per_capita
##  Ireland:1   Min.   :  333994   Min.   :  343682   Min.   : 8643     
##  Canada :1   1st Qu.: 2350773   1st Qu.: 1457187   1st Qu.:39512     
##  France :1   Median : 3684816   Median : 3503268   Median :44814     
##  Germany:1   Mean   : 6027118   Mean   : 7202368   Mean   :44992     
##  Sweden :1   3rd Qu.: 6657754   3rd Qu.: 8919260   3rd Qu.:54789     
##  Japan  :1   Max.   :19390600   Max.   :23159107   Max.   :70638     
##  (Other):2                                                           
##  GDP_PPP_per_capita GNI_per_capita            Region
##  Min.   :16807      Min.   : 8690   Asia         :2  
##  1st Qu.:43327      1st Qu.:38405   Europe       :4  
##  Median :49174      Median :43180   North America:2  
##  Mean   :48094      Mean   :42215                    
##  3rd Qu.:53322      3rd Qu.:53265                    
##  Max.   :72632      Max.   :58270                    
##

查看每一列会发现,各列的取值跨越了几个数量级。默认情况下,分面会对 X 轴和 Y 轴使用相同的范围和刻度。若要更改,您可以在分面代码中添加 scales="free_y",使每个分面使用各自独立的刻度。

ggplot(econdatalong, aes(x=Country, y=value))+
  geom_bar(stat='identity', fill="forest green")+
  facet_wrap(~measure, scales="free_y", ncol=1,  labeller= variable_labeller)
plot

这就好多了。现在每个分面都有各自独立的 y 轴。

使用分面创建细分

您可能注意到,数据集中还包含变量 Region,表示该国家所在的地区。您可以用它为柱形按地区着色,如下所示:

ggplot(econdatalong, aes(x=Country, y=value, fill=Region))+
  geom_bar(stat='identity')+
  facet_wrap(~measure, scales="free_y", ncol=1, labeller= variable_labeller)
plot

不过这有点乱。如果能把不同地区分别放到各自的子面板就好了?使用分面就能做到!这里我们将使用 facet_grid 而非 facet_wrap,这样更容易将分面映射到两个变量上:Region 和 measure,并把这两个变量分布到图表网格的行与列上。注意我们还设置了 scales="free" 和 space="free",允许不同面板占用不同的空间。您还需要创建一个新的标注函数,为行与列同时生成名称。

variable_names <- list(
  "GDP_nom" = "GDP \n(nominal)" ,
  "GDP_PPP" = "GDP \n(PPP)",
  "GDP_nom_per_capita" = "GDP (nominal)\n per capita",
  "GDP_PPP_per_capita" = "GDP (PPP)\n per capita",
  "GNI_per_capita"  = "GNI \nper capita"
)



region_names <- levels(econdata$Region)


variable_labeller2 <- function(variable,value){
  if (variable=='measure') {
  return(variable_names[value])
  } else {
    return(region_names)
  }
}

ggplot(econdatalong, aes(x=Country, y=value, fill=Region))+
  geom_bar(stat='identity')+
  facet_grid(measure~Region, scales="free", space="free_x",  labeller= variable_labeller2)
plot

现在清晰多了!每个地区都有自己的列,每个指标都有自己的一行柱形图。

本教程到此结束。希望您在学习分面时有所收获。

如果您想进一步了解分面,请学习 DataCamp 的 使用 Trelliscope 可视化大数据 课程。

查看我们的 Tidyverse 入门:教程。

主题