Ggplot2: remove blank space for weekends and holidays from x-axis dates

works with problems printing stock data in ggplot2 and with the x axis, which contains spaces on weekends and holidays. this one was very useful, but I ran into various problems when trying to use ordered factors.

library(xts) library(grid) library(dplyr) library(scales) library(bdscale) library(ggplot2) library(quantmod) getSymbols("SPY", from = Sys.Date() - 1460, to = Sys.Date(), adjust = TRUE, auto.assign = TRUE) input <- data.frame(SPY["2015/"]) names(input) <- c("Open", "High", "Low", "Close", "Volume", "Adjusted") # i've tried changing rownames() to index(), and the plot looks good, but the x-axis is inaccurate # i've also tried as.factor() xaxis <- as.Date(rownames(input)) input$xaxis <- xaxis p <- ggplot(input) p <- p + geom_segment(aes(x = xaxis, xend = xaxis, y = Low, yend = High), size = 0.50) # body p <- p + geom_segment(aes(x = xaxis - 0.4, xend = xaxis, y = Open, yend = Open), size = 0.90) # open p <- p + geom_segment(aes(x = xaxis, xend = xaxis + 0.4, y = Close, yend = Close), size = 0.90) # close p <- p + scale_y_continuous(scale_y_log10()) p + ggtitle("SPY: 2015") 

enter image description here

The above chart (without red boxes) is generated with the above code segment. And the following diagrams are some of the problems when trying some solutions. First, if I try to use the index of the data frame, I will create a beautiful graph, but the x axis is not accurate; data currently ends in October, but in the chart below it ends in July: enter image description here

 xaxis <- as.Date(index(input)) 

Secondly, if I try to force the names of the growths to an ordered coefficient, I will lose my horizontal tick data (representing open and closed).
enter image description here

 xaxis <- factor(rownames(input), ordered = TRUE) 

The same question about removing horizontal ticks occurs if I use the bdscale package, but the grid lines are cleaner:

enter image description here

 p <- p + scale_x_bd(business.dates = xaxis) 
+8
date r ggplot2
source share
5 answers

You will probably have to treat dates as discrete rather than continuous. This approach with a slightly simplified version of your code might look like this:

 getSymbols("SPY", from = Sys.Date() - 1460, to = Sys.Date(), adjust = TRUE, auto.assign = TRUE) SPY <- SPY["2015/"] colnames(SPY) <- sub("SPY.","", colnames(SPY)) month_brks <- c(1,endpoints(SPY, "months")[-1]) p <- ggplot(data.frame(xaxis=seq(nrow(SPY)), SPY)) p <- p + geom_linerange(aes(x=xaxis, ymin=Low, ymax=High), size=.5) p <- p + geom_text(aes(x = xaxis, y = Open), size = 4., label="-", hjust=.7, vjust=0) # Open p <- p + geom_text(aes(x = xaxis, y = Close), size = 4., label="-", hjust=-.1, vjust=0) # close p <- p + scale_x_continuous(breaks=month_brks, labels=format(index(SPY)[month_brks], "%d %b %Y")) p <- p + labs(title="SPY: 2015", x="Date", y="Price") 

UPDATE

Updated axis label processing.

+2
source share

Well, you can configure it manually, but it's kind of hacks. First, you must use the index so that your observations are numbered from 1 to 188.

  input$xaxis <-index(as.Date(rownames(input))) 

Then your own code:

 p <- ggplot(input) p <- p + geom_segment(aes(x = xaxis, xend = xaxis, y = Low, yend = High), size = 0.50) # body p <- p + geom_segment(aes(x = xaxis - 0.4, xend = xaxis, y = Open, yend = Open), size = 0.90) # open p <- p + geom_segment(aes(x = xaxis, xend = xaxis + 0.4, y = Close, yend = Close), size = 0.90) # close p <- p + scale_y_continuous(scale_y_log10()) + ggtitle("SPY: 2015") 

