Time-varying width of geom_segment in ggplot2 - r

I would like to plot a rectangle whose width increases as the x-axis on a plot increases. Geom_segment is a great way to plot lines but you cannot map size within aes(). You can only select one size for the entire segment:
geom_segment(aes(x=5,xend=10,y=10,yend=10),size=10)
This doesn't work, the size doesn't vary with the value of x_axis_variable:
geom_segment(aes(x=5,xend=10,y=10,yend=10,size=x_axis_variable))
where x_axis_variable is whatever continuous variable you have plotted on the x-axis.
Is there a workaround, or some other option, to plot a single line whose size varies along the X or Y axes?
I'm happy to post some example data, but I'm actually not sure how helpful it would be for this question because it's not dependent upon data structure. I think it's just an artifact of geom_segment and hopefully there's another option. Thanks!
Edit with sort of the expected output:
Except that I'd like the line to increase gradually over the x-axis, not discretely as in the example.

Can you just use geom_line()?
library(tidyverse)
library(ggplot2)
d <- tibble(x = 1:20, y=5)
ggplot(d, aes(x=x, y=y, size=I(x), color=x)) +
geom_line()

Geom_segment is a great way to plot lines but you cannot map size
within aes().
Is this premise true? Check out my artistic chart:
ggplot(mtcars) +
geom_segment(aes(wt, mpg, xend = dplyr::lead(wt),
yend = dplyr::lead(mpg), size = gear))
Or this:
ggplot(data = data.frame(x = 1:5),
aes(x = x, xend = x + 1,
y = 0, yend = 0, size = x)) +
geom_segment()
geom_segment draws one segment with one size for each element of data you map. If you want the single segment to vary along its length, you might use ggforce::geom_link, like here, where it interpolates the size by making the segment into many pieces.
ggplot() +
geom_segment(aes(x = 0, xend = 1, y = 0, yend = 0)) +
ggforce::geom_link(aes(x = 0, xend = 1, y = 0.5, yend = 0.5, size = after_stat(index)^2)) +
scale_size(range = c(0,10))
For a rectangle you might do something like:
ggplot() +
ggforce::geom_link2(aes(x = c(0, 0, 1, 1, 0),
xend = c(0, 1, 1, 0, 0),
y = c(0,1,1,0, 0),
yend = c(1,1,0,0, 1),
size = c(1,1,2,2, 1)), lineend = "round")

Related

ggplot2 geom_pointline doesn't link centers of points

I would like to create a plot with points and lines between them, but with spaces, in ggplot2, R. I have a shaded area in the plot, so some parts of points has gray and white background. I found lemon library with geom_pointline function.
ggplot(data = dt, aes(x = x, y = y)) +
geom_ribbon(aes(ymin = min, ymax = max), fill = "gray", alpha = 0.35) +
geom_pointline(shape = 19, linecolor = "black", size = 4, color = "blue", distance = 2)
The result I get is shown below. As one can notice, the lines don't start and end in the middle of points, but rather at the top right and bottom left of the point. It gets even worse when I shorten the lines. I tried with many parameters but couldn't solve it. I would like the lines to start and end closer to the middle than it is now.
Thanks in advance!
If switching to an other package is an option for you then one option to achieve your desired result would be ggh4x::geom_pointpath whichs similar to geom_pointline adds some padding around points along a line or path. One drawback is that TBMK it has no option to set different colors for the points and the lines. A hack would be to draw the lines via ggh4x::geom_pointpath then add a geom_point on top of it.
Using some fake example data:
set.seed(123)
dt <- data.frame(
x = seq(20, 160, 20),
y = 1:8,
min = 1:8 - runif(8),
max = 1:8 + runif(8)
)
library(ggplot2)
library(ggh4x)
ggplot(data = dt, aes(x = x, y = y)) +
geom_ribbon(aes(ymin = min, ymax = max), fill = "gray", alpha = 0.35) +
geom_pointpath(shape = 19, size = 4, color = "black", mult = .25) +
geom_point(shape = 19, size = 4, color = "blue")

Maintaining Aspect Ratio of Shapes and Images in ggplot/ ggimage

