Home > OS >  Arrange marginal rug and density so they don't overlap with legend at bottom
Arrange marginal rug and density so they don't overlap with legend at bottom

Time:02-02

Let's have a following dataframe:

test <- structure(list(category = c("C", "C", "C", "C", "C", "C", "C", 
"C", "C", "C", "C", "C", "C", "C", "C", "C", "C", "C", "C", "C", 
"C", "C", "C", "C", "C", "C", "C", "C", "C", "C", "C", "C", "C", 
"C", "C", "C", "C", "C", "C", "C", "C", "C", "C", "C", "C", "C", 
"C", "C", "C", "C", "C", "C", "C", "C", "C", "C", "C", "C", "C", 
"C", "C", "C", "C"), position = c(43.87, 175.63, 119.58, 63.82, 
70.7, 87, 103.23, 128.84, 31.52, 99.21, 40.65, 27.44, 139.39, 
95.38, 87.56, 16.94, 45.18, 32.93, 62.68, 225.38, 48.85, 87.97, 
18.28, 82.39, 76.08, 23.91, 57.61, 76.3, 47.78, 108.4, 43.73, 
228.35, 71.39, 40.92, 59.93, 113.23, 29.51, 9.97, 134.46, 79.26, 
188.23, 24.96, 131.29, 52.58, 114.98, 100.4, 75.89, 54.44, 61.1, 
27.36, 26.95, 22.25, 62.63, 36.87, 15.19, 88.69, 28.92, 235.41, 
35.49, 46.76, 97.62, 47.67, 24.91), length = c(110.83, 205.63, 
135.97, 139.4, 137.48, 120.56, 131, 147.17, 90.37, 108.25, 141.27, 
154.22, 157.4, 143.66, 151.57, 185.03, 313.36, 60.45, 81.37, 
256.44, 140.56, 188.46, 100.16, 203.62, 172.79, 139.8, 174.39, 
132.27, 126.19, 129.12, 152.96, 294.5, 190.25, 112.24, 224.06, 
172.53, 308.87, 118.08, 168.83, 115.55, 238.9, 215.96, 159.52, 
88.47, 178.92, 178.92, 178.38, 81.43, 71.2, 125.03, 139.9, 93.25, 
144.58, 164.3, 126.32, 130.55, 102.31, 246.91, 136.16, 138.35, 
228.73, 124.76, 88.11)), row.names = c(NA, -63L), class = c("tbl_df", 
"tbl", "data.frame"))

I would like to plot the variables as scatterplot, add rug on the y-axis and marginal distributions on both axes. I would also like to make the chart aesthetically pleasing.

This is my most satisfying result so far:

library(ggplot2)
library(ggExtra)

plottest <- ggplot2::ggplot(test, aes(x=length, y=position, colour = category))   
  ggplot2::geom_point()  ggplot2::geom_rug(sides = "r",
                                           col="steelblue",
                                           alpha=0.2, 
                                           size=1.5,
                                           outside = TRUE)   
  ggplot2::theme_minimal()   
  ggplot2::theme(legend.position="bottom",
                 axis.line.x = element_line(size = 0.45, color="#3a424f"),
                 axis.line.y = element_line(size = 0.45, color="#3a424f"))   
  ggplot2::coord_cartesian(clip = "off")   
  ggplot2::scale_color_manual(values=c("#174e73"))

ggExtra::ggMarginal(plottest, groupColour = TRUE) 
 

And here is the result:

enter image description here

However I would prefer to have filled distributions and theme_bw() instead of theme_minimal(). However, in this case, the rug overlaps with distribution. This is not a big deal in case of this minimal example, however in my real-life data this can be more annoying.

I am wondering how to plot rug on the outer side of the plot and move distribution a little bit away from the rug and plotting area, so the elements would not be cluttered one atop of another.

This is another version of my code, with filled densities and preferred theme:

plottest <- ggplot2::ggplot(test, aes(x=length, y=position, colour = category))   
  ggplot2::geom_point()  ggplot2::geom_rug(sides = "r",
                                           col="steelblue",
                                           alpha=0.2, 
                                           size=1.5,
                                           outside = TRUE)   
  ggplot2::theme_bw()   
  ggplot2::theme(legend.position="bottom")   
  ggplot2::coord_cartesian(clip = "off")   
  ggplot2::scale_color_manual(values=c("#174e73"))

ggExtra::ggMarginal(plottest, groupColour = TRUE,groupFill = TRUE) 

This is what I produce with this code: enter image description here

And this is what I would like to have (this graph is prepared in gimp): enter image description here

CodePudding user response:

This is not trivial, as the position of your marginal plot is sort of "relatively hard coded" with ggExtra:::addRightMargPlot in the sense that it is adding an extra gtable column directly adjacent to the panel, but here three (relatively simple) workarounds.

  1. Figure 1. My favourite: Add an "invisible" dupe y axis and make cheeky use of the common newbie "mistake" to remove titles with "" (this is still drawing something, and it is in general better to use NULL, but not here!). You could then even add a margin to this invisible title, but this is not even necessary here.

  2. Figure 2: You can play around with the size argument. This will change the plot appearance drastically and might not be what you want.

  3. Figure 3 (by far the easiest): You can simply accept to plot the rug "inside" which is not necessarily such a huge compromise in your visual.

library(ggExtra)
library(ggplot2)

p <-
  ggplot(test, aes(x = length, y = position, colour = category))  
  geom_point()  
  geom_rug(
    sides = "r",
    col = "steelblue",
    alpha = 0.2,
    size = 1.5,
    outside = TRUE
  )  
  coord_cartesian(clip = "off")  
  scale_color_manual(values = c("#174e73"))  
  ## add a second axis 
  ## use of the common title = "" newbie mistake to your advantage
  scale_y_continuous(sec.axis = dup_axis(name = ""))  
  theme_minimal()  
  theme(legend.position = "bottom",
        axis.line.x = element_line(size = 0.45, color = "#3a424f"),
        axis.line.y = element_line(size = 0.45, color = "#3a424f"),
        ## remove all stuff from your axis 
        axis.text.y.right = element_blank(), 
        axis.ticks.y.right = element_blank(), 
        axis.line.y.right = element_blank())
#> Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
#> ℹ Please use `linewidth` instead.
#> Warning: The `size` argument of `element_line()` is deprecated as of ggplot2 3.4.0.
#> ℹ Please use the `linewidth` argument instead.

ggMarginal(p, groupFill = TRUE)

plottest <-
  ggplot(test, aes(x = length, y = position, colour = category))  
  geom_point()  
## I've removed geom_rug for the below
  theme_minimal()  
  theme(
    legend.position = "bottom",
    axis.line.x = element_line(size = 0.45, color = "#3a424f"),
    axis.line.y = element_line(size = 0.45, color = "#3a424f")
  )  
  coord_cartesian(clip = "off")  
  scale_color_manual(values = c("#174e73"))
#> Warning: The `size` argument of `element_line()` is deprecated as of ggplot2 3.4.0.
#> ℹ Please use the `linewidth` argument instead.


## Option 2 - playing around with sizes
## p_outside is your original plot
p_outside <- plottest  
  geom_rug(
    sides = "r",
    col = "steelblue",
    alpha = 0.2,
    size = 1.5,
    outside = TRUE
  )
#> Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
#> ℹ Please use `linewidth` instead.
ggMarginal(p_outside, size = 1, groupFill = TRUE)

## Option 3 - don't plot the rug outside
## p_inside is your plot with outside argument removed
p_inside <- plottest  
  geom_rug(
    sides = "r",
    col = "steelblue",
    alpha = 0.2,
    size = 1.5,
  )

ggMarginal(p_inside, groupFill = TRUE)

  • Related