1

I want to replicate the style of tisPlot, from the tis package, in ggplot2. I am having trouble creating the line segments capping both the left and right y-axes in ggplot2. I have attached example code that shows a basic tisPlot along with what I have done to match that style in ggplot2. Does anyone here know how to solve this last part of the problem, namely, adding line segments at the tops of the y-axes? Thanks.

library(tis)
library(tidyverse)
library(lubridate)

set.seed(5)

# make example time-series data
firstTis <- tis(cumsum(rnorm(120)), start = c(1996, 1), freq = 12)

tisPlot(firstTis)

# put example times-series data into a dataframe
df <- data_frame(
  date = firstTis %>% time() %>% date_decimal() %>% date(),  
  firstTis = firstTis %>% as.numeric()
  ) 

ggplot(df) +
  geom_line(aes(x = date, y = firstTis)) +
  theme_classic() + 
  theme(
    axis.ticks.length = unit(- 7, "pt"),                                               
    axis.text.x = element_text(margin = margin(t = 10, b = 0, unit = 
"pt")),
axis.text.y = element_text(margin = margin(r = 10, l = 0, unit = "pt"), 
color = "white"),     
axis.text.y.right = element_text(margin = margin(r = 0,  l = 10, unit = 
"pt"), color = "black")
) +   
scale_y_continuous(sec.axis = dup_axis(name = "")) +
Niko
  • 13
  • 2

1 Answers1

2

Updated solution:

v3

You can specify the y-axis limits in coord_cartesian(), & set the multiplicative / additive expansion constants for y-axis to 0. Segments added to simulate the appearance of longer ticks at the top. Breaks can be set dynamically using pretty() from the base package, with manual adjustment in n if the default settings return too many / too few breaks:

date.extension <- diff(range(df$date))*0.02
y.breaks <- pretty(df$firstTis, n = 4) # larger n will return more breaks
y.range <- range(y.breaks)

> date.extension
Time difference of 72.46 days
> y.breaks
[1] -10  -5   0   5  10
> y.range
[1] -10  10

Plot:

p <- ggplot(df) +

  # add horizontal line segments aligned to y-axis upper limit
  annotate("segment", y = max(y.range), yend = max(y.range),
           x = min(df$date)-date.extension, 
           xend = min(df$date) + date.extension) +
  annotate("segment", y = max(y.range), yend = max(y.range),
           x = max(df$date)-date.extension, 
           xend = max(df$date) + date.extension) +

  geom_line(aes(x = date, y = firstTis)) +
  theme_classic() +
  theme(
    axis.ticks.length = unit(- 7, "pt"),
    axis.text.x = element_text(margin = margin(t = 10, b = 0, unit = "pt")),
    axis.text.y = element_text(margin = margin(r = 10, l = 0, unit = "pt"), color = "white"),     
    axis.text.y.right = element_text(margin = margin(r = 0,  l = 10, unit = "pt"), color = "black")
  ) +

  scale_x_date(expand = c(0, 0)) +
  scale_y_continuous(name = "", 
                     breaks = y.breaks,
                     expand = c(0, 0),             # no expansion beyond specified limits
                     sec.axis = dup_axis()) +
  coord_cartesian(xlim = c(min(df$date)-date.extension, 
                           max(df$date)+date.extension),
                  ylim = y.range)
p

(Optional) Prevent line segments at the top from being clipped off:

gt <- ggplotGrob(p)
gt$layout$clip[gt$layout$name=="panel"] <- "off"
grid::grid.draw(gt)

Note that the line segments are coded to extend 2% of the total date range (~72 days in this example) in either direction, from min(df$date) & max(df$date). So if your x-axis range changes drastically (e.g. from 20 years to 1 year), it should still work.

Z.Lin
  • 28,055
  • 6
  • 54
  • 94
  • Thanks for your solution, but I am looking for something that is distinct from and longer than a tick mark. – Niko Sep 12 '17 at 15:00
  • This is really great, thanks. One thing I notice though, is that the annotated lines at the top look thinner than the tick marks and axis lines. Are the full widths of the annotated lines being clipped? – Niko Sep 12 '17 at 15:34
  • Also, you have hard-coded the position of the max break, as well as the y values for the annotated line segments as 10. Is there a way to dynamically locate the coordinates of the ends of the axes? – Niko Sep 12 '17 at 15:40
  • @Niko: Have included dynamic derivation of y-axis breaks / ends. If clipping is a concern (though I can't tell at this line thickness myself), you can convert the plot to `gtable` class & turn off clipping from there. – Z.Lin Sep 12 '17 at 15:58
  • See [here](https://stackoverflow.com/a/41575505/8449629) for more details on turning off clipping. – Z.Lin Sep 12 '17 at 15:59
  • Thank you so much for the help, it is greatly appreciated. – Niko Sep 12 '17 at 16:06