The trick here to meet the challenge is to techncially plot both data sets against the first scale y1 but report the second against a secondary axis with labels showing the original scale y2.
library(gtable)
library(grid)
PlotWithFudgeAxis = function( plot1, FudgeAxis) {
# based on: https://rpubs.com/kohske/dual_axis_in_ggplot2
plot2 <- plot1 + with(FudgeAxis, scale_y_continuous( breaks=breaks, labels=labels))
#extract gtable
g1<-ggplot_gtable(ggplot_build(plot1))
g2<-ggplot_gtable(ggplot_build(plot2))
#overlap the panel of the 2nd plot on that of the 1st plot
pp<-c(subset(g1$layout, name=="panel", se=t:r))
g<-gtable_add_grob(g1, g2$grobs[[which(g2$layout$name=="panel")]], pp$t, pp$l, pp$b,pp$l)
ia <- which(g2$layout$name == "axis-l")
ga <- g2$grobs[[ia]]
ax <- ga$children[[2]]
ax$widths <- rev(ax$widths)
ax$grobs <- rev(ax$grobs)
ax$grobs[[1]]$x <- ax$grobs[[1]]$x - unit(1, "npc") + unit(0.15, "cm")
g <- gtable_add_cols(g, g2$widths[g2$layout[ia, ]$l], length(g$widths) - 1)
g <- gtable_add_grob(g, ax, pp$t, length(g$widths) - 1, pp$b)
grid.draw(g)
}
现在所有人都可以放在一起: Below code shows, how the proposed solution could be used in a day-to-day environment . 情节调用现在不再绘制原始数据y2,而是克隆版本yf(保存在预先计算的辅助对象FudgeAxis内),其运行y1的比例 . 然后使用Kohske的辅助函数PlotWithFudgeAxis操作原始的ggplot对象,以添加第二个轴,保留y2的比例 . 它也描绘了操纵的情节 .
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")
13 回答
有时客户想要两个y尺度 . 给他们“有缺陷”的演讲往往毫无意义 . 但我确实喜欢ggplot2坚持以正确的方式做事 . 我确信ggplot实际上是教育普通用户正确的可视化技术 .
也许你可以使用faceting和scale来比较两个数据系列? - 例如看这里:https://github.com/hadley/ggplot2/wiki/Align-two-plots-on-a-page
这在ggplot2中是不可能的,因为我认为具有单独y尺度的图(不是彼此变换的y尺度)从根本上是有缺陷的 . 一些问题:
不可逆:如果绘图空间上有一个点,则无法将其唯一映射回数据空间中的某个点 .
与其他选项相比,它们相对难以正确阅读 . 有关详细信息,请参阅Petra Isenberg,Anastasia Bezerianos,Pierre Dragicevic和Jean-Daniel Fekete的A Study on Dual-Scale Data Charts .
它们很容易操纵误导:没有独特的方法来指定轴的相对比例,使它们处于操作状态 . Junkcharts博客中的两个例子:one,two
它们是任意的:为什么只有2个刻度,而不是3个,4个或10个?
您也可以阅读Stephen Few关于Dual-Scaled Axes in Graphs Are They Ever the Best Solution?主题的冗长讨论 .
从ggplot2 2.2.0开始,您可以添加这样的辅助轴(取自ggplot2 2.2.0 announcement):
大约3年前,Kohske提供了解决这一挑战的技术支柱[KOHSKE] . 关于其解决方案的主题和技术已经在Stackoverflow上的几个实例中进行了讨论[ID:18989001,29235405,21026598] . 因此,我将仅使用上述解决方案提供特定的变体和一些解释性演练 .
让我们假设我们在组G1中确实存在一些数据y1,其中组G2中的一些数据y2以某种方式相关,例如,范围/比例变换或添加一些噪音 . 因此,人们希望将数据绘制在一个图上,左边是y1,右边是y2 .
如果我们现在将数据与类似的东西一起绘制
它没有很好地对齐,因为较小的规模y1明显地被更大规模的y2折叠 .
The trick here to meet the challenge is to techncially plot both data sets against the first scale y1 but report the second against a secondary axis with labels showing the original scale y2.
因此,我们构建了第一个辅助函数CalcFudgeAxis,它计算并收集要显示的新轴的特征 . 该函数可以修改为ayones喜欢(这个只是将y2映射到y1的范围) .
什么产生一些:
现在我在第二个辅助函数PlotWithFudgeAxis(我们在其中抛出新轴的ggplot对象和辅助对象)中包含了Kohske的解决方案:
现在所有人都可以放在一起: Below code shows, how the proposed solution could be used in a day-to-day environment . 情节调用现在不再绘制原始数据y2,而是克隆版本yf(保存在预先计算的辅助对象FudgeAxis内),其运行y1的比例 . 然后使用Kohske的辅助函数PlotWithFudgeAxis操作原始的ggplot对象,以添加第二个轴,保留y2的比例 . 它也描绘了操纵的情节 .
This now plots as desired with two axis, y1 on the left and y2 on the right
上面的解决方案是,直截了当,一个有限的摇摇欲坠的黑客 . 当它与ggplot内核一起使用时,它会抛出一些警告,我们交换事后的比例等等 . 它必须小心处理,并可能在另一个设置中产生一些不希望的行为 . 同样,可能需要使用辅助函数来调整以获得所需的布局 . 图例的位置是一个问题(它将放置在面板和新轴之间;这就是我下垂的原因) . 2轴的缩放/对齐也有点挑战性:当两个刻度包含“0”时,上面的代码很好地工作,否则一个轴会移位 . 所以肯定有一些改进的机会......
如果想要保存pic,必须将呼叫包装到设备打开/关闭中:
采取上述答案和一些微调(以及任何它的 Value ),这是一种通过
sec_axis
实现两个量表的方法:假设一个简单的(纯虚构的)数据集
dt
:五天,它跟踪的数量中断VS 生产环境 力:(两列的范围相差约5倍) .
以下代码将绘制它们用完整个y轴的两个系列:
这是结果(上面的代码有些颜色调整):
要点(除了在指定y_scale时使用
sec_axis
是 multiply ,在指定系列时每个值为第2个数据系列为5 . 为了在sec_axis定义中获得标签,它需要 dividing by 5(和格式化) . 因此,上面代码中的一个关键部分实际上是geom_line中的*5
和sec_axis中的~./5
(将当前值.
除以5的公式) .相比之下(我不想在这里判断方法),这是两个图表在彼此之上的样子:
你可以自己判断哪一个更能传达信息(“不要扰乱工作中的人!”) . 猜猜这是一个公平的决定方式 .
两个图像的完整代码(上面是's not really more than what',只是完成并准备好运行)在这里:https://gist.github.com/sebastianrothbucher/de847063f32fdff02c83b75f59c36a7d这里有更详细的解释:https://sebastianrothbucher.github.io/datascience/r/visualization/ggplot/2018/03/24/two-scales-ggplot-r.html
下面的文章帮助我将ggplot2生成的两个图组合在一行上:
Multiple graphs on one page (ggplot2) by Cookbook for R
以下是这种情况下代码的外观:
对我来说,棘手的部分是找出两个轴之间的转换函数 . 我使用了myCurveFit .
Finding the transformation function
转换功能:
f(y1) = 0.025*x + 2.75
转换功能:
f(y1) = 40*x - 110
Plotting
注意如何在
ggplot
调用中使用转换函数来转换数据"on-the-fly"第一个
stat_summary
调用是为第一个y轴设置基础的调用 . 调用第二个stat_summary
调用来转换数据 . 请记住,所有数据都将作为第一个y轴的基础 . 因此,需要针对第一个y轴对数据进行归一化 . 为此,我在数据上使用转换函数:y=packetOkSinr*40 - 110
现在要转换第二个轴,我在
scale_y_continuous
调用中使用相反的函数:sec.axis=sec_axis(~.*0.025+2.75, name="y_second")
.我们绝对可以使用基本R函数
plot
构建具有双Y轴的图 .您可以在变量上使用
facet_wrap(~ variable, ncol= )
来创建新的比较 . 它不在同一轴上,但它是相似的 .我承认并同意hadley(和其他人),单独的y尺度是"fundamentally flawed" . 话虽如此 - 我经常希望
ggplot2
具有该功能 - 特别是当数据处于wide-format时,我很快想要可视化或检查数据(即仅供个人使用) .虽然
tidyverse
库可以很容易地将数据转换为长格式(这样facet_grid()
可以工作),但这个过程仍然不是很简单,如下所示:您可以创建应用于第二个geom和右y轴的缩放系数 . 这源于塞巴斯蒂安的解决方案 .
注意:使用
ggplot2
v3.0.0The answer by Hadley对Stephen Few的报告Dual-Scaled Axes in Graphs Are They Ever the Best Solution?给出了一个有趣的参考 .
我不知道OP对"counts"和_1775939的含义是什么,但快速搜索给了我Counts and Rates,所以我得到一些关于北美登山事故1的数据:
然后我尝试在上述报告的第7页中提到的图表(并且按照OP的要求将计数图表绘制成条形图并将费率绘制为折线图):
这就是结果:
但我不喜欢它而且我无法轻易将传奇放在上面......
1 WILLIAMSON,Jed,et al . 2005年北美登山事故 . 登山者书籍,2005年 .
有一些常见的使用情况决定y轴,例如climatograph显示月温度和降水量 . 这是一个简单的解决方案,从Megatron的解决方案中推广,允许您将变量的下限设置为零以外的其他值:
示例数据:
手动设置每个轴的限制:
以下内容根据这些限制进行必要的计算,并使图表本身:
如果要确保红线对应于右侧y轴,可以在代码中添加
theme
句子:右手轴的颜色: