Chandra
Chandra

Reputation: 524

R ggplot2 geom_rect stacked up

I want to stack up a plot on time scale. Below is my data:

 Flight_No Dest      Date  Time STD.60 STD.45      Date2          start       end
1    ab0729  KP 14-Oct-13 00:05      1      0 2013-10-14 2013-10-14 00:05:00 2013-10-14 00:20:00
2    ab8063  KI 14-Oct-13 00:20      0      3 2013-10-14 2013-10-14 00:20:00 2013-10-14 00:35:00
3    ab0337  ST 14-Oct-13 00:30      1      0 2013-10-14 2013-10-14 00:30:00 2013-10-14 00:45:00

Below is my code to plot graph:

data$Total<-data$STD.60+data$STD.45    
ggplot(data,aes(x=start,y=Total,xmin=start,xmax=end,ymin=0,ymax=Total,alpha=0,fill=factor(Dest)))+geom_rect()

Above produces this graph:

enter image description here

However, I want to stack up these rectanlges whenever there is an overlap. i.e. between 00:30 to 00:35, y-axis value should be shown as 4 instead of 3.

Please help.

Upvotes: 3

Views: 1565

Answers (2)

agstudy
agstudy

Reputation: 121608

Once dates ares sorted, it is easy to compute overlapped ranges. For each interval , I check if there is any overlapping( start > end) , and if so and I add the next total with the next total to the current one.

## choose just relevant columns
d <- dat[,c('start','end','Dest','Total')]
# Make sure the data is sorted
d <- d[ order(d$start), ]
h <- d
## here all the main stuff
for (i in head(seq_len(nrow(d)),-1)){
  if(d[i+1,'start'] < d[i,'end']){
    xx <- d[i,]
    xx$start <- d[i+1,'start'] 
    xx$Total <- d[i,'Total'] +d[i+1,'Total']
    h <- rbind(h,xx)   
  }
}

library(ggplot2)
ggplot(h,aes(x=start,y=Total,xmin=start,xmax=end,ymin=0,ymax=Total,
             ,fill=factor(Dest),alpha=0))+
  geom_rect()

EDIT

I add a manual x axis labels using scale_x_datetime. I am using also scales package to format dates.

library(scales)
last_plot()
scale_x_datetime(breaks=unique(c(h$start,h$end)),
               labels = date_format("%H:%M"))

enter image description here

Upvotes: 3

bdemarest
bdemarest

Reputation: 14665

Here is a solution that relies on: (1) Splitting the time axis into 5 minute wide bins, (2) Rebuilding the data in long-form, and (3) Taking advantage of the stacking capabilities of geom_bar(position="stack").

enter image description here

dat = structure(list(Dest = c("KP", "KI", "ST"), Total = c(1L, 3L, 1L), 
    start = structure(c(1381730700, 1381731600, 1381732200), 
    class = c("POSIXct", "POSIXt"), tzone = ""), 
    end = structure(c(1381731600, 1381732500, 1381733100), 
    class = c("POSIXct", "POSIXt"), tzone = "")), 
    .Names = c("Dest", "Total", "start", "end"), 
    class = "data.frame", row.names = c(NA, -3L))

# Use loop to split each row of data into bins.
Time = as.POSIXct(vector())
Dest = vector("character", length=0)
Total = vector("integer", length=0)

for (i in seq(nrow(dat))) {
    times = seq(from=dat[i, "start"], to=dat[i, "end"], by="5 min")
    times = head(times, -1) # Remove last element.
    Time = c(Time, times)
    Dest = c(Dest,  rep(dat[i, "Dest"],  length(times)))
    Total= c(Total, rep(dat[i, "Total"], length(times)))
}

dat2 = data.frame(Time, Total, Dest)

library(ggplot2)
p = ggplot(dat2, aes(x=Time, y=Total, fill=Dest)) + 
    geom_bar(stat="identity", position="stack", width=300, color="grey30")

ggsave("plot.png", plot=p, width=10, height=4.5, dpi=120)

Notes:

  1. You can change the bin width by changing the seq(..., by= argument. See ?seq.POSIXt.
  2. You may want to round the start and end times to the nearest x minutes, to ease the binning process.
  3. geom_bar(..., width=300) works because there are 300 seconds in 5 minutes. Adjust as needed.
  4. The tick marks on the x-axis are in the center of the bars, but they really apply the left edge of the bar. Adjust with scale_x_datetime(breaks= as shown by @agstudy.

Upvotes: 2

Related Questions