Keeping the width constant in ggplot in R - r

Have already tried this link but it fails to work, moreover this question was asked 5 years ago so I hope there is a better way around than this lengthy code.
How to make the width of bars and spaces between them fixed for several barplots using ggplot, having different number of bars on each plot?
#with 10 bins
data <- data.frame(x=1:10,y=runif(10))
library(ggplot2)
ggplot(data, aes(x,y)) + geom_bar(stat="identity")
#with 3 bins
ggplot(data[1:3,], aes(x,y)) + geom_bar(stat="identity")
Adding width=1 to geom_bar(...) doesn't help as well. I need the second plot automatically to have less width and the same bar width and spaces as the first one.

One solution would be to adjust the coordinates to match:
ggplot(data[1:3,], aes(x,y)) + geom_bar(stat="identity") +
scale_x_continuous(limits = c(0.5,10)) # This is approximate but pretty close.
#
# For an exact match, you can go into the ggplot_build object and
# extract [["layout"]][["panel_params"]][[1]][["x.range"]]
# You could then use the exact values:
# scale_x_continuous(limits = c(0.055,10.945), expand = c(0,0))
Another would be to combine the bars into one plot, and then use facets to show the data on the same scale.
library(dplyr)
data2 <-
data %>% mutate(plot = "A") %>%
bind_rows(
data[1:3,] %>% mutate(plot = "B")
)
(a <- ggplot(data2, aes(x,y)) + geom_bar(stat="identity") +
facet_grid(~plot)
)
If you want to use this with plotly, you could then use plotly::ggplotly(a).

Related

How to reduce binwidth in geom_bar for one single bar?

I'm trying to get a side-by-side bar plot using ggplot's geom_bar(). Here's some sample data I made up for replication purposes:
dat <- data.frame("x"=c(rep(c(1,2,3,4,5),5)),
"by"=c(NA,0,0,0,0,NA,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1))
I want to plot "x" grouped by "by". Now, because I don't need to plot NA values, I filtered for !is.na(by))
library(dplyr)
dat <- filter(dat, !is.na(by))
Now for the plot:
library(ggplot2)
ggplot(dat, aes(x=x, fill=as.factor(by))) + geom_bar(position="dodge") + theme_tufte()
This returns what I need; almost. Unfortunately, the first bar looks really weird, because it's binwidth is twice as high (due to the fact that there are no zeros in "by" for "x"==1).
Is there a way to reduce the binwidth for the first bar back to "normal"?
You could also do it like this. Precalculate the table and use geom_col.
ggplot(as.data.frame(table(dat)), aes(x = x, y = Freq, fill = by)) +
theme_bw() +
geom_col(position = "dodge")
Never mind, I just figured out that you can manipulate the binwidth argument using an ifelse statement.
...geom_bar(..., binwidth = ifelse("by"==1 & is.na("x"), .5, 1)))
So if you play around with this, it will work. At least it worked for me.

Plotting data with R. How to break coordinates [duplicate]

