ggplot 新的scale_x函数无法正常工作

phi*_*ver 6 r ggplot2 axis-labels

为了摆脱股票的非交易日(又称周末)而ggplot不是日期,我使用数据中的行数,然后添加中断和标签。“ works ”下面的代码完成了这个任务,并像chartSeriesquantmod 包中那样绘制数据。ggplot添加不存在的信息或显示差距,具体取决于您制作的图表类型。对于处理股票价格来说,这并不方便。因此,作品部分。

但由于这只是一个标签问题,因此轴转换器函数会更符合逻辑并且更易于使用。我尝试创建一个scale_x_finance函数(请参阅“不起作用”部分),但我一定是错误地解释了反函数,因为我只返回了 1 个日期的图,而不是整个时间序列。

我读了几个这样的问题,比如这个这个,但到目前为止还没有运气。

我知道有一个名为 的包bdscale存在,但它已经六年多没有更新了,而且它创建的中断/标签不是我需要的。

使用的结果scale_x_finance应该类似于作品部分的情节。我想知道是否有人知道我在这里缺少什么。

我在问题底部添加了一些测试数据。

作品

library(ggplot2)

# get the start date and the last days of the month for breaks and label positions
get_breaks <- function(x) {
  out <- c(1, which(ave(as.numeric(x),format(x,"%Y%m"), FUN = function(x) x == max(x)) == 1))
}

# use 1:nrow to be able to use scale_x_continuous
ggplot(test_data, aes(x = 1:nrow(test_data))) + 
  geom_line(aes(y = close)) +
  scale_x_continuous(name = "date",
                     breaks = get_breaks(test_data$date),
                     labels = test_data$date[get_breaks(test_data$date)])
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

不起作用

scale_x_finance <- function (...,
                             dates,
                             breaks = get_breaks(dates)){
  
  my_transformer <- function(dates, breaks = get_breaks(dates)) {
    
    transform <- function(dates) seq_along(dates) 
    inverse <- function(nums) dates[nums] 
    
    scales::trans_new(name = "date",
                      transform = transform,
                      inverse = inverse,
                      breaks = breaks,
                      domain = range(dates))
  }
  
  scale_x_continuous(name = "date",
                     trans = my_transformer(dates = dates, breaks = breaks),
                     ...)
}


ggplot(test_data, aes(x = date)) + 
  geom_line(aes(y = close)) +
  scale_x_finance(dates = test_data$date) 
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

数据:

test_data <- structure(list(date = structure(c(18995, 18996, 18997, 18998, 
                                               18999, 19002, 19003, 19004, 19005, 19006, 19010, 19011, 19012, 
                                               19013, 19016, 19017, 19018, 19019, 19020, 19023, 19024, 19025, 
                                               19026, 19027, 19030, 19031, 19032, 19033, 19034, 19037, 19038, 
                                               19039, 19040, 19041, 19045, 19046, 19047, 19048, 19051, 19052, 
                                               19053, 19054, 19055, 19058, 19059, 19060, 19061, 19062, 19065, 
                                               19066, 19067, 19068, 19069, 19072, 19073, 19074, 19075, 19076, 
                                               19079, 19080, 19081, 19082), class = "Date"), 
                            close = c(182.009995, 179.699997, 174.919998, 172, 172.169998, 172.190002, 175.080002, 
                                      175.529999, 172.190002, 173.070007, 169.800003, 166.229996, 164.509995, 
                                      162.410004, 161.619995, 159.779999, 159.690002, 159.220001, 170.330002, 
                                      174.779999, 174.610001, 175.839996, 172.899994, 172.389999, 171.660004, 
                                      174.830002, 176.279999, 172.119995, 168.639999, 168.880005, 172.789993, 
                                      172.550003, 168.880005, 167.300003, 164.320007, 160.070007, 162.740005, 
                                      164.850006, 165.119995, 163.199997, 166.559998, 166.229996, 163.169998, 
                                      159.300003, 157.440002, 162.949997, 158.520004, 154.729996, 150.619995, 
                                      155.089996, 159.589996, 160.619995, 163.979996, 165.380005, 168.820007, 
                                      170.210007, 174.070007, 174.720001, 175.600006, 178.960007, 177.770004, 
                                      174.610001)), row.names = c(NA, 62L), class = "data.frame")
Run Code Online (Sandbox Code Playgroud)

All*_*ron 4

问题出在你的my_transformer对象上。这需要能够获取数据中不存在的日期并适当地转换它们。例如,当 ggplot 计算绘图限制时,它可能会传递不属于向量的两个日期的向量dates。该transform函数会将两个日期的任何向量转换为向量c(1, 2),这不是你想要的 - 你需要根据你的向量插入任意日期dates

类似的概念适用于该inverse函数,该函数必须处理任意数字并将它们反向转换为日期。

我认为处理这个问题的最简单方法是确保日期在内部都以数字方式处理my_transformer,然后通过调用在 ghe rnd 处回顾性地给出标签scale_x_continuous

所以你的变压器可能是这样的:

library(ggplot2)

my_transformer <- function(dates) {
  dates <- as.numeric(dates)
  pos   <- seq_along(dates) - 1
  
  transform <- function(x) {
    if(all(is.na(x))) return(x)
    x <- as.numeric(x)
    y <- numeric(length(x))
    in_range <- x >= min(dates) & x <= max(dates)
    y[in_range] <- approx(dates, pos, x[in_range])$y
    y[x < min(dates)] <- x[x < min(dates)] - min(dates)
    y[x > max(dates)] <- x[x > max(dates)] - max(dates) + max(pos)
    y
  }
  
  inverse <- function(x) {
    if(all(is.na(x))) return(x)
    x <- as.numeric(x)
    y <- numeric(length(x))
    y[is.na(x)] <- NA
    in_range <- x >= 0 & x <= max(pos) & !is.na(x)
    y[in_range] <- approx(pos, dates, x[in_range])$y
    y[x < 0] <- x[x < 0] + min(dates)
    y[x > max(pos)] <- max(dates) + x[x > max(pos)] - max(pos)
    y
  }
  
    scales::trans_new(name = "date",
                      transform = transform,
                      inverse   = inverse)
}
Run Code Online (Sandbox Code Playgroud)

scale_x_finance是这样的:

scale_x_finance <- function (dates, ...) {
  
  scale_x_continuous(name = "date",  ..., 
                     trans = my_transformer(dates),
                     labels = ~ as.Date(.x, origin = "1970-01-01"))
}
Run Code Online (Sandbox Code Playgroud)

这样你的情节调用就只是:

ggplot(test_data, aes(x = date, y = close)) + 
  geom_line(aes(y = close)) +
  scale_x_finance(dates = test_data$date)
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

为了证明数据中的间隙只是删除了 x 轴上的空间(这似乎是这里的最终目标),我们可以删除一周的数据,并看到缺失日期两侧的日期靠得更近:

test_data <- test_data[-(25:31),]

ggplot(test_data, aes(x = date, y = close)) + 
  geom_line(aes(y = close)) +
  scale_x_finance(dates = test_data$date)
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述