同一图表上的多个日内时间序列

Multiple intraday time series on the same chart

我正在努力解决我认为在 R 中应该非常简单的事情。

请考虑以下示例:

library(dplyr)
library(tidyverse)

time = c('2013-01-03 22:04:21.549', '2013-01-03 22:04:22.349', '2013-01-03 22:04:23.559', '2013-01-03 22:04:25.559' )
value1 = c(1,2,3,4)
value2 = c(400,500,444,210)

data <- data_frame(time, value1, value2)
data <-data %>%  mutate(time = as.POSIXct(time))

> data
# A tibble: 4 × 3
                 time value1 value2
               <dttm>  <dbl>  <dbl>
1 2013-01-03 22:04:21      1    400
2 2013-01-03 22:04:22      2    500
3 2013-01-03 22:04:23      3    444
4 2013-01-03 22:04:25      4    210

我的问题很简单:

我想在具有两个不同 Y 轴的同一张图表上绘制 value1value2

事实上,正如您在示例中看到的那样,两个变量之间的单位差异很大,因此仅使用一个轴会压缩其中一个时间序列。

令人惊讶的是,为这个问题得到一个漂亮的图表已被证明是非常困难的。我很生气(当然,不是真的很生气。只是困惑 ;))。

在 Python Pandas 中,可以简单地使用:

data.set_index('time', inplace = True)
data[['value1', 'value2']].plot(secondary_y = 'value2')

在 Stata 中,可以简单地说:

twoway (line value1 time, sort ) (line value2 time, sort)

在R中,我不知道该怎么做。我在这里错过了什么吗? Base R,ggplot2,一些奇怪的包,任何具有合适自定义选项的可行解决方案在这里都可以。

可能满足您需求的基本 R hack。我将不遗余力地阐明哪些组件(蓝色与红色)负责哪些组件。它很丑陋,但它展示了必要的要点。使用您的数据:

# making sure the left and right sides have the same space
par(mar = c(4,4,1,4) + 0.1)
# first plot
plot(value1 ~ time, data = data, pch = 16, col = "blue", las = 1,
     col.axis = "blue", col.lab = "blue")
grid(lty = 1, col = "blue")
# "reset" the whole plot for an overlay
par(fig = c(0,1,0,1), new = TRUE)
# second plot, sans axes and other annotation
plot(value2 ~ time, data = data, pch = 16, col = "red",
     axes = FALSE, ann = FALSE)
grid(lty = 3, col = "red")
# add the right-axis and label
axis(side = 4, las = 1, col.axis = "red")
mtext("value2", side = 4, line = 3, col = "red")

我添加网格是为了突出一个美学问题:它们没有对齐 "neatly"。如果您对此表示满意,请随时停下来。

这是一种方法(尚未使用 significantly-different 数据范围进行测试)。 (根据您的数据和偏好,肯定还有其他方法。)

# one way that may "normalize" the y-axes for you, so that the grid should be identical
y1 <- pretty(data$value1)
y1n <- length(y1)
y2 <- pretty(data$value2)
y2n <- length(y2)
if (y1n < y2n) {
  y1 <- c(y1, y1[y1n] + diff(y1)[1])
} else if (y1n > y2n) {
  y2 <- c(y2, y2[y2n] + diff(y2)[1])
}

以及随后的情节,添加ylim=range(...)

# making sure the left and right sides have the same space
par(mar = c(4,4,1,4) + 0.1)
# first plot
plot(value1 ~ time, data = data, pch = 16, col = "blue", las = 1, ylim = range(y1),
     col.axis = "blue", col.lab = "blue")
grid(lty = 1, col = "blue")
# "reset" the whole plot for an overlay
par(fig = c(0,1,0,1), new = TRUE)
# second plot, sans axes and other annotation
plot(value2 ~ time, data = data, pch = 16, col = "red", ylim = range(y2),
     axes = FALSE, ann = FALSE)
grid(lty = 3, col = "red")
# add the right-axis and label
axis(side = 4, las = 1, col.axis = "red")
mtext("value2", side = 4, line = 3, col = "red")

(虽然 red-blue 交替的网格线很糟糕,但它们证明网格实际上对齐得很好。)

注意:par(fig = c(0,1,0,1), new = TRUE) 的使用有点脆弱。在绘图之间更改边距或进行其他重大更改等操作很容易破坏叠加层,除非您进行一些手动操作以查看加法过程的实际效果,否则您不会真正知道。在此 "check" 过程中,您可能希望从第二个图中删除 axes=F, ann=F 以确认至少框和 x-axis 按预期对齐。

ggplot2 的 2.2.0 版允许定义辅助轴。现在,第二个时间序列可以适当缩放并显示在同一个图表中:

data %>% 
  mutate(value2 = value2 / 100) %>%    # scale value2
  gather(variable, value, -time) %>%   # reshape wide to long
  ggplot(aes(time, value, colour = variable)) + 
  geom_point() + geom_line() + 
  scale_y_continuous(name = "value1", sec.axis = sec_axis(~ . * 100, name = "value2"))