我需要在一个图表中绘制一个显示计数的柱状图和一个显示率的折线图,我可以分别做这两个,但当我把它们放在一起时,我的第一层(即geom_bar)的比例被第二层(即geom_line)重叠。

我可以将geom_line的轴向右移动吗?


当前回答

可以对变量使用facet_wrap(~ variable, ncol=)来创建一个新的比较。它们不在同一个轴上,但很相似。

其他回答

这是我对如何做二次轴变换的两种看法。首先,您希望将主数据和辅助数据的范围耦合起来。这通常是混乱的,因为您不想要的变量污染了全局环境。

为了简化这一点,我们将创建一个生成两个函数的函数工厂,其中scales::rescale()完成所有繁重的工作。因为这些是闭包,所以它们知道创建它们的环境,所以它们“有”创建之前生成的to和from参数的“内存”。

一个函数进行正向转换:将辅助数据转换为主要尺度。 第二个函数进行反向转换:将主要单位中的数据转换为次要单位。

library(ggplot2)
library(scales)

# Function factory for secondary axis transforms
train_sec <- function(primary, secondary, na.rm = TRUE) {
  # Thanks Henry Holm for including the na.rm argument!
  from <- range(secondary, na.rm = na.rm)
  to   <- range(primary, na.rm = na.rm)
  # Forward transform for the data
  forward <- function(x) {
    rescale(x, from = from, to = to)
  }
  # Reverse transform for the secondary axis
  reverse <- function(x) {
    rescale(x, from = to, to = from)
  }
  list(fwd = forward, rev = reverse)
}

这看起来相当复杂,但是创建函数工厂会使其余的一切变得更简单。现在,在绘制图形之前,我们将通过向工厂显示主要和次要数据来生成相关函数。我们将使用经济学数据集,它的失业列和pasavert列的范围非常不同。

sec <- with(economics, train_sec(unemploy, psavert))

然后我们使用y = sec$fwd(psavert)将辅助数据重新缩放到主轴,并指定~ sec$rev(.)作为辅助轴的转换参数。这给了我们一个主要范围和次要范围在图上占据相同空间的图。

ggplot(economics, aes(date)) +
  geom_line(aes(y = unemploy), colour = "blue") +
  geom_line(aes(y = sec$fwd(psavert)), colour = "red") +
  scale_y_continuous(sec.axis = sec_axis(~sec$rev(.), name = "psavert"))

工厂比这稍微灵活一些,因为如果您只是想重新调整最大值,您可以传入下限为0的数据。

# Rescaling the maximum
sec <- with(economics, train_sec(c(0, max(unemploy)),
                                 c(0, max(psavert))))

ggplot(economics, aes(date)) +
  geom_line(aes(y = unemploy), colour = "blue") +
  geom_line(aes(y = sec$fwd(psavert)), colour = "red") +
  scale_y_continuous(sec.axis = sec_axis(~sec$rev(.), name = "psavert"))

由reprex包于2021-02-05创建(v0.3.0)

我承认这个例子中的区别不是很明显,但如果你仔细观察,你会发现最大值是相同的,红线比蓝色的线低。

编辑:

这种方法现在已经在ggh4x包中的help_secondary()函数中被捕获和扩展。声明:我是ggh4x的作者。

可以对变量使用facet_wrap(~ variable, ncol=)来创建一个新的比较。它们不在同一个轴上,但很相似。

常见的用例有双y轴,例如,显示每月温度和降水的气体图。这里是一个简单的解决方案,从威震天的解决方案中推广,允许你设置变量的下限为零:

示例数据:

climate <- tibble(
  Month = 1:12,
  Temp = c(-4,-4,0,5,11,15,16,15,11,6,1,-3),
  Precip = c(49,36,47,41,53,65,81,89,90,84,73,55)
  )

将以下两个值设置为接近数据限制的值(您可以使用这些值来调整图形的位置;坐标轴仍然是正确的):

ylim.prim <- c(0, 180)   # in this example, precipitation
ylim.sec <- c(-4, 18)    # in this example, temperature

下面根据这些极限进行必要的计算,并制作出图本身:

b <- diff(ylim.prim)/diff(ylim.sec)
a <- ylim.prim[1] - b*ylim.sec[1]) # there was a bug here

ggplot(climate, aes(Month, Precip)) +
  geom_col() +
  geom_line(aes(y = a + Temp*b), color = "red") +
  scale_y_continuous("Precipitation", sec.axis = sec_axis(~ (. - a)/b, name = "Temperature")) +
  scale_x_continuous("Month", breaks = 1:12) +
  ggtitle("Climatogram for Oslo (1961-1990)")  

如果你想确保红线对应右边的y轴,你可以在代码中添加一个主题句:

ggplot(climate, aes(Month, Precip)) +
  geom_col() +
  geom_line(aes(y = a + Temp*b), color = "red") +
  scale_y_continuous("Precipitation", sec.axis = sec_axis(~ (. - a)/b, name = "Temperature")) +
  scale_x_continuous("Month", breaks = 1:12) +
  theme(axis.line.y.right = element_line(color = "red"), 
        axis.ticks.y.right = element_line(color = "red"),
        axis.text.y.right = element_text(color = "red"), 
        axis.title.y.right = element_text(color = "red")
        ) +
  ggtitle("Climatogram for Oslo (1961-1990)")

右轴的颜色:

下面的文章帮助我将ggplot2生成的两个图合并到单行上:

一页上的多个图(ggplot2)由Cookbook for R

下面是代码在这种情况下的样子:

p1 <- 
  ggplot() + aes(mns)+ geom_histogram(aes(y=..density..), binwidth=0.01, colour="black", fill="white") + geom_vline(aes(xintercept=mean(mns, na.rm=T)), color="red", linetype="dashed", size=1) +  geom_density(alpha=.2)

p2 <- 
  ggplot() + aes(mns)+ geom_histogram( binwidth=0.01, colour="black", fill="white") + geom_vline(aes(xintercept=mean(mns, na.rm=T)), color="red", linetype="dashed", size=1)  

multiplot(p1,p2,cols=2)

我承认并同意哈德利(和其他人)的观点,即单独的y量表“存在根本缺陷”。说到这里,我经常希望ggplot2有这个特性——特别是当数据是宽格式的,并且我想快速地可视化或检查数据时(即仅供个人使用)。

虽然tidyverse库可以很容易地将数据转换为长格式(这样facet_grid()就可以工作),但这个过程仍然不是简单的,如下所示:

library(tidyverse)
df.wide %>%
    # Select only the columns you need for the plot.
    select(date, column1, column2, column3) %>%
    # Create an id column – needed in the `gather()` function.
    mutate(id = n()) %>%
    # The `gather()` function converts to long-format. 
    # In which the `type` column will contain three factors (column1, column2, column3),
    # and the `value` column will contain the respective values.
    # All the while we retain the `id` and `date` columns.
    gather(type, value, -id, -date) %>%
    # Create the plot according to your specifications
    ggplot(aes(x = date, y = value)) +
        geom_line() +
        # Create a panel for each `type` (ie. column1, column2, column3).
        # If the types have different scales, you can use the `scales="free"` option.
        facet_grid(type~., scales = "free")