Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stacked bar plot in R with multiple rows per day

I would like to display work done in a day as a stacked bar plot, in order to see, day by day, how much activity I've done in each category, with the Y-axis representing time from 0:00 to 23:59.

#   day             tstart   tend   duration category
1   2012-10-01      13:40    14:16  36       Recreation
2   2012-10-02      10:15    10:57  42       Work
3   2012-10-02      13:23    13:47  24       Chores
4   2012-10-02      13:47    14:48  61       Work
5   2012-10-03      09:09    11:40  151      Work
6   2012-10-03      13:33    14:04  31       Recreation
7   2012-10-03      17:00    19:40  160      Recreation

I know I will have to convert "time start" as a numeric, but I don't know how to "merge" the multiple rows for the same day, so that they're making up only one bar in the plot.

In (very primitive) ASCII art, what I'm expecting is something like:

23:00
22:00
21:00
20:00
19:00                C
18:00                C
17:00                C
16:00
15:00
14:00          W     R
13:00    R     C
12:00
11:00                W
10:00          W     W
 9:00                W
 8:00
 7:00
 6:00
 5:00
 4:00
 3:00
 2:00
 1:00
 0:00
        01    02    03

(where R, W and C would be bars of different colors for the different activites: Recreation, Work and Chores)

In fact, being newbie in R plots, I don't know the plot function (and the plot package) I have to look at, moreover as they're will be holes in the plot -- no activity recorded (for example) between 0:00 and 09:09, then between 11:40 and 13:33, etc. on 2012-10-03...

like image 341
fniessen Avatar asked Feb 07 '13 09:02

fniessen


2 Answers

Here is a quick solution with ggplot2 :

d <- read.table(textConnection("
day             tstart   tend   duration category
2012-10-01      13:40    14:16  36       Recreation
2012-10-02      10:15    10:57  42       Work
2012-10-02      13:23    13:47  24       Chores
2012-10-02      13:47    14:48  61       Work
2012-10-03      09:09    11:40  151      Work
2012-10-03      13:33    14:04  31       Recreation
2012-10-03      17:00    19:40  160      Recreation"), header=TRUE)

d$day <- as.Date(d$day)
d$tstart <- as.POSIXct(d$tstart, format="%H:%M")
d$tend <- as.POSIXct(d$tend, format="%H:%M")

library(ggplot2)
library(scales)
g <- ggplot(data=d, aes()) + geom_segment(aes(x=day,xend=day,y=tstart,yend=tend,color=category),size=20) + scale_x_date(labels = date_format("%d")) 
g + scale_y_datetime(limits=c(as.POSIXct("00:00", format="%H:%M"),as.POSIXct("23:59", format="%H:%M")), labels = date_format("%H:%M"))

Which gives :

enter image description here

EDITED : the y axis in the initial answer was wrong.

like image 105
juba Avatar answered Oct 24 '22 16:10

juba


While I was writing this post, juba posted excellent solution using ggplot2, I will post my solution nonetheless as an alternative.

This is very crude way of doing it, but it accomplishes what you may be looking for.

First a small utility function to convert time of format hh:mm to decimal representation

decTime <- function(x) {
    t <- as.numeric(strsplit(x, ":")[[1]])
    t <- t[1] + t[2]/60
    return(t)
}

str <- 'n   day     tstart   tend   duration category
1   2012-10-01      13:40    14:16  36       Recreation
2   2012-10-02      10:15    10:57  42       Work
3   2012-10-02      13:23    13:47  24       Chores
4   2012-10-02      13:47    14:48  61       Work
5   2012-10-03      09:09    11:40  151      Work
6   2012-10-03      13:33    14:04  31       Recreation
7   2012-10-03      17:00    19:40  160      Recreation'

df <- read.table(textConnection(str), header=T)

Convert day to numeric (for ease of specifying width of rectangles) and times to decimal

df$day  <- gsub('2012-10-', "", df$day)
df$day <- as.numeric(df$day)
df$starttime <- sapply(as.character(df$tstart), decTime, USE.NAMES=F)
df$endtime <- sapply(as.character(df$tend), decTime, USE.NAMES=F)

Get colors for different rectangles

df$color <- ifelse(df$category=='Recreation', 'RED', ifelse(df$category =='Chores', 'BLUE', 'GREEN'))

Plot the graph one step at at time

#Plot empty graph
plot(x=unique(df$day), y=c(0,0,0), axes=F, ylim=c(0,24), xlim=c(0.5,3.5), xlab='date', ylab='time', type='n')
#Label axes properly
axis(side=1, at=c(1,2,3), labels=c('01', '02', '03'))
axis(side=2, at=seq(from=0,to=24,by=1), labels=seq(from=0,to=24,by=1))
#Draw required rectangles
rect(df$day-0.25, df$starttime, df$day+0.25, df$endtime, col=df$color)

Result should be somewhat you may want.

enter image description here

like image 35
CHP Avatar answered Oct 24 '22 18:10

CHP