how to prevent axes from intersecting in ggplot2 - r

I'm using ggplot2 to make line graphs of some log-transformed data that all have large values (between 10^6 and 10^8); since the axes doesn't start at zero, I'd prefer not to have them intersect at the "origin."
Here's what the axes currently look like:
I'd prefer something more like one gets from base graphics (but I'm additionally using geom_ribbon and other fancy things I really like in ggplot2, so I'd prefer to find a ggplot2 solution):
Here's what I'm doing currently:
mydata <- data.frame(Day = rep(1:8, 3),
Treatment = rep(c("A", "B", "C"), each=8),
Value = c(7.415929, 7.200486, 7.040555, 7.096490, 7.056413, 7.143981, 7.429724, 7.332760, 7.643673, 7.303994, 7.343151, 6.923636, 6.923478, 7.249170, 7.513370, 7.438630, 7.209895, 7.000063, 7.160154, 6.677734, 7.026307, 6.830495, 6.863329, 7.319219))
ggplot(mydata, aes(x=Day, y=Value, group=Treatment))
+ theme_classic()
+ geom_line(aes(color = Treatment), size=1)
+ scale_y_continuous(labels = math_format(10^.x))
+ coord_cartesian(ylim = c(6.4, 7.75), xlim=c(0.5, 8))
plot(mydata$Day, mydata$Value, frame.plot = F) #non-intersecting axes

Workaround for this problem would be to remove axis lines with theme(axis.line=element_blank()) and then add false axis lines with geom_segment() - one for x axis and second for y axis. x, y , xend and yend values are determined from your plot (taken as the smallest and the largest values shown on plot for each corresponding axis) and axis limits used in coord_cartesian() (minimal value of limits to ensure that segment is plotted in place of axis).
ggplot(mydata, aes(x=Day, y=Value, group=Treatment)) +theme_classic() +
geom_line(aes(color = Treatment), size=1) +
scale_y_continuous(labels = math_format(10^.x))+
coord_cartesian(ylim = c(6.4, 7.75), xlim=c(0.5, 8))+
theme(axis.line=element_blank())+
geom_segment(x=2,xend=8,y=6.4,yend=6.4)+
geom_segment(x=0.5,xend=0.5,y=6.5,yend=7.75)

An older question. But since I was looking for this functionality recently I thought I'd flag the ggh4x package, which adds guides for truncating axes.
library(ggh4x)
#> Loading required package: ggplot2
ggplot(data.frame(x=0:10, y=0:10), aes(x, y)) +
geom_point() +
theme_classic() +
guides(x = "axis_truncated", y = "axis_truncated")
Created on 2023-02-17 with reprex v2.0.2
Apart from convenience, two nice things about the ggh4x option are that 1) it is stable across more complex plot compositions like faceting and 2) its dependencies are a subset of those belonging to ggplot2, so you aren't introducing a bunch of additional imports.
P.S. There's an open GitHub issue to bring this kind of "floating axes" functionality to the main ggplot2 library. It looks like it will eventually be incorporated.

Related

How to seperate histogram plot into two facet with ggplot in r?

I want to separate my histogram into two parts and zoom the second part. In short, I want to keep the histogram in the original shape, just zoom the x-axis tail.
Using mpg dataset as an example, I create a facet label according to 'displ' column and create a histogram plot.
mpg$displn<-scale(mpg$displ)
mpg$myFacet<-"01"
mpg$myFacet[mpg$displn>1]<-"02"
library(ggh4x)
ggplot(mpg,aes(x=displn))+geom_histogram(aes(y=..density..),binwidth = 0.1)+ facet_grid(. ~ myFacet, scales="free", space="free") + scale_x_continuous(breaks = seq(-1.5, 2.5, 1)) + theme(strip.text.x = element_blank())+ theme(panel.spacing=unit(0,'npc')) +force_panelsizes(cols = c(0.3, 1))
The question is the two facets using different 'y=..density..' and looks different from the original figure.
Is there any suggestion on how should I improve this?
Thanks in advance!
Typically, one would use ggforce::facet_zoom() for this purpose:
library(ggplot2)
library(ggforce)
ggplot(mpg, aes(x = scale(displ))) +
geom_histogram(aes(y = after_stat(density)), binwidth = 0.1) +
facet_zoom(xlim = c(1, 3))
Created on 2022-01-13 by the reprex package (v2.0.1)
The reason your original approach doesn't work is because densities are calculated by group, and data belonging to different panels are automatically separated into different groups.

