我需要在一个图表中绘制一个显示计数的柱状图和一个显示率的折线图,我可以分别做这两个,但当我把它们放在一起时,我的第一层(即geom_bar)的比例被第二层(即geom_line)重叠。
我可以将geom_line的轴向右移动吗?
我需要在一个图表中绘制一个显示计数的柱状图和一个显示率的折线图,我可以分别做这两个,但当我把它们放在一起时,我的第一层(即geom_bar)的比例被第二层(即geom_line)重叠。
我可以将geom_line的轴向右移动吗?
当前回答
这在ggplot2中是不可能的,因为我认为具有单独y尺度的图(不是相互转换的y尺度)从根本上是有缺陷的。一些问题:
The are not invertible: given a point on the plot space, you can not uniquely map it back to a point in the data space. They are relatively hard to read correctly compared to other options. See A Study on Dual-Scale Data Charts by Petra Isenberg, Anastasia Bezerianos, Pierre Dragicevic, and Jean-Daniel Fekete for details. They are easily manipulated to mislead: there is no unique way to specify the relative scales of the axes, leaving them open to manipulation. Two examples from the Junkcharts blog: one, two They are arbitrary: why have only 2 scales, not 3, 4 or ten?
你也可能想要阅读Stephen Few关于双缩放轴在图形中的主题的冗长讨论,它们是最好的解决方案吗?
其他回答
从ggplot2 2.2.0开始,您可以添加如下的辅助轴(取自ggplot2 2.2.0公告):
ggplot(mpg, aes(displ, hwy)) +
geom_point() +
scale_y_continuous(
"mpg (US)",
sec.axis = sec_axis(~ . * 1.20, name = "mpg (UK)")
)
以下内容结合了Dag Hjermann的基本数据和编程,改进了user4786271创建“转换函数”的策略,以优化组合图和数据轴,并响应了浸信会的提示,这样的函数可以在R中创建。
#Climatogram for Oslo (1961-1990)
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))
#y1 identifies the position, relative to the y1 axis,
#the locations of the minimum and maximum of the y2 graph.
#Usually this will be the min and max of y1.
#y1<-(c(max(climate$Precip), 0))
#y1<-(c(150, 55))
y1<-(c(max(climate$Precip), min(climate$Precip)))
#y2 is the Minimum and maximum of the secondary axis data.
y2<-(c(max(climate$Temp), min(climate$Temp)))
#axis combines y1 and y2 into a dataframe used for regressions.
axis<-cbind(y1,y2)
axis<-data.frame(axis)
#Regression of Temperature to Precipitation:
T2P<-lm(formula = y1 ~ y2, data = axis)
T2P_summary <- summary(lm(formula = y1 ~ y2, data = axis))
T2P_summary
#Identifies the intercept and slope of regressing Temperature to Precipitation:
T2PInt<-T2P_summary$coefficients[1, 1]
T2PSlope<-T2P_summary$coefficients[2, 1]
#Regression of Precipitation to Temperature:
P2T<-lm(formula = y2 ~ y1, data = axis)
P2T_summary <- summary(lm(formula = y2 ~ y1, data = axis))
P2T_summary
#Identifies the intercept and slope of regressing Precipitation to Temperature:
P2TInt<-P2T_summary$coefficients[1, 1]
P2TSlope<-P2T_summary$coefficients[2, 1]
#Create Plot:
ggplot(climate, aes(Month, Precip)) +
geom_col() +
geom_line(aes(y = T2PSlope*Temp + T2PInt), color = "red") +
scale_y_continuous("Precipitation", sec.axis = sec_axis(~.*P2TSlope + P2TInt, 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)")
Most noteworthy is that a new "transformation function" works better with just two data points from the data set of each axes—usually the maximum and minimum values of each set. The resulting slopes and intercepts of the two regressions enable ggplot2 to exactly pair the plots of the minimums and maximums of each axis. As user4786271 pointed out, the two regressions transform each data set and plot to the other. One transforms the break points of the first y axis to the values of the second y axis. The second transforms the data of the secondary y axis to be "normalized" according to the first y axis. The following output shows how the axis align the minimums and maximums of each dataset:
使最大值和最小值匹配可能是最合适的;但是,这种方法的另一个好处是,如果需要,可以通过更改与主轴数据相关的编程行轻松地移动与次要轴相关的绘图。下面的输出只是将y1编程行中输入的最小降水量更改为“0”,从而将最小温度水平与“0”降水水平对齐。
从:y1<-(c(max(气候$ precp), min(气候$ precp)))
到:y1<-(c(max(气候$ precp), 0))
请注意,生成的新回归和ggplot2如何自动调整绘图和轴,以正确地将最低温度与“0”降水水平的新“基数”对齐。同样,可以很容易地提升Temperature图,使其更加明显。下面的图是通过简单地将上面提到的线更改为:
“日元<——(c(150年,55岁))”
上面的线表示温度曲线的最大值与“150”降水水平相吻合,温度曲线的最小值与“55”降水水平相吻合。再次注意,ggplot2和由此产生的新的回归输出如何使图保持与轴的正确对齐。
以上可能不是理想的输出;然而,这是一个例子,说明了如何容易地操纵图形,并且在图和轴之间仍然有正确的关系。 Dag Hjermann的主题的结合提高了与情节对应的轴的识别。
这是我对如何做二次轴变换的两种看法。首先,您希望将主数据和辅助数据的范围耦合起来。这通常是混乱的,因为您不想要的变量污染了全局环境。
为了简化这一点,我们将创建一个生成两个函数的函数工厂,其中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的作者。
这在ggplot2中是不可能的,因为我认为具有单独y尺度的图(不是相互转换的y尺度)从根本上是有缺陷的。一些问题:
The are not invertible: given a point on the plot space, you can not uniquely map it back to a point in the data space. They are relatively hard to read correctly compared to other options. See A Study on Dual-Scale Data Charts by Petra Isenberg, Anastasia Bezerianos, Pierre Dragicevic, and Jean-Daniel Fekete for details. They are easily manipulated to mislead: there is no unique way to specify the relative scales of the axes, leaving them open to manipulation. Two examples from the Junkcharts blog: one, two They are arbitrary: why have only 2 scales, not 3, 4 or ten?
你也可能想要阅读Stephen Few关于双缩放轴在图形中的主题的冗长讨论,它们是最好的解决方案吗?
我发现这个答案对我帮助最大,但发现有一些边缘情况,它似乎不能正确处理,特别是消极的情况,以及极限距离为0的情况(如果我们从最大/最小数据中获取极限,就会发生这种情况)。测试似乎表明,这是一致的
我使用以下代码。这里我假设我们有[x1,x2]我们想把它变换成[y1,y2]。我处理这个问题的方法是将[x1,x2]转换为[0,1](一个足够简单的转换),然后[0,1]转换为[y1,y2]。
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)
)
#Set the limits of each axis manually:
ylim.prim <- c(0, 180) # in this example, precipitation
ylim.sec <- c(-4, 18) # in this example, temperature
b <- diff(ylim.sec)/diff(ylim.prim)
#If all values are the same this messes up the transformation, so we need to modify it here
if(b==0){
ylim.sec <- c(ylim.sec[1]-1, ylim.sec[2]+1)
b <- diff(ylim.sec)/diff(ylim.prim)
}
if (is.na(b)){
ylim.prim <- c(ylim.prim[1]-1, ylim.prim[2]+1)
b <- diff(ylim.sec)/diff(ylim.prim)
}
ggplot(climate, aes(Month, Precip)) +
geom_col() +
geom_line(aes(y = ylim.prim[1]+(Temp-ylim.sec[1])/b), color = "red") +
scale_y_continuous("Precipitation", sec.axis = sec_axis(~((.-ylim.prim[1]) *b + ylim.sec[1]), name = "Temperature"), limits = ylim.prim) +
scale_x_continuous("Month", breaks = 1:12) +
ggtitle("Climatogram for Oslo (1961-1990)")
这里的关键部分是,我们用~((.-ylim.prim[1]) *b + ylim.sec[1])转换次要y轴,然后对实际值y = ylim.prim[1]+(Temp-ylim.sec[1])/b)应用逆。我们还应该确保limits = ylim.prim。