I want to make a bar plot where one of the values is much bigger than all other values. Is there a way of having a discontinuous y-axis? My data is as follows:
df <- data.frame(a = c(1,2,3,500), b = c('a1', 'a2','a3', 'a4'))
p <- ggplot(data = df, aes(x = b, y = a)) + geom_bar()
p <- p + opts(axis.text.x=theme_text(angle= 90, hjust=1)) + coord_flip()
p
Is there a way that I can make my axis run from 1- 10, then 490 - 500? I can't think of any other way of plotting the data (aside from transforming it, which I don't want to do)
[Edit 2019-05-06]:
8 years later, above code needs to be amended to work with version 3.1.1 of ggplot2 in order to create the same chart:
library(ggplot2)
ggplot(df) +
aes(x = b, y = a) +
geom_col() +
coord_flip()
As noted elsewhere, this isn't something that ggplot2 will handle well, since broken axes are generally considered questionable.
Other strategies are often considered better solutions to this problem. Brian mentioned a few (faceting, two plots focusing on different sets of values). One other option that people too often overlook, particularly for barcharts, is to make a table:
Looking at the actual values, the 500 doesn't obscure the differences in the other values! For some reason tables don't get enough respect as data a visualization technique. You might object that your data has many, many categories which becomes unwieldy in a table. If so, it's likely that your bar chart will have too many bars to be sensible as well.
And I'm not arguing for tables all the time. But they are definitely something to consider if you are making barcharts with relatively few bars. And if you're making barcharts with tons of bars, you might need to rethink that anyway.
Finally, there is also the axis.break function in the plotrix package which implements broken axes. However, from what I gather you'll have to specify the axis labels and positions yourself, by hand.
Eight years later, the ggforce package offers a facet_zoom() extension which is an implementation of Hadley Wickham's suggestion to show two plots (as referenced in Brian Diggs' answer).
Zoom facet
library(ggforce)
ggplot(df) +
aes(x = b, y = a) +
geom_col() +
facet_zoom(ylim = c(0, 10))
Unfortunately, the current version 0.2.2 of ggforce throws an error with coord_flip() so only vertical bars can be shown.
The zoomed facet shows the variations of the small values but still contains the large - now cropped - a4 bar. The zoom.data parameter controls which values appear in the zoomed facet:
library(ggforce)
ggplot(df) +
aes(x = b, y = a) +
geom_col() +
facet_zoom(ylim = c(0, 10), zoom.data = ifelse(a <= 10, NA, FALSE))
Two plots
Hadley Wickham suggested
I think it's much more appropriate to show two plots - one of all the
data, and one of just the small values.
This code creates two plots
library(ggplot2)
g1 <- ggplot(df) +
aes(x = b, y = a) +
geom_col() +
coord_flip()
g2 <- ggplot(df) +
aes(x = b, y = a) +
geom_col() +
coord_flip() +
ylim(NA, 10)
which can be combined into one plot by
cowplot::plot_grid(g1, g2) # or ggpubr::ggarrange(g1, g2)
or
gridExtra::grid.arrange(g1, g2) # or egg::ggarrange(g1, g2)
Two facets
This was suggested in a comment by Chase and also by Brian Diggs in his answer who interpreted Hadley's suggestion to use
faceted plots, one with all the data, one zoomed in a particular region
but no code was supplied for this approach, so far.
As there is no simple way to scale facets separately (see related question, e.g.) the data needs to be manipulated:
library(dplyr)
library(ggplot2)
ggplot() +
aes(x = b, y = a) +
geom_col(data = df %>% mutate(subset = "all")) +
geom_col(data = df %>% filter(a <= 10) %>% mutate(subset = "small")) +
coord_flip() +
facet_wrap(~ subset, scales = "free_x")
No, not using ggplot. See the discussion in the thread at http://groups.google.com/group/ggplot2/browse_thread/thread/8d2acbfc59d2f247 where Hadley explains why it is not possible but gives a suggested alternative (faceted plots, one with all the data, one zoomed in a particular region).
Not with ggplot, but with plotrix you can easily do that:
library(plotrix)
gap.barplot(df$a, gap=c(5,495),horiz=T)
No, unfortunately not
The fear is that allowing discontinuous axes will lead to deceit of the audience. However, there are cases where not having a discontinuous axis leads to distortion.
For example, if the axis is truncated, but usually lies within some interval (say [0,1]), the audience may not notice the truncation and make distorted conclusions about the data. In this case, an explicit discontinuous axis would be more appropriate and transparent.
Compare:
An option could be using the ggbreak package using the scale_y_cut() or scale_x_cut() function. This function makes it possible to cut the ggplot object into parts with the possibility to specify which part is zoom in or zoom out. Here is a reproducible example with left plot normal and right plot with the function used:
df <- data.frame(a = c(1,2,3,500), b = c('a1', 'a2','a3', 'a4'))
library(ggplot2)
library(ggbreak)
library(patchwork)
p1 <- ggplot(df) +
aes(x = b, y = a) +
geom_col()
p2 <- ggplot(df) +
aes(x = b, y = a) +
geom_col() +
scale_y_cut(breaks=c(4, 30), which=c(1, 3), scales=c(0.5, 3))
p1 + p2
Created on 2022-08-22 with reprex v2.0.2
As you can see from the example, some parts are zoomed in and zoomed out. This can be changed by using different arguments.
Arguments used:
breaks:
a numeric or numeric vector, the points to be divided
which:
integer, the position of subplots to scales, started from left to
right or top to bottom.
scales:
numeric, relative width or height of subplots.
To change the space between the subplots, you can use the argument space.
For some extra information and examples check this tutorial.
A clever ggplot solution is provided by Jörg Steinkamp, using facet_grid. Simplified, it is something like this:
library("tidyverse")
df <- data.frame(myLetter=LETTERS[1:4], myValue=runif(12) + rep(c(4,0,0),2)) # cluster a few values well above 1
df$myFacet <- df$myValue > 3
(ggplot(df, aes(y=myLetter, x=myValue))
+ geom_point()
+ facet_grid(. ~ myFacet, scales="free", space="free")
+ scale_x_continuous(breaks = seq(0, 5, .25)) # this gives both facets equal interval spacing.
+ theme(strip.text.x = element_blank()) # get rid of the facet labels
)
As of 2022-06-01, we have the elegant-looking ggbreak package, which appears to answer the OP's question. Although I haven't tried it on my own data, it looks to be compatible with many or all other ggplot2 functionality. Offers differential scaling too, perhaps useful to OP's and similar uses.
library(ggplot2)
library(ggbreak)
set.seed(2019-01-19)
d <- data.frame(x = 1:20,
y = c(rnorm(5) + 4, rnorm(5) + 20, rnorm(5) + 5, rnorm(5) + 22))
p1 <- ggplot(d, aes(y, x)) + geom_col(orientation="y") +
theme_minimal()
p1 + scale_x_break(c(7, 17), scales = 1.5) + scale_x_break(c(18, 21), scales=2)
I doubt there's anything off the shelf in R, but you could show the data as a series of 3D partial cubes. 500 is only 5*10*10, so it would scale well. The exact value could be a label.
This probably should only be used if you must have a graphic representation for some reason.
One strategy is to change the axis to plot Log Scale. This way you get to reduce exponentially higher value by a factor of 10

