4

I need a flexible way to make radar / spider charts in ggplot2. From solutions I've found on github and the ggplot2 group, I've come this far:

library(ggplot2) 

# Define a new coordinate system 
coord_radar <- function(...) { 
  structure(coord_polar(...), class = c("radar", "polar", "coord")) 
} 
is.linear.radar <- function(coord) TRUE 

# rescale all variables to lie between 0 and 1 
scaled <- as.data.frame(lapply(mtcars, ggplot2:::rescale01))

scaled$model <- rownames(mtcars)    # add model names as a variable 

as.data.frame(melt(scaled,id.vars="model")) -> mtcarsm

ggplot(mtcarsm, aes(x = variable, y = value)) + 
    geom_path(aes(group = model)) +
    coord_radar() + facet_wrap(~ model,ncol=4) + 
    theme(strip.text.x = element_text(size = rel(0.8)), 
          axis.text.x = element_text(size = rel(0.8)))

which works, except for the fact that lines are not closed. I thougth that I would be able to do this:

mtcarsm <- rbind(mtcarsm,subset(mtcarsm,variable == names(scaled)[1]))
ggplot(mtcarsm, aes(x = variable, y = value)) + 
    geom_path(aes(group = model)) +
    coord_radar() + facet_wrap(~ model,ncol=4) + 
    theme(strip.text.x = element_text(size = rel(0.8)), 
          axis.text.x = element_text(size = rel(0.8)))

in order to join the lines, but this does not work. Neither does this:

closes <- subset(mtcarsm,variable == names(scaled)[c(1,11)])
ggplot(mtcarsm, aes(x = variable, y = value)) + 
    geom_path(aes(group = model)) +
    coord_radar() + facet_wrap(~ model,ncol=4) + 
    theme(strip.text.x = element_text(size = rel(0.8)), 
          axis.text.x = element_text(size = rel(0.8))) + geom_path(data=closes)

which does not solve the problem, and also produces lots of

"geom_path: Each group consist of only one observation. Do you need to adjust the group aesthetic?"

messages. Som, how do I go about closing the lines?

/Fredrik

Fredrik Karlsson
  • 485
  • 8
  • 21
  • 1
    `ggplot(mtcarsm[mtcarsm$model == "Maserati Bora", ], aes(x = variable, y = value)) + geom_path(aes(group = model)) + coord_radar() ` closes the lines here. – lukeA Mar 06 '15 at 12:23
  • Now I'm confused. Running the code you posted clearly shows a gap in the line between `mpg` and `carb` for me. What is going on here? – Fredrik Karlsson Mar 07 '15 at 18:55
  • 1
    Forget to mention: closes the line for the `rbind`ed data frame. – lukeA Mar 07 '15 at 19:37

6 Answers6

4

Using the new ggproto mechanism available in ggplot2 2.0.0, coord_radar can be defined as:

coord_radar <- function (theta = "x", start = 0, direction = 1) 
{
 theta <- match.arg(theta, c("x", "y"))
 r <- if (theta == "x") 
        "y"
      else "x"
 ggproto("CoordRadar", CoordPolar, theta = theta, r = r, start = start, 
      direction = sign(direction),
      is_linear = function(coord) TRUE)
}

Not sure if the syntax is perfect but it is working...

Erwan LE PENNEC
  • 516
  • 3
  • 10
3

The codes here seem outdated for ggplot2: 2.0.0

Try my package zmisc: devtools:install_github("jerryzhujian9/ezmisc")

After you install it, you will be able to run:

df = mtcars
df$model = rownames(mtcars)

ez.radarmap(df, "model", stats="mean", lwd=1, angle=0, fontsize=0.6, facet=T, facetfontsize=1, color=id, linetype=NULL)
ez.radarmap(df, "model", stats="none", lwd=1, angle=0, fontsize=1.5, facet=F, facetfontsize=1, color=id, linetype=NULL)

if you are curious about what's inside, see my codes at github:

The main codes were adapted from http://www.cmap.polytechnique.fr/~lepennec/R/Radar/RadarAndParallelPlots.html

enter image description here enter image description here

Jerry T
  • 1,541
  • 1
  • 19
  • 17
  • does your package add functionalities over those provided by the code you link to? Or does it provide just a shortcut to the same sort of plots? – PatrickT Apr 19 '16 at 10:04
  • 1
    I have a different rescale algorithm and internally also convert data format to the long format that is required by ggplot2. – Jerry T Apr 19 '16 at 21:43
3
  • solution key factor
    1. add duplicated mpg row after melt by rbind
    2. inherit CoordPolar on ggproto
    3. set is_linear = function() TRUE on ggproto

especially is_linear = function() TRUE is important,
since if not you will get plot like this...

enter image description here

with is_linear = function() TRUE settings you can get,

enter image description here

library(dplyr)
library(data.table)
library(ggplot2)

rm(list=ls())

scale_zero_to_one <- 
  function(x) {
    r <- range(x, na.rm = TRUE)
    min <- r[1]
    max <- r[2]
    (x - min) / (max - min)
  }

