oepix
oepix

Reputation: 214

R: Changing order of guide/legend from sorting strings to sorting numeric in ggplot2

I am trying to create an isoline plot with ggplot2. I successfully filled the tiles of the geom_tile() function in groups using the cut() function for breaks. Using this approach, the breaks will be interpreted as strings though, not as values, giving me trouble in the order the guide is set up. This way, e.g. a value of 20 will be interpreted as a bigger number than 180, resulting in fatally misleading colors in the plot, such as this purposely ugly one: Isoline plot

To create this ugly image, I used the following code:

plot.isoline <- function(data, dbr=20, n_interp=200){

  library(akima)
  library(ggplot2)
  library(fields)

  data <- read.table(file = "http://s000.tinyupload.com/download.php?file_id=90687695741432763217&t=9068769574143276321730276", header = T)

  dint <- interp(x = data$x, y = data$y, z = data$z,
                 xo = seq(min(data$x), max(data$x), length = n_interp),
                 yo = seq(min(data$y), max(data$y), length = n_interp))

  dat_interp <- data.frame(expand.grid(x=dint$x,y=dint$y), z=c(dint$z))

  br <- seq(floor(min(dat_interp$z)/dbr)*dbr, ceiling(max(dat_interp$z)/dbr)*dbr, dbr)

  breaks <- cut(dat_interp$z, breaks = br)
  breaks <- gsub(","," - ",breaks,fixed=TRUE)

  dat_interp$breaks <- sapply(breaks, function(x){strsplit(x, "[( ]")[[1]][[2]]})

  ret <- ggplot(dat_interp) + 
    aes(x=x, y=y, z=z) +
    geom_tile(aes(fill=breaks)) +
    scale_fill_manual("TEMP", values=tim.colors(length(br)), guide = guide_legend(reverse=TRUE)) +
    stat_contour(breaks=br) +
    scale_x_continuous(expand=c(0,0)) +
    scale_y_continuous(expand=c(0,0))

  return(ret)        
}

I already tried setting the break strings to values the following way:

dat_interp$breaks <- as.numeric(sapply(breaks, function(x){strsplit(x, "[( ]")[[1]][[2]]}))

This approach only leads to an error "Error: Continuous value supplied to discrete scale". I am sure there has to be a way to order the scale in a numeric way, and I'd be really glad if someone could help me out on this one.

Upvotes: 1

Views: 591

Answers (2)

oepix
oepix

Reputation: 214

With the helpful input of @krlmlr I managed to solve the problem. As he said, the crucial part was to just edit the levels(). For anyone interested, this is the important part:

  breaks <- cut(dat_interp$z, breaks = br)
  levels(breaks) <- gsub(","," - ", levels(breaks),fixed=TRUE)
  dat_interp$breaks <- breaks
  levels(dat_interp$breaks) <- as.numeric(sapply(levels(breaks), function(x){strsplit(x, "[( ]")[[1]][[2]]}))

This way I get the following result, properly sorted: Working result

And the full code:

plot.isoline <- function(data, dbr=20, n_interp=200){

  library(akima)
  library(ggplot2)
  library(fields)

  data <- read.table(file = "http://s000.tinyupload.com/download.php?file_id=90687695741432763217&t=9068769574143276321730276", header = T)

  dint <- interp(x = data$x, y = data$y, z = data$z,
                 xo = seq(min(data$x), max(data$x), length = n_interp),
                 yo = seq(min(data$y), max(data$y), length = n_interp))

  dat_interp <- data.frame(expand.grid(x=dint$x,y=dint$y), z=c(dint$z))

  br <- seq(floor(min(dat_interp$z)/dbr)*dbr, ceiling(max(dat_interp$z)/dbr)*dbr, dbr)

  breaks <- cut(dat_interp$z, breaks = br)
  levels(breaks) <- gsub(","," - ", levels(breaks),fixed=TRUE)
  dat_interp$breaks <- breaks
  levels(dat_interp$breaks) <- as.numeric(sapply(levels(breaks), function(x){strsplit(x, "[( ]")[[1]][[2]]}))

  ret <- ggplot(dat_interp) + 
    aes(x=x, y=y, z=z) +
    geom_tile(aes(fill=breaks)) +
    scale_fill_manual("TEMP", values=tim.colors(length(br)), guide = guide_legend(reverse=TRUE)) +
    stat_contour(breaks=br) +
    scale_x_continuous(expand=c(0,0)) +
    scale_y_continuous(expand=c(0,0))

  return(ret)        
}

Upvotes: 1

krlmlr
krlmlr

Reputation: 25484

ggplot2 will sort character values as strings, and factor values by their level. In your code,

breaks <- gsub(","," - ",breaks,fixed=TRUE)

converts the factor returned by cut to a character. Try omitting it.

If you need to change the display, use something like

levels(breaks) <- gsub(",", " - ", levels(breaks), fixed=TRUE)

Upvotes: 1

Related Questions