Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
485 views
in Technique[技术] by (71.8m points)

r - Plotting continuous and discrete series in ggplot with facet

I have data that plots over time with four different variables. I would like to combine them in one plot using facet_grid, where each variable gets its own sub-plot. The following code resembles my data and the way I'm presenting it:

require(ggplot2)
require(reshape2)

subm <- melt(economics, id='date', c('psavert','uempmed','unemploy'))
mcsm <- melt(data.frame(date=economics$date, q=quarters(economics$date)), id='date')
mcsm$value <- factor(mcsm$value)


ggplot(subm, aes(date, value, col=variable, group=1)) + geom_line() + 
       facet_grid(variable~., scale='free_y') + 
       geom_step(data=mcsm, aes(date, value)) + 
       scale_y_discrete(breaks=levels(mcsm$value))

If I leave out scale_y_discrete, R complains that I'm trying to combine discrete value with continuous scale. If I include scale_y_discreate my continuous series miss their scale.

Is there any neat way of solving this issue ie. getting all scales correct ? I also see that the legend is alphabetically sorted, can I change that so the legend is ordered in the same order as the sub-plots ?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Problem with your data is that that for data frame subm value is numeric (continuous) but for the mcsm value is factor (discrete). You can't use the same scale for numeric and continuous values and you get y values only for the last facet (discrete). Also it is not possible to use two scale_y...() functions in one plot.

My approach would be to make mcsm value as numeric (saved as value2) and then use them - it will plot quarters as 1,2,3 and 4. To solve the problem with legend, use scale_color_discrete() and provide breaks= in order you need.

mcsm$value2<-as.numeric(mcsm$value)
ggplot(subm, aes(date, value, col=variable, group=1)) + geom_line()+
 facet_grid(variable~., scale='free_y') + geom_step(data=mcsm, aes(date, value2)) +
  scale_color_discrete(breaks=c('psavert','uempmed','unemploy','q'))

enter image description here

UPDATE - solution using grobs

Another approach is to use grobs and library gridExtra to plot your data as separate plots.

First, save plot with all legends and data (code as above) as object p. Then with functions ggplot_build() and ggplot_gtable() save plot as grob object gp. Extract from gp only part that plots legend (saved as object gp.leg) - in this case is list element number 17.

library(gridExtra)
p<-ggplot(subm, aes(date, value, col=variable, group=1)) + geom_line()+
  facet_grid(variable~., scale='free_y') + geom_step(data=mcsm, aes(date, value2)) +
  scale_color_discrete(breaks=c('psavert','uempmed','unemploy','q'))
gp<-ggplot_gtable(ggplot_build(p))
gp.leg<-gp$grobs[[17]]

Make two new plot p1 and p2 - first plots data of subm and second only data of mcsm. Use scale_color_manual() to set colors the same as used for plot p. For the first plot remove x axis title, texts and ticks and with plot.margin= set lower margin to negative number. For the second plot change upper margin to negative number. faced_grid() should be used for both plots to get faceted look.

p1 <- ggplot(subm, aes(date, value, col=variable, group=1)) + geom_line()+
   facet_grid(variable~., scale='free_y')+
  theme(plot.margin = unit(c(0.5,0.5,-0.25,0.5), "lines"),
        axis.text.x=element_blank(),
        axis.title.x=element_blank(),
        axis.ticks.x=element_blank())+
  scale_color_manual(values=c("#F8766D","#00BFC4","#C77CFF"),guide="none")

p2 <- ggplot(data=mcsm, aes(date, value,group=1,col=variable)) + geom_step() +
  facet_grid(variable~., scale='free_y')+
  theme(plot.margin = unit(c(-0.25,0.5,0.5,0.5), "lines"))+ylab("")+
  scale_color_manual(values="#7CAE00",guide="none")

Save both plots p1 and p2 as grob objects and then set for both plots the same widths.

gp1 <- ggplot_gtable(ggplot_build(p1))
gp2 <- ggplot_gtable(ggplot_build(p2))
maxWidth = grid::unit.pmax(gp1$widths[2:3],gp2$widths[2:3])
gp1$widths[2:3] <- as.list(maxWidth)
gp2$widths[2:3] <- as.list(maxWidth)

With functions grid.arrange() and arrangeGrob() arrange both plots and legend in one plot.

grid.arrange(arrangeGrob(arrangeGrob(gp1,gp2,heights=c(3/4,1/4),ncol=1),
       gp.leg,widths=c(7/8,1/8),ncol=2))

enter image description here


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...