I'm trying to build a visualisation with both drawn shapes (e.g. using geom_circle) and images. In both cases, I want to be able to position them on the page specifically with coordinates, rather than using one of the built in chart types.
See update further down...
However, I can either get the circles with the correct aspect ratio (i.e. round not oval) or the images, but not both. In the example below, you can see the image is not shown as square when it is.
I have tried various things including coord_fixed, scale_size_identity and coord_cartesian, but to no avail. The overall output will be landscape, which is why I have set the cartesian limits as I have.
This is a simplified version. In the full version, I'll get the coordinates from the data (which I'm fine with).
images <-data.frame(url = c("https://upload.wikimedia.org/wikipedia/commons/d/de/Windows_live_square.JPG"))
ggplot(mpg) +
ggforce::geom_circle(aes(x0 = displ * 50 - 60, y0 = hwy, r=cty)) +
#scale_size_identity() +
# Add Image
ggimage::geom_image(data = images,
aes(x = 4, y = 20, image=url),
size = 0.4,
hjust = 0.0,
by="height"
) +
coord_cartesian(
xlim = c(0, 120),
ylim = c(0, 80),
expand = FALSE,
clip = "on"
)
Update following really helpful input from #tjebo and further investigation.
I have now found there are at least 4 ways to add images to plots, each with their own advantages and disadvantages. I've listed these below to help others on this search.
Draw basic shapes to which images can be added
g <- ggplot(mpg) +
ggforce::geom_circle(aes(x0 = displ * 50 - 60, y0 = hwy, r=cty))
Plot with ggtexture - multiple images - aspect defined by x and y max - min
https://rdrr.io/github/clauswilke/ggtextures/man/geom_textured_rect.html
g + ggtextures::geom_textured_rect(data = images,
aes(xmin = 20, xmax = 60,
ymin = 20, ymax = 60, image = url),
lty = "blank", # line type of blank to remove border
fill="white", # used to fill transparent areas of image
nrow = 1,
ncol = 1,
img_width = unit(1, "null"),
img_height = unit(1, "null"),
position = "identity") +
coord_equal() # A fixed scale coordinate system forces a specified ratio between the physical representation of data units on the axes.
Plot with ggimage - multiple images - aspect defined by device
g + ggimage::geom_image(data = images,
aes(x = 20, y = 40, image=url),
size = 0.2,
by="height"
)
Plot with cowplot - single image - freedom over aspect
Independent drawing surface and scale (0-1)
cowplot::ggdraw(g) +
cowplot::draw_image(images[[1, "url"]],
x = .5, y = .3, width = 0.5, height = 0.5)
Plot with annotation_custom (ggplot) - original aspect
Seems to use widest of width of height and centre on mid coordinates
image <- magick::image_read(images[[1, "url"]])
rasterImg <- grid::rasterGrob(image, interpolate = FALSE)
g + annotation_custom(rasterImg, xmin = 00, xmax =200, ymin = 10, ymax = 50)
I strongly feel you may have seen this thread: Can geom_image() from the ggimage package be made to preserve the image aspect ratio? - this was asked and answered by ggplot2 gurus such as Claus Wilke, Baptiste and Marco Sandri - it seems that ggimage is scaling to the device. Thus if you would like a square, you need to save to a device of square dimensions. Or, if you don't have a square image, of course, dimensions relative to your image used.
I used see::geom_point2 (no weird border), because I got the strong feeling that you have not used ggforce::geom_circle. Also mild changes where I added the aes call.
library(ggimage)
#> Loading required package: ggplot2
images <-data.frame(url = c("https://upload.wikimedia.org/wikipedia/commons/d/de/Windows_live_square.JPG"))
# g <-
ggplot(mpg) +
see::geom_point2(aes(x = displ, y = hwy, size = cty), alpha = 0.2) +
scale_size_identity() +
# Add Image
geom_image(data = images,
aes(x = 4, y = 20, image=url),
size = 0.4,
hjust = 0.0,
by = "height"
)
Using reprex, with both fig width and height set to 3 inches
Created on 2021-02-13 by the reprex package (v1.0.0)
A different approach would be to not use ggimage - e.g., use cowplot for custom annotation, makes it super simple to add an image.
library(ggplot2)
library(cowplot)
p <- ggplot(mpg) +
see::geom_point2(aes(x = displ, y = hwy, size = cty), alpha = 0.2) +
scale_size_identity()
ggdraw(p) +
draw_image("https://upload.wikimedia.org/wikipedia/commons/d/de/Windows_live_square.JPG",
x = .5, y = .3, width = 0.5, height = 0.5)
Created on 2021-02-13 by the reprex package (v1.0.0)
Or, use the ggtextures package, with a little tweak of the coordinate system
this discussion seems relevant
library(ggtextures)
library(ggplot2)
library(tibble)
img_df <- tibble(
xmin = 1, ymin = 1, xmax = 4, ymax = 4,
image = "https://upload.wikimedia.org/wikipedia/commons/d/de/Windows_live_square.JPG"
)
ggplot(mpg) +
see::geom_point2(aes(x = displ, y = hwy, size = cty), alpha = 0.2) +
geom_textured_rect(data = img_df,
aes(xmin = xmin, xmax = xmax,
ymin = ymin, ymax = ymax, image = image),
nrow = 1,
ncol = 1,
img_width = unit(1, "null"),
img_height = unit(1, "null"),
position = "identity") +
coord_equal() # this is then necessary...
Created on 2021-02-13 by the reprex package (v1.0.0)

Transform x,y coordinate space in ggplot

I know that you can transform the coordinates of a plot using coord_trans(), and you can even perform coordinate transformations along both axes (e.g. coord_trans(x = "log10", y = "log10")), but is there a way to perform a coordinate transformation that depends on the values of both axes, like a shear?
I know that I can perform the linear transformation before I pass my data to ggplot using something like ggforce::linear_trans() like this example:
trans <- linear_trans(shear(1, 0))
square <- data.frame(x = c(0, 0, 1, 1), y = c(0, 1, 1, 0))
square2 <- trans$transform(square$x, square$y)
ggplot(square2, aes(x, y)) +
geom_polygon(colour = 'black')
However, I'm hoping that there would be a way to write a custom coordinate system such that the data doesn't need to be transformed beforehand, e.g.:
square <- data.frame(x = c(0, 0, 1, 1), y = c(0, 1, 1, 0))
ggplot(square, aes(x, y)) +
geom_polygon(colour = 'black') +
coord_shear(x=1)
I implemented a custom coord that does this. It takes a transformer like that produced by ggforce::linear_trans and applies it to a ggplot. Check it out in my deeptime package here.

R ggplot overlapping lines to use matplotlib colour behaviour

When two lines coincide, matplotlib uses the "sum" of the two line colours, while ggplot uses colour from one line. The matplotlib way makes it clearer that the two lines are overlapping. Is it possible to make ggplot do the similar colouring?
Setting alpha sort of does that, but with alpha, the resulting colour is dominated by the top colour. (If alpha = 0.5, then top colour gets opacity 0.5 and under colour gets opacity 0.5 * 0.5.)
matplotlib
pd.DataFrame({'A' : [0,1,2,3, 4], 'B' : [-1, 0, 2, 3, 0]}).plot(title = 'matplotlib in python')
ggplot
dt = data.table(name = rep(c('A','B'), each = 5),
y = c(0,1,2,3,4,-1, 0, 2, 3, 0),
x = 1:5)
ggplot(dt) +
geom_line(aes(x = x, y = y, col = name)) +
ggtitle('ggplot in R')

Is it possible to insert a line of no discrimination in ROC plot using ggroc?

I have created a ROC plot with multiple ROC-curves using ggroc based on pROC. How can I insert a line of no discrimination?
I would like to have a line of no discrimination from 0,0 to 1,1 in my plot, so that I can better visually evaluate my ROC-curves.
I have tried using the plot() function on my ggplot object, and I have tried using + geom_abline(), and the lines() function without any luck.
library(pROC)
#Creating curves and labeling)
ROC_curves <- ggroc(list(log=ROC_log, tree=ROC_tree, xgbt=ROC_xgbt), legacy.axes=TRUE)
ROC_curves2 <- ROC_curves + xlab("FPR") + ylab("TPR")
#but this part doesn't Work:
+ qplot(1,1) + geom_abline(intercept=0, slope=1)
I have also tried doing:
plot(ROC_curves2, identity=TRUE)
I would like a line of no discrimination going from 0,0 to 1,1 in my plot.
When adding qplot(1,1) + geom_abline(), I get "Error: Don't know how to add o to a plot".
When using plot() a plot is returned, but still with no line.
The ROC_curves already returns a ggplot plot. Adding a new plot to it with qplot is not possible nor necessary, just add geom_abline directly:
ROC_curves + xlab("FPR") + ylab("TPR") +
geom_abline(intercept = 0, slope = 1,
color = "darkgrey", linetype = "dashed")
The abline extends beyond the limits of the ROC curve. To avoid that you can use geom_segment instead:
ROC_curves + xlab("FPR") + ylab("TPR") +
geom_segment(aes(x = 0, xend = 1, y = 0, yend = 1),
color="darkgrey", linetype="dashed")
Also note that if you weren't using legacy.axes=TRUE you would need to have intercept = 1 so that the line crosses the 0 line on the top right.
... + geom_segment(aes(x = 0, xend = 1, y = 0, yend = 1)) # legacy.axes = TRUE
... + geom_segment(aes(x = 1, xend = 0, y = 0, yend = 1)) # legacy.axes = FALSE
#Calimo's solution didn't work for me but I think that is due to the size of my dataset so the graph won't render. Found a gitlab issue (https://github.com/tidyverse/ggplot2/issues/4190) about how annotate is much faster than geom_segment. I'm using the following:
+ annotate("segment",x = 1, xend = 0, y = 0, yend = 1, color="red", linetype="dashed")

Resources