15

I am trying to create the plot like following (many times I end up drawing a plot like this by hand, but this time I want to plot it myself).

enter image description here

Here is my data and my trial:

myd <- data.frame (period = c("Triassic", "Jurasic", 
 "Cretaceous", "Cenzoic"), myears = c(245, 208, 145, 65), 
 label = c(226, 176,105, 32 ))
myd2 <- data.frame (event = c("Diansaurs_strt", "Birds", 
  "Diansaurs_ext", "Human"), myears = c(235, 200, 60, 0.5))
myd2$x <- -0.25
with (myd2, plot(x,myears,ylim=c(0,250),
xlim = c(0, 10), axes=F,xlab="",ylab="",type="p",pch=17))
with (myd2,text(x,myears,event,pos=4,xpd=T))
axis(side=2,at = myd$label, labels = myd$period)

enter image description here

I have issues particularly matching of axis with plot and orientation of text and points. Any other idea or improvement help appreciated.

Kent Pawar
  • 2,378
  • 2
  • 29
  • 42
shNIL
  • 963
  • 10
  • 24

4 Answers4

16

For constructing novel plots "from the ground up", and for maximal control over individual graphical elements, the grid graphical system is hard to beat:

library(grid)

## Set up plotting area with reasonable x-y limits
## and a "native" scale related to the scale of the data.
x <- -1:1
y <-  extendrange(c(myd$myears, myd2$myears))
dvp <- dataViewport(x, y, name = "figure")

grid.newpage()
pushViewport(dvp)

## Plot the central timeline
grid.lines(unit(0, "native"), unit(c(0,245), "native"),
           gp = gpar(col="dodgerblue"))

## Annotate LHS
grid.segments(x0=0.5, x1=0.47,
              y0=unit(c(0, myd$myears), "native"),
              y1=unit(c(0, myd$myears), "native"),
              gp=gpar(col="dodgerblue"))
grid.text(label=c(0, myd$myears), x=0.44, y=unit(c(0, myd$myears), "native"))
grid.text(label=myd$period, x=0.3, y=unit(myd$label, "native"),
          just=0, gp=gpar(col="dodgerblue", fontface="italic"))

## Annotate RHS
## Create a function that plots a pointer to the specified coordinate
pointer <- function(x, y, width=1) {
    grid.polygon(x = x + unit(width*(c(0, .1, .1)), "npc"),
                 y = y + unit(width*(c(0, .03, -.03)), "npc"), 
                 gp = gpar(fill="dodgerblue", col="blue", lwd=2))
}
## Call it once for each milestone
for(y in myd2$myears) {
    pointer(unit(.5, "npc"), y=unit(y, "native"), width=0.3)
}
## Or, if you just want blue line segments instead of those gaudy pointers:
## grid.segments(x0=0.5, x1=0.53,
##           y0=unit(c(myd2$myears), "native"),
##           y1=unit(c(myd2$myears), "native"), gp=gpar(col="dodgerblue"))

grid.text(label=myd2$event, x=0.55, y=unit(myd2$myears, "native"),
          just=0)

enter image description here