And finally, I looked at the tab where the breaks should be made, and provided these labels manually:

 p + scale_x_continuous(breaks=input$xaxis[c(1,62,125,188)], labels=c("jan","apr","jul","oct")) 

NOTE HERE that I was lazy and just took the closest date for 1-jan, 1-apr 1-jul and 1-oct, because 1 jan is a holiday, the jan label is below 2-jan. And I put the β€œoct” label below below 30 seconds, the last entry in input . You can disable the course as you wish.

Of course, you can automate the tag by adding a tag field with a date and retrieve the month.

+1
source share

If you want to use bdscale for this, just tell it to use more grid lines:

 ggplot(input) + geom_segment(aes(x = xaxis, xend = xaxis, y = Low, yend = High), size = 0.50) + # body geom_segment(aes(x = xaxis - 0.4, xend = xaxis, y = Open, yend = Open), size = 0.90) + # open geom_segment(aes(x = xaxis, xend = xaxis + 0.4, y = Close, yend = Close), size = 0.90) + # close ggtitle("SPY: 2015") + xlab('') + ylab('') + scale_x_bd(business.dates=xaxis, max.major.breaks=10, labels=date_format("%b '%y")) # <==== !!!! 

enter image description here

He should put October on the axis there, but that's not so smart. Womp womp. We invite you to participate!

+1
source share

The method below uses a cut to remove spaces between missing dates, and then removes the empty space between the faces to restore the appearance of an unloaded chart.

First, we create a grouping variable, which increases every time a break occurs in the dates (code adapted from this SO answer ). We will use this later for cutting.

 input$group = c(0, cumsum(diff(input$xaxis) > 1)) 

Now we will add the following code to your plot. facet_grid creates a new facet at every place where there is a break in the date sequence due to weekends or holidays. scale_x_date adds basic mark marks once a week and small grid lines for each day, but you can customize this. The theme function eliminates face labels and vertical spaces between faces:

 p + facet_grid(. ~ group, space="free_x", scales="free_x") + scale_x_date(breaks=seq(as.Date("2015-01-01"),max(input$xaxis), "1 week"), minor_breaks="1 day", labels=date_format("%b %d, %Y")) + theme(axis.text.x=element_text(angle=-90, hjust=0.5, vjust=0.5, size=11), panel.margin = unit(-0.05, "lines"), strip.text=element_text(size=0), strip.background=element_rect(fill=NA)) + ggtitle("SPY: 2015") 

Here we get the plot. Spaces for weekends and holidays have disappeared. Major gaps occur every week. I set the weeks in the scale_x_date breaks argument to start on Thursday, since none of the holidays fell on Thursday, and so each face has a large date mark. (In contrast, default breaks will fall on Monday. Since holidays often fall on Monday, weeks with Mondays will not have a large default mark with breaks by default.) Note, however, that the interval between the main breaks essentially varies depending on how many days the market was open this week.

enter image description here

+1
source share

Failed to get OHLC to work - think you need a custom geom .

I know that this is not exactly what you asked for, but can I seduce you with a delicious candlestick chart?

 library(dplyr) library(bdscale) library(ggplot2) library(quantmod) library(magrittr) library(scales) getSymbols("SPY", from = Sys.Date() - 1460, to = Sys.Date(), adjust = TRUE, auto.assign = TRUE) input <- data.frame(SPY["2015/"]) %>% set_names(c("open", "high", "low", "close", "volume", "adjusted")) %>% mutate(date=as.Date(rownames(.))) input %>% ggplot(aes(x=date, ymin=low, ymax=high, lower=pmin(open,close), upper=pmax(open,close), fill=open<close, group=date, middle=pmin(open,close))) + geom_boxplot(stat='identity') + ggtitle("SPY: 2015") + xlab('') + ylab('') + theme(legend.position='none') + scale_x_bd(business.dates=input$date, max.major.breaks=10, labels=date_format("%b '%y")) 

enter image description here

+1
source share

All Articles