ggplot2: visualize broken y axis [duplicate]

I want to make a bar plot where one of the values is much bigger than all other values. Is there a way of having a discontinuous y-axis? My data is as follows:
df <- data.frame(a = c(1,2,3,500), b = c('a1', 'a2','a3', 'a4'))
p <- ggplot(data = df, aes(x = b, y = a)) + geom_bar()
p <- p + opts(axis.text.x=theme_text(angle= 90, hjust=1)) + coord_flip()
p
Is there a way that I can make my axis run from 1- 10, then 490 - 500? I can't think of any other way of plotting the data (aside from transforming it, which I don't want to do)
[Edit 2019-05-06]:
8 years later, above code needs to be amended to work with version 3.1.1 of ggplot2 in order to create the same chart:
library(ggplot2)
ggplot(df) +
aes(x = b, y = a) +
geom_col() +
coord_flip()
As noted elsewhere, this isn't something that ggplot2 will handle well, since broken axes are generally considered questionable.
Other strategies are often considered better solutions to this problem. Brian mentioned a few (faceting, two plots focusing on different sets of values). One other option that people too often overlook, particularly for barcharts, is to make a table:
Looking at the actual values, the 500 doesn't obscure the differences in the other values! For some reason tables don't get enough respect as data a visualization technique. You might object that your data has many, many categories which becomes unwieldy in a table. If so, it's likely that your bar chart will have too many bars to be sensible as well.
And I'm not arguing for tables all the time. But they are definitely something to consider if you are making barcharts with relatively few bars. And if you're making barcharts with tons of bars, you might need to rethink that anyway.
Finally, there is also the axis.break function in the plotrix package which implements broken axes. However, from what I gather you'll have to specify the axis labels and positions yourself, by hand.
Eight years later, the ggforce package offers a facet_zoom() extension which is an implementation of Hadley Wickham's suggestion to show two plots (as referenced in Brian Diggs' answer).
Zoom facet
library(ggforce)
ggplot(df) +
aes(x = b, y = a) +
geom_col() +
facet_zoom(ylim = c(0, 10))
Unfortunately, the current version 0.2.2 of ggforce throws an error with coord_flip() so only vertical bars can be shown.
The zoomed facet shows the variations of the small values but still contains the large - now cropped - a4 bar. The zoom.data parameter controls which values appear in the zoomed facet:
library(ggforce)
ggplot(df) +
aes(x = b, y = a) +
geom_col() +
facet_zoom(ylim = c(0, 10), zoom.data = ifelse(a <= 10, NA, FALSE))
Two plots
Hadley Wickham suggested
I think it's much more appropriate to show two plots - one of all the
data, and one of just the small values.
This code creates two plots
library(ggplot2)
g1 <- ggplot(df) +
aes(x = b, y = a) +
geom_col() +
coord_flip()
g2 <- ggplot(df) +
aes(x = b, y = a) +
geom_col() +
coord_flip() +
ylim(NA, 10)
which can be combined into one plot by
cowplot::plot_grid(g1, g2) # or ggpubr::ggarrange(g1, g2)
or
gridExtra::grid.arrange(g1, g2) # or egg::ggarrange(g1, g2)
Two facets
This was suggested in a comment by Chase and also by Brian Diggs in his answer who interpreted Hadley's suggestion to use
faceted plots, one with all the data, one zoomed in a particular region
but no code was supplied for this approach, so far.
As there is no simple way to scale facets separately (see related question, e.g.) the data needs to be manipulated:
library(dplyr)
library(ggplot2)
ggplot() +
aes(x = b, y = a) +
geom_col(data = df %>% mutate(subset = "all")) +
geom_col(data = df %>% filter(a <= 10) %>% mutate(subset = "small")) +
coord_flip() +
facet_wrap(~ subset, scales = "free_x")
No, not using ggplot. See the discussion in the thread at http://groups.google.com/group/ggplot2/browse_thread/thread/8d2acbfc59d2f247 where Hadley explains why it is not possible but gives a suggested alternative (faceted plots, one with all the data, one zoomed in a particular region).
Not with ggplot, but with plotrix you can easily do that:
library(plotrix)
gap.barplot(df$a, gap=c(5,495),horiz=T)
No, unfortunately not
The fear is that allowing discontinuous axes will lead to deceit of the audience. However, there are cases where not having a discontinuous axis leads to distortion.
For example, if the axis is truncated, but usually lies within some interval (say [0,1]), the audience may not notice the truncation and make distorted conclusions about the data. In this case, an explicit discontinuous axis would be more appropriate and transparent.
Compare:
An option could be using the ggbreak package using the scale_y_cut() or scale_x_cut() function. This function makes it possible to cut the ggplot object into parts with the possibility to specify which part is zoom in or zoom out. Here is a reproducible example with left plot normal and right plot with the function used:
df <- data.frame(a = c(1,2,3,500), b = c('a1', 'a2','a3', 'a4'))
library(ggplot2)
library(ggbreak)
library(patchwork)
p1 <- ggplot(df) +
aes(x = b, y = a) +
geom_col()
p2 <- ggplot(df) +
aes(x = b, y = a) +
geom_col() +
scale_y_cut(breaks=c(4, 30), which=c(1, 3), scales=c(0.5, 3))
p1 + p2
Created on 2022-08-22 with reprex v2.0.2
As you can see from the example, some parts are zoomed in and zoomed out. This can be changed by using different arguments.
Arguments used:
breaks:
a numeric or numeric vector, the points to be divided
which:
integer, the position of subplots to scales, started from left to
right or top to bottom.
scales:
numeric, relative width or height of subplots.
To change the space between the subplots, you can use the argument space.
For some extra information and examples check this tutorial.
A clever ggplot solution is provided by Jörg Steinkamp, using facet_grid. Simplified, it is something like this:
library("tidyverse")
df <- data.frame(myLetter=LETTERS[1:4], myValue=runif(12) + rep(c(4,0,0),2)) # cluster a few values well above 1
df$myFacet <- df$myValue > 3
(ggplot(df, aes(y=myLetter, x=myValue))
+ geom_point()
+ facet_grid(. ~ myFacet, scales="free", space="free")
+ scale_x_continuous(breaks = seq(0, 5, .25)) # this gives both facets equal interval spacing.
+ theme(strip.text.x = element_blank()) # get rid of the facet labels
)
As of 2022-06-01, we have the elegant-looking ggbreak package, which appears to answer the OP's question. Although I haven't tried it on my own data, it looks to be compatible with many or all other ggplot2 functionality. Offers differential scaling too, perhaps useful to OP's and similar uses.
library(ggplot2)
library(ggbreak)
set.seed(2019-01-19)
d <- data.frame(x = 1:20,
y = c(rnorm(5) + 4, rnorm(5) + 20, rnorm(5) + 5, rnorm(5) + 22))
p1 <- ggplot(d, aes(y, x)) + geom_col(orientation="y") +
theme_minimal()
p1 + scale_x_break(c(7, 17), scales = 1.5) + scale_x_break(c(18, 21), scales=2)
I doubt there's anything off the shelf in R, but you could show the data as a series of 3D partial cubes. 500 is only 5*10*10, so it would scale well. The exact value could be a label.
This probably should only be used if you must have a graphic representation for some reason.
One strategy is to change the axis to plot Log Scale. This way you get to reduce exponentially higher value by a factor of 10