Josh O'Brien
  • 159,210
  • 26
  • 366
  • 455
  • @Josh O'Brien, thank you, can also present alternate version with just lines you listed in previous revisions – shNIL Dec 19 '12 at 18:42
  • @sharnil -- Sure enough. I just put them back in in commented form. (I actually prefer their cleaner look, but tackled the 'pointer' version as an interesting challenge.) Thanks for your question! – Josh O'Brien Dec 19 '12 at 18:57
  • @JoshO'Brien thanks, I have small question - how can we change the thickness of line in grid. lwd doesnot work. – shNIL Dec 19 '12 at 19:06
  • @sharnil -- You need to pass in the `lwd` argument through the `gp=` ("graphical parameters") argument. (There's actually an example there in my `pointer` function definition.) See `?gpar` for the full list of parameters that may be passed this way. This style takes a little bit of getting used to, but you'll soon appreciate the fact that all of the graphical primitives accept the same set of parameters in the same format! – Josh O'Brien Dec 19 '12 at 19:14
  • 1
    @sharnil -- If you are interested in learning a bit about grid, you should be aware of the useful vignettes that ship with it. Try `browseVignettes(package="grid")` to see them all. The vignette called "Introduction to grid" is a good one to start with. – Josh O'Brien Dec 19 '12 at 19:19
  • @JoshO'Brien Excellent answer that has opened my eyes to the potential in `grid`. Favourited. – SlowLearner Dec 20 '12 at 02:48
  • @SlowLearner -- Glad to hear that. I think **grid** is a really impressive piece of software. [Here](http://stackoverflow.com/questions/11489447/combining-two-plots-in-r/11533995#11533995) are [a few](http://stackoverflow.com/questions/13015110/color-a-designated-area/13015631#13015631) other [examples](http://stackoverflow.com/questions/9981929/how-to-display-all-x-labels-in-r-barplot/9982513#9982513) using [grid](http://stackoverflow.com/questions/9985013/how-do-you-draw-a-line-across-a-multiple-figure-environment-in-r/9985936#9985936) that may be helpful when you start to delve into it. – Josh O'Brien Dec 20 '12 at 14:13
5

You can try something like this to get you started:

myd <- data.frame(period = c("", "Triassic", "Jurasic", 
                             "Cretaceous", "Cenzoic", ""), 
                  myears = c(260, 245, 208, 145, 65, -5), 
                  label = c(260, 226, 176,105, 32, -5))
myd2 <- data.frame(event = c("Dinosaurs_strt", "Birds", 
                             "Dinosaurs_ext", "Human"), 
                   myears = c(235, 200, 60, 0.5))
myd2$x <- 1
with(myd2, plot(x, myears, ylim = c(-5, 250), xlim = c(0, 10), 
                axes = FALSE, xlab = "", ylab = "", type = "n"))
with(myd2, text(x, myears, event, pos = 4, xpd = TRUE))
axis(side = 2, at = myd$label, labels = myd$period, las = 2)
X0 <- rep(myd2$x, 4)
Y0 <- myd2$myears
X1 <- rep(-.25, 4)
Y1 <- Y0
arrows(X0, Y0, X1, Y1)

enter image description here

I've added an extra empty element at the start and end of your data in "myd" to help with the axis. Then, instead of using pch, I've used arrows to match the right hand labels with the axis.

Some tweaking could probably make it look a lot nicer.

A5C1D2H2I1M1N2O1R2T1
  • 190,393
  • 28
  • 405
  • 485
3

Here are some enhancements ( I suggest to add 0 for now just to make scale well):

myd <- data.frame (period = c("Triassic", "Jurasic", 


 "Cretaceous", "Cenzoic", "now"), myears = c(245, 208, 145, 65, 0), 
    label = c(226, 176,105, 32, NA ))
    myd2 <- data.frame (event = c("Diansaurs_strt", "Birds", "Diansaurs_ext", "Human"),
    myears = c(235, 200, 60, 0.5))
    myd2$x <- -0.25
    with (myd2, plot(x,myears,ylim=c(0,250), xlim = c(0, 10), 
    axes=F,xlab="",ylab="",type="p",pch=17, col = "green"))
    with (myd2, plot(x,myears,ylim=c(0,250), 
    xlim = c(0, 10), axes=F,xlab="",ylab="",type="p",pch="-", col = "green"))
    with (myd2,text(x,myears,event,pos=4,xpd=T), col = "green")
    axis(side=2,at = myd$label, labels = myd$period, tick = FALSE, 
    las = 2, col = "green", )
    axis(side=2,at = myd$myears, labels = myd$myears,  las = 2, col = "green")

enter image description here

There are few issues remaining you might want to change oriantation of the arrow (I belief that you can someway find <- symbol, but I do not know how to).

jon
  • 11,186
  • 19
  • 80
  • 132
0

For drawing the triangles look at the my.symbols and ms.polygon functions in the TeachingDemos package.

In your right graph above the Dinosaurs are moved up, if you want this in general (moving labels that would otherwise be too close or overlap) then look at the spread.labs function in the TeachingDemos package.

Some other possible functions that could help with the plot are text, mtext, grconvertX, grconvertY, segments, and axis.

Greg Snow
  • 48,497
  • 6
  • 83
  • 110