Suppose I have a plot like this:
DF <- data.frame(date=Sys.Date() - (-100):100, y=rnorm(201))
library("ggplot2")
library(scales)
ggplot(DF, aes(x=date, y=y)) +
geom_point() +
scale_x_date(breaks = "1 month", minor_breaks = "1 week", labels=date_format("%b"))
Here I want to include major lines and labels at every month and minor lines at every week. This works well, but now I would like to include the year behind the abbreviated month, but only for the first month of that year in the plot. Thus, the labels should read sep 2014. okt, nov, dec, jan 2015, feb, mrt....
Is this possible?
You can do it with a custom date formatter to remove duplicated years:
my_date_format <- function()
{
function(x)
{
m <- format(x,"%b")
y <- format(x,"%Y")
ifelse(duplicated(y),m,paste(m,y))
}
}
ggplot(DF, aes(x=date, y=y)) +
geom_point() +
scale_x_date(breaks = "1 month", minor_breaks = "1 week", labels=my_date_format())
Related
Below I create a reproducible example chart ranging for 10000 days. As you can see this chart is highly informative and value adding, but it will do for the example.
Instead of one x label every 10 years, I would like to force a label every year. How can this be achieved?
library(ggplot2)
library(tidyr)
exdays <- 1:10000
exdata <- sin(exdays)
exdate <- as_date("2022-01-01")+days(exdays)
exdat <- tibble(exdate, exdata)
p1 <- ggplot(exdat, aes(x=exdate, y=exdata)) +
geom_line(color="darkred", size=0.7) +
ggtitle("example")
p1
You maybe want this using scale_x_date with date_breaks of 1 year where you specify the date_labels:
library(ggplot2)
library(tidyr)
exdays <- 1:10000
exdata <- sin(exdays)
exdate <- as_date("2022-01-01")+days(exdays)
exdat <- tibble(exdate, exdata)
p1 <- ggplot(exdat, aes(x=exdate, y=exdata)) +
geom_line(color="darkred", size=0.7) +
scale_x_date(date_breaks = "1 year", date_labels = "%Y") +
ggtitle("example")
p1
Output:
This works
p1 <- ggplot(exdat, aes(x=exdate, y=exdata)) +
geom_line(color="darkred", size=0.7) +
ggtitle("example") + scale_x_date(date_breaks = "1 year")
Let's consider data:
x <- c(
"1900-01-01", "1900-04-01", "1900-07-01", "1900-10-01", "1901-02-01",
"1901-05-01", "1901-08-01",
"1901-11-01", "1902-02-01", "1902-05-01", "1902-08-01", "1902-11-01", "1903-02-01"
)
x <- as.Date(x)
y <- 1:length(x)
df <- data.frame("Date" = x, "Preds" = y)
I want to make a plot using ggplot but with every date marked (in format %M-%Y):
My work so far:
ggplot(df, aes(x = Date, y = Preds)) +
geom_line() +
scale_x_date(date_breaks = "3 month", date_labels = "%b-%Y")
However this code brings one problem: starting day is in February, whereas our dates starts in Jan.
Do you know how can I back my time by one month ? And Do I have to specify exactly that I want to make a breaks by three months ? Or is there any generic solution that would allow me to just have ticks for every date without this specification ?
We just need to include breaks = x:
ggplot(df, aes(x = Date, y = Preds)) +
geom_line() +
scale_x_date(breaks = x, date_labels = "%b-%Y")
This code produces a single boxplot:
df <- data.frame(value = rnorm(62), my.date = seq(as.Date("2013-12-01"), as.Date("2014-01-31"), by="1 day"))
library(ggplot2)
ggplot(df, aes(as.Date(my.date), value)) + geom_boxplot() + scale_x_date(minor_breaks = "1 week", labels = date_format("%W\n%b"))
How can I produce a plot that has single boxplots for each week between 1 December and 31 January? So within the single plot, there should be about 8 boxplots. Would prefer solution that uses either ggplot() or scale_x_date().
One option is to transform your date before using ggplot
library(ggplot2)
df <- data.frame(value = rnorm(62),
my.date = seq(as.Date("2013-12-01"), as.Date("2014-01-31"), by="1 day"))
weeks <- format(df$my.date, "%Y/%W")
weeks <- factor(weeks, levels = unique(weeks))
ggplot(df, aes(weeks, value)) +
geom_boxplot()
library(ggplot2)
ggplot(df, aes(format(as.Date(my.date), "%W\n%b"), value)) + geom_boxplot()
Edit:
To order the dates:
ggplot(df, aes(reorder(format(as.Date(my.date), "%W\n%b"),
as.Date(my.date)),
value)) +
geom_boxplot()
This fulfils #luciano's request to retain functionality of scale_x_date
library('scales')
library(ggplot2)
df <- data.frame(value = rnorm(62), my.date = seq(as.Date("2013-12-01"), as.Date("2014-01-31"), by="1 day"))
ggplot(df, aes(x=as.Date(my.date), y=value, group=format(as.Date(my.date),"%W-%b"))) + geom_boxplot() + scale_x_date(date_breaks = "1 week", date_labels="%Y-%b-%d")
Alternatively, if you don't want the data grouped by week# - which gives you the split around most new years - you can group by week ending Sundays as below. Adjusting from the Sunday weekending, to say Friday, can be achieved with some such code
ceiling_date(x, "week") + ifelse(weekdays(x) %in% c("Saturday", "Sunday"), 5, -2)
ggplot(df, aes(x=as.Date(my.date), y=value, group=ceiling_date(my.date, "week"))) + geom_boxplot() + scale_x_date(date_breaks = "1 week", date_labels="%Y-%b-%d")
Problem
I would like to format my X-axis (time) so that the weekends are clearly visible. I would like to display the date as well as the day of the week.
Current situation
I do this with (full code below)
scale_x_date(breaks=myData$timestamp,
labels=paste(
substr(format(myData$timestamp, "%a"),1,1),
format(myData$timestamp, "%d"),
sep="\n")
)
which gives me
Wanted situation
I would rather have a one letter abbreviation for the weekdays since it became a bit tight there.. Also, I'd like to color sundays (and holidays really) in red. Here's what I mean (made with GIMP). Note how the first Monday and last Friday was added by using
scale_x_date(breaks = "1 day",
minor_breaks = "1 days",
labels = date_format("%a\n%d"),
name="")
However, then I get a three letter abbreviation of the weekdays, which I removed in GIMP.
Here is the complete code for this example.
library(ggplot2)
library(scales)
library(reshape2)
minimumTime <- as.Date("2014-07-01")
maximumTime <- as.Date("2014-07-31")
x <- seq(minimumTime,maximumTime, by="1 day")
y1 <- sin(as.numeric(x)/3)
y2 <- cos(as.numeric(x)/3)
myData <- data.frame(timestamp=x, y1=y1, y2=y2)
myData <- melt(myData, id.vars="timestamp")
rects <- data.frame(saturdays=myData[weekdays(myData$timestamp) == "Saturday","timestamp"]-0.5, sundays = myData[weekdays(myData$timestamp) == "Saturday","timestamp"]+1.5)
myPlot <- ggplot() +
geom_rect(data=rects, aes(xmin=saturdays, xmax=sundays,ymin=-Inf, ymax=Inf), alpha=0.1) +
geom_line(data=myData, aes(x=timestamp, y=value, colour=variable,size=1)) +
geom_point(data=myData, aes(x=timestamp, y=value, colour=variable,size=2)) +
scale_x_date(breaks=myData$timestamp, labels=paste(substr(format(myData$timestamp, "%a"),1,1),format(myData$timestamp, "%d"),sep="\n")) +
#scale_x_date(breaks = "1 day", minor_breaks = "1 days", labels = date_format("%a\n%d"), name="") +
scale_size_continuous(range = c(1.5,5), guide=FALSE)
So to sum up:
Is there a way to color specific breaks in another color?
Is there a way change to the labels manually and still have them for the Monday and
the Friday at the beginning and the end in this case?
Also, if there's a way to have the lines of each label centered, that would be
awesome :)
Thank you!
You can use your custom formater for labels also using breaks="1 day" argument, you just have to use function(x) after labels= and replace myDate$timestamp with x. This will also solve the third problem.
+ scale_x_date(breaks="1 day",
labels= function(x) paste(substr(format(x, "%a"),1,1),format(x, "%d"),sep="\n"))
Or you can make your transformation as seperate function and then use it for labels=.
my_date_trans<-function(x) {
paste(substr(format(x, "%a"),1,1),format(x, "%d"),sep="\n")
}
+ scale_x_date(breaks="1 day",labels=my_date_trans)
To change colors for labels you should use theme() and axis.text.x=. Here I using vector of colors that contains 6 time black and then red as your scale starts with Monday. Those colors are then repeated.
ggplot() +
geom_rect(data=rects, aes(xmin=saturdays, xmax=sundays,ymin=-Inf, ymax=Inf), alpha=0.1) +
geom_line(data=myData, aes(x=timestamp, y=value, colour=variable,size=1)) +
geom_point(data=myData, aes(x=timestamp, y=value, colour=variable,size=2)) +
scale_x_date(breaks="1 day",labels=my_date_trans)+
scale_size_continuous(range = c(1.5,5), guide=FALSE)+
theme(axis.text.x=element_text(color=c(rep("black",6),"red")))
This code produces a single boxplot:
df <- data.frame(value = rnorm(62), my.date = seq(as.Date("2013-12-01"), as.Date("2014-01-31"), by="1 day"))
library(ggplot2)
ggplot(df, aes(as.Date(my.date), value)) + geom_boxplot() + scale_x_date(minor_breaks = "1 week", labels = date_format("%W\n%b"))
How can I produce a plot that has single boxplots for each week between 1 December and 31 January? So within the single plot, there should be about 8 boxplots. Would prefer solution that uses either ggplot() or scale_x_date().
One option is to transform your date before using ggplot
library(ggplot2)
df <- data.frame(value = rnorm(62),
my.date = seq(as.Date("2013-12-01"), as.Date("2014-01-31"), by="1 day"))
weeks <- format(df$my.date, "%Y/%W")
weeks <- factor(weeks, levels = unique(weeks))
ggplot(df, aes(weeks, value)) +
geom_boxplot()
library(ggplot2)
ggplot(df, aes(format(as.Date(my.date), "%W\n%b"), value)) + geom_boxplot()
Edit:
To order the dates:
ggplot(df, aes(reorder(format(as.Date(my.date), "%W\n%b"),
as.Date(my.date)),
value)) +
geom_boxplot()
This fulfils #luciano's request to retain functionality of scale_x_date
library('scales')
library(ggplot2)
df <- data.frame(value = rnorm(62), my.date = seq(as.Date("2013-12-01"), as.Date("2014-01-31"), by="1 day"))
ggplot(df, aes(x=as.Date(my.date), y=value, group=format(as.Date(my.date),"%W-%b"))) + geom_boxplot() + scale_x_date(date_breaks = "1 week", date_labels="%Y-%b-%d")
Alternatively, if you don't want the data grouped by week# - which gives you the split around most new years - you can group by week ending Sundays as below. Adjusting from the Sunday weekending, to say Friday, can be achieved with some such code
ceiling_date(x, "week") + ifelse(weekdays(x) %in% c("Saturday", "Sunday"), 5, -2)
ggplot(df, aes(x=as.Date(my.date), y=value, group=ceiling_date(my.date, "week"))) + geom_boxplot() + scale_x_date(date_breaks = "1 week", date_labels="%Y-%b-%d")