Menu Home

How to remember point shape codes in R

I suspect I am not unique in not being able to remember how to control the point shapes in R. Part of this is a documentation problem: no package ever seems to write the shapes down. All packages just use the “usual set” that derives from S-Plus and was carried through base-graphics, to grid, lattice and ggplot2. The quickest way out of this is to know how to generate an example plot of the shapes quickly. We show how to do this in ggplot2. This is trivial- but you get tired of not having it immediately available.

ggplot(data=data.frame(x=c(1:16))) + geom_point(aes(x=x,y=x,shape=x))

Or if you are feeling more daring:

ggplot(data=data.frame(x=c(1:16))) + geom_point(aes(x=x,y=x,shape=x)) + 


Update 4-25-2012

As Idris kindly pointed out in the comments the above no longer works. I suspect what changed is something in the transition to R2.15.0. The following hack does work:

sum <- ggplot()
for(i in 1:16) { 
   sum <- sum + 

The trick is outside the aes() it looks like an integer works and at least 16 values work (instead of only around 6 inside the aes()). Frankly we are doing a lot of work to dance around R’s delayed evaluation and variable binding rules in this case. The ggplot2 examples have always plugged factors into the shapes- but I had always assumed that was to densify the indexing (get all the shape numbers into an interval) and not a requirement for using the shape parameter. The online documentation of shape has always been a line of text of the form:

Aesthetic Default Related scales
shape 16 identity, manual, shape

Which to my mind doesn’t specify if you have to supply a factor or not. But you really should buy Professor Hadley Wickham’s excellent book: ggplot2: Elegant Graphics for Data Analysis (not an affiliate link). My copy (like almost everything else I own) just happens to be packed in a box right now, so I can not easily consult it.

And it looks like we do get some nice bug fixes with this update. The missing scales on some of the facet_wrap panes appear to be fixed (yey!):

ggplot(data=data.frame(x=c(1:16))) + geom_point(aes(x=x,y=x)) +


Categories: Tutorials

Tagged as:


Data Scientist and trainer at Win Vector LLC. One of the authors of Practical Data Science with R.

13 replies

  1. I get this error:

    > ggplot(data = data.frame(x=c(1:16))) + geom_point(aes(x=x, y=x, shape=x))
    Error: A continuous variable can not be mapped to shape

  2. @Idris
    I now get the same error (just upgraded to R2.15.0 after writing this). I can’t even work around it, for example:

    ggplot(data=data.frame(x=c(1:16))) + geom_point(aes(x=x,y=x,shape=factor(x)))

    only displays 6 shapes. Not sure what is going on, but thanks for the feedback.

  3. I had a similar idea a while back and use the following code in base:

    symbols <- function(){
    plot(x=rep(5:1, 5), y=rep(1:5, each=5), pch=25:1,
    ylim=c(1, 5.25), xlab="", ylab="",
    main="Symbols (pch)", axes=FALSE)
    text(25:1, x=rep(5:1, 5), y=rep(1:5, each=5)+.2, cex=.8)

  4. This is soooo much easier in base R

    plot(1:16, pch=1:16)

    Not to mention easier on the eyes without the gray background and grid lines.

  5. To make this work in ggplot2 0.9.0, use scale_shape_identity. For example,
    ggplot(data=data.frame(x=1:25)) + geom_point(aes(x=x,y=x,shape=x)) + scale_shape_identity()

    or Kevin would appreciate the short version using qplot:
    qplot(x=1:25,y=1:25,shape=1:25) + scale_shape_identity()

    I put things from 1:25 as that’s how many shapes there are. Hadley in the book probably use code like this to make the figure:
    ggplot(data=data.frame(x=rep(1:5,5),y=rep(1:5,each=5),shape=1:25)) +
    geom_point(aes(x=x,y=y,shape=shape)) + scale_shape_identity() +

    Different shapes that look the same when not filled have different fill properties revealed thus:
    ggplot(data=data.frame(x=rep(1:5,5),y=rep(1:5,each=5),shape=1:25)) +
    geom_point(aes(x=x,y=y,shape=shape),fill=’blue’,size=5) + scale_shape_identity() +

    Thanks for prompting me to figure this out.

  6. Well this totally did not go the way I expected. I thought I would just post how I plot the shapes (since they are not well documented). Now I feel made to look doubly foolish. First for not knowing about scale_shape_identity() (On scale_shape() is used in examples but not documented and scale_shape_identity() is nowhere to be seen). Second this shouldn’t be a hard task with hidden complexity (and it is not a hard task in base graphics). ggplot2 is my favorite plotting package, but man that did not go well.

  7. @Jason Bryer
    I was about to say that that was just cruel in the number of modifiers. But that is really beautiful plot, good enough to print and tape up as a reminder.

    Edit April 2, 2016:

    Here is your code updated for the current ggplot2:

    ggplot(data=data.frame(x=c(0:25))) + geom_point(size=8, aes(x=x,y=x,shape=x)) +
      facet_wrap(~ x, scales='free') + xlab('') + ylab('') +
      scale_shape_identity() +
      theme(axis.text.x=element_blank(), axis.text.y=element_blank(),
            axis.ticks=element_blank(), legend.position='none',

    And the image:

  8. Here is my attempt:

    ggplot(data=data.frame(x=c(0:25))) + geom_point(size=10, aes(x=x,y=x,shape=x)) +
    facet_wrap(~ x, scales=’free’) + xlab(”) + ylab(”) +
    scale_shape_identity() +
    opts(axis.text.x=theme_blank(), axis.text.y=theme_blank(),
    axis.ticks=theme_blank(), legend.position=’none’,

  9. Further thought reveals that Hadley has code to do all this in the ggplot2 documentation. Type ?shape and look at the bottom examples.

%d bloggers like this: