5

How do i scale an axis with ggplot2 beginning at a certain point. Let's say we have a range from 0 to 100 and most values are within the range 1 to 10 and one value is at 100.

require('data.table')
require('ggplot2')

test <- data.table(x=1:10,y=c(seq(1,9),100))

ggplot(test, aes(x=x,y=y)) + geom_point(size=5)

enter image description here

I would like to create a graph with an y-scale from 1 to 10 by 1 and afterwards by 10 so the space between the value 9 and 100 gets "smaller" in the graph.

Update:

The way of eipi10 works perfect for what i want to achieve. Just one more detail i am struggling with. How do i get rid of the 2nd legend and keep the right ratio in the final plot?

enter image description here

and the code for the plot:

test <- data.table(x=1:10,y=c(seq(1,9),100))

p1 = ggplot(test, aes(x=x,y=y,color=x)) + 
  geom_point(size=5) +
  scale_x_continuous(limits=c(0,10)) +
  coord_cartesian(ylim=c(-0.1,10)) +
  scale_y_continuous(breaks=0:10) +
  theme(plot.margin=unit(c(0,0.5,0,0),"lines"))

p2 = ggplot(test, aes(x=x,y=y,color=x)) + 
  geom_point(size=5) + #geom_point(size=5,show.legend=FALSE) +
  scale_x_continuous(limits=c(0,10)) +
  coord_cartesian(ylim=c(40,110)) +
  scale_y_continuous(breaks=c(50,100)) +
  theme(plot.margin=unit(c(0,0.5,-0.5,0), "lines"),
       axis.title.x=element_blank(),
       axis.ticks.x=element_blank(),
       axis.text.x=element_blank(),
       legend.position="none") +
 labs(y="")

gA <- ggplotGrob(p1)
gB <- ggplotGrob(p2)
maxWidth = grid::unit.pmax(gA$widths[2:5], gB$widths[2:5])
gA$widths[2:5] <- as.list(maxWidth)
gB$widths[2:5] <- as.list(maxWidth)
grid.arrange(gB, gA, ncol=1, heights=c(0.15,0.85))

Update 2:

An example of the final result. Thanks again to eipi10 and his great support! enter image description here

RandomDude
  • 1,101
  • 18
  • 33
  • (1) This is right on the line between an OK minor edit/clarification and a [chameleon question](http://meta.stackexchange.com/questions/43478/exit-strategies-for-chameleon-questions) ... (2) it would be nice if there were a "phantom" characteristic for ggplot elements where you could specify that the space would be allocated but nothing would be drawn. – Ben Bolker Nov 27 '15 at 16:10
  • 1
    I don't think the OP should have been expected to know in advance that including a legend would cause a new issue with lining up the plots, so it seems to me the legend follow-up isn't an unreasonable fit with the original question. Dealing with the legend also requires similar types of "grob gymnastics", making the overall solution relatively coherent and self-contained. – eipi10 Nov 27 '15 at 19:01

1 Answers1

5

A log transformation will do that:

require('data.table')
require('ggplot2')
library(scales)

test <- data.table(x=1:10,y=c(seq(1,9),100))

ggplot(test, aes(x=x,y=y)) + 
  geom_point(size=5) +
  scale_y_log10(breaks=c(1,3,10,30,100))

enter image description here

UPDATE: There's no easy way to do a broken axis with ggplot2 (because ggplot2 doesn't allow you to (easily) do things that are considered bad practice), but here's a way to get what you're looking for. (Just don't tell Hadley I told you.)

library(data.table)
library(ggplot2)
library(scales)
library(grid)
library(gridExtra)

test <- data.table(x=1:10,y=c(seq(1,9),100))

The overall strategy is to make two separate plots, one for y>=10 and one for y<10 and then put them together. We'll change the plot margins in order to control the amount of space between the top of the bottom plot and the bottom of the top plot. We'll also get rid of the x-axis ticks and labels on the top plot.

Bottom plot (y < 10):

p1 = ggplot(test[test$y<10,], aes(x=x,y=y)) + 
  geom_point(size=5) +
  scale_x_continuous(limits=c(0,10)) +
  coord_cartesian(ylim=c(-0.1,10)) +
  scale_y_continuous(breaks=0:10) +
  theme(plot.margin=unit(c(0,0.5,0,0),"lines"))

Top plot (y >= 10). For this one, we get rid of the x axis labels and tick marks:

p2 = ggplot(test[test$y>=10,], aes(x=x,y=y)) + 
  geom_point(size=5) +
  scale_x_continuous(limits=c(0,10)) +
  coord_cartesian(ylim=c(10.0,110)) +
  scale_y_continuous(breaks=c(50,100)) +
  theme(plot.margin=unit(c(0,0.5,-0.5,0), "lines"),
        axis.title.x=element_blank(),
        axis.ticks.x=element_blank(),
        axis.text.x=element_blank()) +
  labs(y="")

Left align the two plots (based on this SO answer):

gA <- ggplotGrob(p1)
gB <- ggplotGrob(p2)
maxWidth = grid::unit.pmax(gA$widths[2:5], gB$widths[2:5])
gA$widths[2:5] <- as.list(maxWidth)
gB$widths[2:5] <- as.list(maxWidth)

Arrange both plots together. The heights argument determines the proportion of vertical space allotted to each plot:

grid.arrange(gB, gA, ncol=1, heights=c(0.15,0.85))

enter image description here

UPDATE 2: To include a legend, but also ensure that the plots are properly right justified, do the following:

1) Run the code in your updated question to create plots p1 and p2, where only p1 has a legend.