facet_wrap Title wrapping & Decimal places on free_y axis (ggplot2)

I have a set of code that produces multiple plots using facet_wrap:
ggplot(summ,aes(x=depth,y=expr,colour=bank,group=bank)) +
geom_errorbar(aes(ymin=expr-se,ymax=expr+se),lwd=0.4,width=0.3,position=pd) +
geom_line(aes(group=bank,linetype=bank),position=pd) +
geom_point(aes(group=bank,pch=bank),position=pd,size=2.5) +
scale_colour_manual(values=c("coral","cyan3", "blue")) +
facet_wrap(~gene,scales="free_y") +
theme_bw()
With the reference datasets, this code produces figures like this:
I am trying to accomplish two goals here:
Keep the auto scaling of the y axis, but make sure only 1 decimal place is displayed across all the plots. I have tried creating a new column of the rounded expr values, but it causes the error bars to not line up properly.
I would like to wrap the titles. I have tried changing the font size as in Change plot title sizes in a facet_wrap multiplot, but some of the gene names are too long and will end up being too small to read if I cram them on a single line. Is there a way to wrap the text, using code within the facet_wrap statement?
Probably cannot serve as definite answer, but here are some pointers regarding your questions:
Formatting the y-axis scale labels.
First, let's try the direct solution using format function. Here we format all y-axis scale labels to have 1 decimal value, after rounding it with round.
formatter <- function(...){
function(x) format(round(x, 1), ...)
}
mtcars2 <- mtcars
sp <- ggplot(mtcars2, aes(x = mpg, y = qsec)) + geom_point() + facet_wrap(~cyl, scales = "free_y")
sp <- sp + scale_y_continuous(labels = formatter(nsmall = 1))
The issue is, sometimes this approach is not practical. Take the leftmost plot from your figure, for example. Using the same formatting, all y-axis scale labels would be rounded up to -0.3, which is not preferable.
The other solution is to modify the breaks for each plot into a set of rounded values. But again, taking the leftmost plot of your figure as an example, it'll end up with just one label point, -0.3
Yet another solution is to format the labels into scientific form. For simplicity, you can modify the formatter function as follow:
formatter <- function(...){
function(x) format(x, ..., scientific = T, digit = 2)
}
Now you can have a uniform format for all of plots' y-axis. My suggestion, though, is to set the label with 2 decimal places after rounding.
Wrap facet titles
This can be done using labeller argument in facet_wrap.
# Modify cyl into factors
mtcars2$cyl <- c("Four Cylinder", "Six Cylinder", "Eight Cylinder")[match(mtcars2$cyl, c(4,6,8))]
# Redraw the graph
sp <- ggplot(mtcars2, aes(x = mpg, y = qsec)) + geom_point() +
facet_wrap(~cyl, scales = "free_y", labeller = labeller(cyl = label_wrap_gen(width = 10)))
sp <- sp + scale_y_continuous(labels = formatter(nsmall = 2))
It must be noted that the wrap function detects space to separate labels into lines. So, in your case, you might need to modify your variables.
This only solved the first part of the question. You can create a function to format your axis and use scale_y_continous to adjust it.
df <- data.frame(x=rnorm(11), y1=seq(2, 3, 0.1) + 10, y2=rnorm(11))
library(ggplot2)
library(reshape2)
df <- melt(df, 'x')
# Before
ggplot(df, aes(x=x, y=value)) + geom_point() +
facet_wrap(~ variable, scale="free")
# label function
f <- function(x){
format(round(x, 1), nsmall=1)
}
# After
ggplot(df, aes(x=x, y=value)) + geom_point() +
facet_wrap(~ variable, scale="free") +
scale_y_continuous(labels=f)
scale_*_continuous(..., labels = function(x) sprintf("%0.0f", x)) worked in my case.