How to draw circles inside each other with ggplot2?

I want to draw two circles inside each other with ggplot2.
So far my effort is:
make a fake data and plot it with geom_line(). If I convert this with coord_polar() then I will not be able to see two different circles the one inside each other
library(ggplot2)
library(tidyverse)
x1=seq(0,6000000,1000)
y1=rep(1,length(x1))
y2=rep(2,length(x1))
data=as.data.frame(cbind(x1,y1,y2))
Created on 2021-12-25 by the reprex package (v2.0.1)
# plot the data
ggplot(data) +
geom_line(aes(x1,y1)) +
geom_line(aes(x1,y2))
#coord_polar()
I would avoid the geom_circle option and use the coord_polar option if possible.
The reason is that these two circles have some differences in the x-axis, which I would indicate after drawing the circles.
I would like my plot to look like this
The code you have with coord_polar() is correct, just the plot limits need adjusting to see both the circles, e.g.
ggplot(data) +
geom_line(aes(x1,y1)) +
geom_line(aes(x1,y2)) +
coord_polar() + ylim(c(0,NA))
The reason for using ylim is that this is the direction getting transformed to the radius by the coord_polar()
Why not use two geom_point() with different sizes and pch = 21?
library(ggplot2)
df <- tibble(x = 0, y = 0)
ggplot(df, aes(x, y)) +
geom_point(pch = 21, size = 50) +
geom_point(pch = 21, size = 40) +
theme_void()

Increase spaces between x values of boxplot (overlapping x labels)

Hello I am very new to using coding language and recently made my first couple of figures in R. I used this code to make the figures and they turned out good except that the labels in the x axis were overlapping.
library(ggplot2)
ggplot(LR_density, aes(x=Plant_Lines, y=`Lateral_Root_Density.(root/cm)`, fill=Expression_Type)) +
geom_boxplot() +
geom_jitter(color="black", size=0.4, alpha=0.9) +
ggtitle("Lateral root density across plant expression types")
The figure produced by the line of code I used
I was wondering if anyone knew how to get the x axis labels to be more spaced out in ggplot2 boxplots. I have been looking around but havent found a clear answer on this. Any help on what to do or where to look would be great!
As per comment, this thread shows another option to deal with overlapping x axis labels, which one can use since ggplot2 3.3.0
In included a second graph which "squeezes" the axis a bit, which kind of also simulates the effect of changing the viewport/ file size.
library(ggplot2)
ggplot(diamonds, aes(x = cut, y = price)) +
geom_boxplot() +
scale_x_discrete(guide = guide_axis(n.dodge = 2))
ggplot(diamonds, aes(x = cut, y = price)) +
geom_boxplot() +
scale_x_discrete(guide = guide_axis(n.dodge = 2)) +
coord_fixed(1/10^3.4)
Created on 2020-04-30 by the reprex package (v0.3.0)

Introduce explicit line break in ggplot2 on the Y-axis (boxplot)

I'm attempting to write some code that can be used to make boxplots of temperatures at which proteins melt at, I'm 99% there except I need to introduce a line break on the y-axis of my boxplot.
Essentially, my current y axis scale goes from 45-60, I want to make the y axis start at 0, line break, 45-60. See the picture as an e.g.
I've tried using the scale_y_continuous to set a break but that didn't work as I'd hoped.
df %>%
group_by(Protein) %>%
ggplot(., aes(x = factor(Protein), y = Melting_Temperature)) +
geom_boxplot() +
theme_classic() +
geom_point(aes(x = as.numeric(df$Protein) + 0.5, colour = Protein),
alpha=0.7)+
xlab("Protein Type")+
ylab("Melting Temperature") +
stat_summary(fun.y=mean, colour = "darkred", geom = "point", shape =
18, size = 3, show_guide = FALSE) +
geom_text(data = means, aes(label = round(Melting_Temperature, 1), y =
Melting_Temperature + 0.5))
IMHO, tick marks and axis labels should be sufficient to indicate the range of data on display. So, there is no need to start an axis at 0 (except for bar charts and alike).
However, the package ggthemes offers Tufte style axes which might be an alternative to the solution the OP is asking for:
library(ggplot2)
library(ggthemes)
ggplot(iris) +
aes(x = Species, y = Sepal.Length) +
geom_boxplot() +
geom_rangeframe() +
theme_tufte(base_family = "")
Note that the iris dataset is used here in place of OP's data which are not available.
geom_rangeframe() plots axis lines which extend to the maximum and minimum of the plotted data. As the plot area is usually somewhat larger this creates a kind of gap.
theme_tufte() is a theme based on Chapter 6 "Data-Ink Maximization and Graphical Design" of Edward Tufte's The Visual Display of Quantitative Information with no border, no axis lines, and no grids.
This is not supported in ggplot as built. In this discussion from 2010, Hadley Wickham (author of ggplot as well as RStudio et al) explains that axis breaks are questionable practice in his view.
Those comments by Hadley are linked, and other options discussed, in this prior SO discussion.

