ggplot2 timeseries chart with background shading

2019-05-22 20:59发布

I have an excel graph that I want to create in R.

Excel Chart

I tried recreating it with some dummy data

a<-rnorm(12)
a_ts<-ts(a, start=c(2015, 1), frequency=12)
a_time<-time(a_ts)
a_series<-ts.union(ret=a_ts, date=a_time)
a_series_df<-as.data.frame(a_series)

ggplot() +
geom_rect(data=data.frame(xmin=decimal_date(as.Date(c("2015-01-01"))),
xmax=decimal_date(as.Date(c("2015-05-31"))), ymin=-Inf, ymax=Inf),
aes(xmin=xmin,xmax=xmax,ymin=ymin,ymax=ymax), fill="pink", alpha=0.5) +
geom_line(data = a_series_df, aes(x=date,y=ret, color='blue')) +
theme(axis.text.x=element_text(angle=90,hjust=1,vjust=0.5)) 
#this does not work
#scale_x_date(breaks = "1 month", minor_breaks = "1 month",   labels=date_format("%B-%d")) +
#scale_y_continuous(labels = scales::percent)

which looks like this

R version

I am struggling with the date conversions and also setting the x and y origins to zero and getting the axis labels right, the last two lines of code work for non-date data points. I would also like to have a legend below the chart for series1, series2 and and entry for the shaded area.

Any help would be appreciated.

Update after applying the suggestions:

X axis should be at y = 0 everything else should stay as it is, as in the excel

标签: r charts ggplot2
3条回答
贼婆χ
2楼-- · 2019-05-22 21:35

Below is an example that should get you most of the way there. This uses the lubridate package for working with date and times (Dates in this case). This shows you one way you could plot two separate lines on the same plot with most of the requested modifications. In this example an alpha of 0.05 is used.

library(lubridate)
library(ggplot2)

### Set up dummy data.
dayVec     <- seq(ymd('2016-01-01'), ymd('2016-01-10'), by = '1 day')
set.seed(1234)
dayCount   <- length(dayVec)
dayValVec1 <- rnorm(dayCount)
dayValVec2 <- rnorm(dayCount)
dayDF      <- data.frame(Date = rep(dayVec, 2),
                         DataType = factor(c(rep('A', dayCount), rep('B', dayCount))),
              Value = c(dayValVec1, dayValVec2))

### Dummy data in data frame (DataType is a factor)
dayDF
##          Date DataType       Value
## 1  2016-01-01        A -1.20706575
## 2  2016-01-02        A  0.27742924
## 3  2016-01-03        A  1.08444118
## 4  2016-01-04        A -2.34569770
## 5  2016-01-05        A  0.42912469
## 6  2016-01-06        A  0.50605589
## 7  2016-01-07        A -0.57473996
## 8  2016-01-08        A -0.54663186
## 9  2016-01-09        A -0.56445200
## 10 2016-01-10        A -0.89003783
## 11 2016-01-01        B -0.47719270
## 12 2016-01-02        B -0.99838644
## 13 2016-01-03        B -0.77625389
## 14 2016-01-04        B  0.06445882
## 15 2016-01-05        B  0.95949406
## 16 2016-01-06        B -0.11028549
## 17 2016-01-07        B -0.51100951
## 18 2016-01-08        B -0.91119542
## 19 2016-01-09        B -0.83717168
## 20 2016-01-10        B  2.41583518

ggplot(dayDF, aes(Date, Value, colour = DataType)) +
    geom_line() +
    geom_rect(aes(xmin=ymd('2016-01-02'),
                  xmax = ymd('2016-01-06'),
                  ymin = -Inf,
                  ymax = Inf), fill = 'pink', alpha = 0.05) +
    scale_x_datetime(labels = date_format('%b-%d'), breaks = date_breaks('1 day'), expand=c(0,0)) +
    theme(axis.text.x     = element_text(angle=90),
          legend.position = 'bottom')

enter image description here

EDIT

Note, the date_breaks value can be changed to 1 month if you want to plot by month. This example is just per day.

查看更多
太酷不给撩
3楼-- · 2019-05-22 21:57

Just make sure the values are actual Date objects:

library(lubridate)
library(ggplot2)
library(scales)

set.seed(1492)
a <- rnorm(12)

a_ts <- ts(a, start=c(2015, 1), frequency=12)
a_time <- time(a_ts)

a_series <- ts.union(ret=a_ts, date=a_time)
a_series_df <- as.data.frame(a_series)
a_series_df$date <- as.Date(as.character(a_series_df$date), "%Y.%j")

rect_df <- data.frame(xmin=as.Date(c("2015-01-01")),
                      xmax=as.Date(c("2015-05-31")))

ggplot() +
  geom_rect(data=rect_df,
            aes(xmin=xmin, xmax=xmax, ymin=-Inf, ymax=Inf), 
            fill="pink", alpha=0.5) +
  geom_line(data = a_series_df, aes(x=date, y=ret), color='blue') +
  scale_x_date(expand=c(0,0), labels=date_format("%b-%d"), 
               date_breaks="1 month") +
  scale_y_continuous(expand=c(0,0), labels=percent) +
  labs(x=NULL, y=NULL) +
  theme_bw() +
  theme(axis.text.x=element_text(angle=90, hjust=1, vjust=0.5)) +
  theme(panel.grid.minor=element_blank()) +
  theme(panel.grid.major.x=element_blank()) +
  theme(axis.ticks=element_blank())

enter image description here

查看更多
Summer. ? 凉城
4楼-- · 2019-05-22 21:58
#Make data
a_time <- time(ts(rnorm(12), start=c(2015, 1), frequency=12))
a_series <-ts.union(ret=a_ts, date=a_time)
a_series_df <-as.data.frame(a_series)
a_series_df$date <- as.Date(format(date_decimal(a_series_df$date), 
                                   "%d-%m-%Y"), format="%d-%m-%Y")

dat_rect <- data.frame(
                    xmin = as.Date(c("2015-01-01")),
                    xmax = as.Date(c("2015-05-31")),
                    ymin = -Inf,
                    ymax = Inf
)

#Next time, if using functions not in base R, indicate what packages they are from
#decimal_date for example is from lubridate, which in this solution it is not needed.
ggplot() +
        geom_rect(data=dat_rect,
                  aes(xmin=xmin,xmax=xmax,ymin=ymin,ymax=ymax),
                  fill="pink", alpha=0.5) +
        geom_line(data=a_series_df, aes(x=date,y=ret, color='blue')) +

        theme(axis.text.x=element_text(angle=90,hjust=1,vjust=0.5)) +

#Set date
scale_x_date(date_breaks='1 month',
             date_minor_breaks='1 month',
             labels=date_format("%B-%d"),
             expand=c(0,0)) +
scale_y_continuous(labels = scales::percent)
查看更多
登录 后发表回答