2) Extract legend as a separate grob using the function below (from this SO answer).

3) Remove the legend from p1.

4) Lay out the plots and the legend using grid.arrange and arrangeGrob.

# Function to extract the legend as a stand-alone grob
g_legend<-function(a.gplot){
  tmp <- ggplot_gtable(ggplot_build(a.gplot))
  leg <- which(sapply(tmp$grobs, function(x) x$name) == "guide-box")
  legend <- tmp$grobs[[leg]]
  legend
}

# Extract the legend from p1
leg = g_legend(p1)

# Remove the legend from p1
p1 = p1 + theme(legend.position="none")

# Left justify the two plots
gA <- ggplotGrob(p1)
gB <- ggplotGrob(p2)
maxWidth = grid::unit.pmax(gA$widths[2:5], gB$widths[2:5])
gA$widths[2:5] <- as.list(maxWidth)
gB$widths[2:5] <- as.list(maxWidth)

# Lay out the plots and the legend
grid.arrange(arrangeGrob(gB, gA, ncol=1, heights=c(0.15,0.85)),
             leg, ncol=2, widths=c(0.9,0.1))

enter image description here

Community
  • 1
  • 1
eipi10
  • 91,525
  • 24
  • 209
  • 285
  • Is there a way to cut out some part of the graph in between? I would prefer to keep the linear relation of the points x=1:9 easy to see... Let's say cut out from y=15 to y= 95. – RandomDude Nov 26 '15 at 18:25
  • Exactly what i was looking for! Thanks for your perfect support! – RandomDude Nov 26 '15 at 19:39
  • eipi10, any idea how i can solve the issue with two legends? `theme(legend.position='none')` has a bad side effect for the plot – RandomDude Nov 27 '15 at 15:53
  • add `show.legend=FALSE` to the geom for which you want to exclude the legend. For example, `geom_point(show.legend=FALSE)`. – eipi10 Nov 27 '15 at 17:45
  • seems like `show.legend` doesn't work. Legends is still visible – RandomDude Nov 27 '15 at 18:07
  • Check my updated post, i added the line where i include `show.legend=FALSE` as a comment. and i deleted `legend.position="none"` – RandomDude Nov 27 '15 at 18:19
  • So you want to have one (and only one) legend, but you also want the plots have the same alignment at the right edge? – eipi10 Nov 27 '15 at 18:44
  • yes exactly. I tried all options to disable the legend i could find and i also tried playing around with the margins of the plot without legend - no success so far – RandomDude Nov 27 '15 at 18:53
  • Just a note - your spacebar is broken. – MS Berends May 13 '22 at 12:51