scaled.data <-
  mtcars %>%
  lapply(scale_zero_to_one) %>%
  as.data.frame %>%
  mutate(car.name=rownames(mtcars)) 

plot.data <-
  scaled.data %>%
  melt(id.vars='car.name') %>%
  rbind(subset(., variable == names(scaled.data)[1]))

# create new coord : inherit coord_polar
coord_radar <- 
  function(theta='x', start=0, direction=1){
    # input parameter sanity check
    match.arg(theta, c('x','y'))

    ggproto(
      NULL, CoordPolar, 
      theta=theta, r=ifelse(theta=='x','y','x'),
      start=start, direction=sign(direction),
      is_linear=function() TRUE)
  }

plot.data %>%
  ggplot(aes(x=variable, y=value, group=car.name, colour=car.name)) + 
  geom_path() +
  geom_point(size=rel(0.9)) +
  coord_radar() + 
  facet_wrap(~ car.name, nrow=4) + 
  theme_bw() +
  theme(
    axis.title.y = element_blank(),
    axis.text.y = element_blank(),
    axis.ticks.y = element_blank(),
    axis.title.x = element_blank(),
    legend.position = 'none') +
  labs(title = "Cars' Status")
  • final result
    enter image description here
Curycu
  • 1,545
  • 17
  • 21
1

Sorry, I was beeing stupid. This seems to work:

library(ggplot2) 

# Define a new coordinate system 
coord_radar <- function(...) { 
  structure(coord_polar(...), class = c("radar", "polar", "coord")) 
} 
is.linear.radar <- function(coord) TRUE 

# rescale all variables to lie between 0 and 1 
scaled <- as.data.frame(lapply(mtcars, ggplot2:::rescale01))

scaled$model <- rownames(mtcars)    # add model names as a variable 

as.data.frame(melt(scaled,id.vars="model")) -> mtcarsm


mtcarsm <- rbind(mtcarsm,subset(mtcarsm,variable == names(scaled)[1]))
ggplot(mtcarsm, aes(x = variable, y = value)) + 
    geom_path(aes(group = model)) +
    coord_radar() + facet_wrap(~ model,ncol=4) + 
    theme(strip.text.x = element_text(size = rel(0.8)), 
          axis.text.x = element_text(size = rel(0.8))) 
Fredrik Karlsson
  • 485
  • 8
  • 21
0

It turns out than geom_polygom still produces a polygon in the polar coordinates so that

# rescale all variables to lie between 0 and 1
scaled <- as.data.frame(lapply(mtcars, ggplot2:::rescale01))
scaled$model <- rownames(mtcars)    # add model names as a variable
# melt the dataframe
mtcarsm <- reshape2::melt(scaled)
# plot it as using the polygon geometry in the polar coordinates
ggplot(mtcarsm, aes(x = variable, y = value)) +
geom_polygon(aes(group = model), color = "black", fill = NA, size = 1) +
coord_polar() + facet_wrap( ~ model) +
theme(strip.text.x = element_text(size = rel(0.8)),
    axis.text.x = element_text(size = rel(0.8)),
    axis.ticks.y = element_blank(),
    axis.text.y = element_blank()) +
xlab("") + ylab("")

works perfectly...

Erwan LE PENNEC
  • 516
  • 3
  • 10
  • Yes, but I think most would like to see straight lines inbetween points in a plot (not bubbly). I like the output of the coord_radar solution below better. – Fredrik Karlsson Jun 05 '15 at 06:03
0

Thank you guys for the help but it did not cover all of my needs. I used two series of data to be compared so I took the subset of mtcars for Mazda:

  1. nobody mentioned about order of the x variable and ggplot2 sorts this variable for the plot but does not sort the data and it made my chart wrong at the first attempt. Apply sorting function for me it was dplyr::arrange(plot.data, x.variable.name)

  2. I needed to annotate the chart with values and ggplot2::annotate() works fine but it was not included in the recent answers

  3. the above code did not work fine for my data until adding ggplot2::geom_line

Finally this code chunk did my chart:

scaled <- as.data.frame(lapply(mtcars, ggplot2:::rescale01))
scaled$model <- rownames(mtcars)
mtcarsm <- scaled %>%
  filter(grepl('Mazda', model)) %>% 
  gather(variable, value, mpg:carb) %>% 
  arrange(variable)


ggplot(mtcarsm, aes(x = variable, y = value)) + 
  geom_polygon(aes(group = model, color = model), fill = NA, size = 1) +
  geom_line(aes(group = model, color = model), size = 1) + 
  annotate("text", x = mtcarsm$variable, y = (mtcarsm$value + 0.05), label = round(mtcarsm$value, 2), size = 3) +
  theme(strip.text.x = element_text(size = rel(0.8)),
        axis.text.x = element_text(size = rel(1.2)),
        axis.ticks.y = element_blank(),
        axis.text.y = element_blank()) +
  xlab("") + ylab("") +
  guides(color = guide_legend()) +
  coord_radar()

Hopefully usefull for somebody