R plot type "b" with text instead of points - Slope graph with ggplot2

is there a way in ggplot2 to get the plot type "b"? See example:
x <- c(1:5)
y <- x
plot(x,y,type="b")
Ideally, I want to replace the points by their values to have something similar to this famous example:
EDIT:
Here some sample data (I want to plot each "cat" in a facet with plot type "b"):
df <- data.frame(x=rep(1:5,9),y=c(0.02,0.04,0.07,0.09,0.11,0.13,0.16,0.18,0.2,0.22,0.24,0.27,0.29,0.31,0.33,0.36,0.38,0.4,0.42,0.44,0.47,0.49,0.51,0.53,0.56,0.58,0.6,0.62,0.64,0.67,0.69,0.71,0.73,0.76,0.78,0.8,0.82,0.84,0.87,0.89,0.91,0.93,0.96,0.98,1),cat=rep(paste("a",1:9,sep=""),each=5))
Set up the axes by drawing the plot without any content.
plot(x, y, type = "n")
Then use text to make your data points.
text(x, y, labels = y)
You can add line segments with lines.
lines(x, y, col = "grey80")
EDIT: Totally failed to clock the mention of ggplot in the question. Try this.
dfr <- data.frame(x = 1:5, y = 1:5)
p <- ggplot(dfr, aes(x, y)) +
geom_text(aes(x, y, label = y)) +
geom_line(col = "grey80")
p
ANOTHER EDIT: Given your new dataset and request, this is what you need.
ggplot(df, aes(x, y)) + geom_point() + geom_line() + facet_wrap(~cat)
YET ANOTHER EDIT: We're starting to approach a real question. As in 'how do you make the lines not quite reach the points'.
The short answer is that that isn't a standard way to do this in ggplot2. The proper way to do this would be to use geom_segment and interpolate between your data points. This is quite a lot of effort however, so I suggest an easier fudge: draw big white circles around your points. The downside to this is that it makes the gridlines look silly, so you'll have to get rid of those.
ggplot(df, aes(x, y)) +
facet_wrap(~cat) +
geom_line() +
geom_point(size = 5, colour = "white") +
geom_point() +
opts(panel.background = theme_blank())
There's an experimental grob in gridExtra to implement this in Grid graphics,
library(gridExtra)
grid.newpage() ; grid.barbed(pch=5)
This is now easy with ggh4x::geom_pointpath. Set shape = NA and add a geom_text layer.
library(ggh4x)
#> Loading required package: ggplot2
df <- data.frame(x = rep(1:5, each = 5),
y = c(outer(seq(0, .8, .2), seq(0.02, 0.1, 0.02), `+`)),
cat = rep(paste0("a", 1:5)))
ggplot(df, aes(x, y)) +
geom_text(aes(label = cat)) +
geom_pointpath(aes(group = cat, shape = NA))
Created on 2021-11-13 by the reprex package (v2.0.1)
Another way to make great slope graphs is using the package CGPfunctions.
library(CGPfunctions)
newggslopegraph(newcancer, Year, Survival, Type)
You have also many options to choose. You can find a good tutorial here:
https://www.r-bloggers.com/2018/06/creating-slopegraphs-with-r/

Resources