cut y axis ggplot barplot [duplicate]

I want to make a bar plot where one of the values is much bigger than all other values. Is there a way of having a discontinuous y-axis? My data is as follows:
df <- data.frame(a = c(1,2,3,500), b = c('a1', 'a2','a3', 'a4'))
p <- ggplot(data = df, aes(x = b, y = a)) + geom_bar()
p <- p + opts(axis.text.x=theme_text(angle= 90, hjust=1)) + coord_flip()
p
Is there a way that I can make my axis run from 1- 10, then 490 - 500? I can't think of any other way of plotting the data (aside from transforming it, which I don't want to do)
[Edit 2019-05-06]:
8 years later, above code needs to be amended to work with version 3.1.1 of ggplot2 in order to create the same chart:
library(ggplot2)
ggplot(df) +
aes(x = b, y = a) +
geom_col() +
coord_flip()
As noted elsewhere, this isn't something that ggplot2 will handle well, since broken axes are generally considered questionable.
Other strategies are often considered better solutions to this problem. Brian mentioned a few (faceting, two plots focusing on different sets of values). One other option that people too often overlook, particularly for barcharts, is to make a table:
Looking at the actual values, the 500 doesn't obscure the differences in the other values! For some reason tables don't get enough respect as data a visualization technique. You might object that your data has many, many categories which becomes unwieldy in a table. If so, it's likely that your bar chart will have too many bars to be sensible as well.
And I'm not arguing for tables all the time. But they are definitely something to consider if you are making barcharts with relatively few bars. And if you're making barcharts with tons of bars, you might need to rethink that anyway.
Finally, there is also the axis.break function in the plotrix package which implements broken axes. However, from what I gather you'll have to specify the axis labels and positions yourself, by hand.
Eight years later, the ggforce package offers a facet_zoom() extension which is an implementation of Hadley Wickham's suggestion to show two plots (as referenced in Brian Diggs' answer).
Zoom facet
library(ggforce)
ggplot(df) +
aes(x = b, y = a) +
geom_col() +
facet_zoom(ylim = c(0, 10))
Unfortunately, the current version 0.2.2 of ggforce throws an error with coord_flip() so only vertical bars can be shown.
The zoomed facet shows the variations of the small values but still contains the large - now cropped - a4 bar. The zoom.data parameter controls which values appear in the zoomed facet:
library(ggforce)
ggplot(df) +
aes(x = b, y = a) +
geom_col() +
facet_zoom(ylim = c(0, 10), zoom.data = ifelse(a <= 10, NA, FALSE))
Two plots
Hadley Wickham suggested
I think it's much more appropriate to show two plots - one of all the
data, and one of just the small values.
This code creates two plots
library(ggplot2)
g1 <- ggplot(df) +
aes(x = b, y = a) +
geom_col() +
coord_flip()
g2 <- ggplot(df) +
aes(x = b, y = a) +
geom_col() +
coord_flip() +
ylim(NA, 10)
which can be combined into one plot by
cowplot::plot_grid(g1, g2) # or ggpubr::ggarrange(g1, g2)
or
gridExtra::grid.arrange(g1, g2) # or egg::ggarrange(g1, g2)
Two facets
This was suggested in a comment by Chase and also by Brian Diggs in his answer who interpreted Hadley's suggestion to use
faceted plots, one with all the data, one zoomed in a particular region
but no code was supplied for this approach, so far.
As there is no simple way to scale facets separately (see related question, e.g.) the data needs to be manipulated:
library(dplyr)
library(ggplot2)
ggplot() +
aes(x = b, y = a) +
geom_col(data = df %>% mutate(subset = "all")) +
geom_col(data = df %>% filter(a <= 10) %>% mutate(subset = "small")) +
coord_flip() +
facet_wrap(~ subset, scales = "free_x")
No, not using ggplot. See the discussion in the thread at http://groups.google.com/group/ggplot2/browse_thread/thread/8d2acbfc59d2f247 where Hadley explains why it is not possible but gives a suggested alternative (faceted plots, one with all the data, one zoomed in a particular region).
Not with ggplot, but with plotrix you can easily do that:
library(plotrix)
gap.barplot(df$a, gap=c(5,495),horiz=T)
No, unfortunately not
The fear is that allowing discontinuous axes will lead to deceit of the audience. However, there are cases where not having a discontinuous axis leads to distortion.
For example, if the axis is truncated, but usually lies within some interval (say [0,1]), the audience may not notice the truncation and make distorted conclusions about the data. In this case, an explicit discontinuous axis would be more appropriate and transparent.
Compare:
An option could be using the ggbreak package using the scale_y_cut() or scale_x_cut() function. This function makes it possible to cut the ggplot object into parts with the possibility to specify which part is zoom in or zoom out. Here is a reproducible example with left plot normal and right plot with the function used:
df <- data.frame(a = c(1,2,3,500), b = c('a1', 'a2','a3', 'a4'))
library(ggplot2)
library(ggbreak)
library(patchwork)
p1 <- ggplot(df) +
aes(x = b, y = a) +
geom_col()
p2 <- ggplot(df) +
aes(x = b, y = a) +
geom_col() +
scale_y_cut(breaks=c(4, 30), which=c(1, 3), scales=c(0.5, 3))
p1 + p2
Created on 2022-08-22 with reprex v2.0.2
As you can see from the example, some parts are zoomed in and zoomed out. This can be changed by using different arguments.
Arguments used:
breaks:
a numeric or numeric vector, the points to be divided
which:
integer, the position of subplots to scales, started from left to
right or top to bottom.
scales:
numeric, relative width or height of subplots.
To change the space between the subplots, you can use the argument space.
For some extra information and examples check this tutorial.
A clever ggplot solution is provided by Jörg Steinkamp, using facet_grid. Simplified, it is something like this:
library("tidyverse")
df <- data.frame(myLetter=LETTERS[1:4], myValue=runif(12) + rep(c(4,0,0),2)) # cluster a few values well above 1
df$myFacet <- df$myValue > 3
(ggplot(df, aes(y=myLetter, x=myValue))
+ geom_point()
+ facet_grid(. ~ myFacet, scales="free", space="free")
+ scale_x_continuous(breaks = seq(0, 5, .25)) # this gives both facets equal interval spacing.
+ theme(strip.text.x = element_blank()) # get rid of the facet labels
)
As of 2022-06-01, we have the elegant-looking ggbreak package, which appears to answer the OP's question. Although I haven't tried it on my own data, it looks to be compatible with many or all other ggplot2 functionality. Offers differential scaling too, perhaps useful to OP's and similar uses.
library(ggplot2)
library(ggbreak)
set.seed(2019-01-19)
d <- data.frame(x = 1:20,
y = c(rnorm(5) + 4, rnorm(5) + 20, rnorm(5) + 5, rnorm(5) + 22))
p1 <- ggplot(d, aes(y, x)) + geom_col(orientation="y") +
theme_minimal()
p1 + scale_x_break(c(7, 17), scales = 1.5) + scale_x_break(c(18, 21), scales=2)
I doubt there's anything off the shelf in R, but you could show the data as a series of 3D partial cubes. 500 is only 5*10*10, so it would scale well. The exact value could be a label.
This probably should only be used if you must have a graphic representation for some reason.
One strategy is to change the axis to plot Log Scale. This way you get to reduce exponentially higher value by a factor of 10

Resources