ggalt icon indicating copy to clipboard operation
ggalt copied to clipboard

dumbbell three X arguments

Open pedrocr83 opened this issue 5 years ago • 8 comments

I'm using geom_dumbbell (really cool btw), to plot 3 values on the x-axis to compare the distances between them. Because it only takes x and x_end args i'm using geom_dumbbell twice, efectively solving my problem. However, since my data changes everytime i run the plot sometimes the line connecting x and x_end of the last plot being created goes over one of the points. Can you see a way around this or another way of plotting 3 values using geom_dumbbell. (example attached)

dumbbell

pedrocr83 avatar Jun 06 '19 10:06 pedrocr83

UPDATE: I'm using IF statements to decide which dumbbell graph is drawn first but this seems a subpar solution...

pedrocr83 avatar Jun 06 '19 13:06 pedrocr83

Well met, @pedrocr83!

This is a super interesting request (i.e. making this a more generic function). Lemme ponder a bit what said API would look like.

In the interim, here's an example for how to build this with with three points (it works for n points, really) "manually"

library(hrbrthemes)
library(tidyverse)

tibble(
  val1 = c(3, 2, 4),
  val2 = c(1, 4, 5),
  val3 = c(5, 8, 6),
  cat = factor(month.name[1:3], levels = rev(month.name[1:3]))
) -> xdf

ggplot() +
  # this draws leading eye-tracker dotted line from the category name
  # to the first point (helps readers follow the values to the categories)
  geom_segment(
    data = gather(xdf, measure, val, -cat) %>% 
      group_by(cat) %>% 
      top_n(-1) %>% 
      slice(1) %>%
      ungroup(),
    aes(x = 0, xend = val, y = cat, yend = cat),
    linetype = "dotted", size = 0.5, color = "gray80"
  ) +
  # this draws the underlying full segment intelligently choosing
  # the correct min/max points
  geom_segment(
    data = gather(xdf, measure, val, -cat) %>% 
      group_by(cat) %>% 
      summarise(start = range(val)[1], end = range(val)[2]) %>% 
      ungroup(),
    aes(x = start, xend = end, y = cat, yend = cat),
    color = "gray80", size = 2
  ) +
  # this draws the 
  geom_point(
    data = gather(xdf, measure, value, -cat),
    aes(value, cat, color = measure), 
    size = 4
  ) +
  # i just extended the scale a bit + put axis on top; choose aesthetics that work 
  # for you
  scale_x_comma(position = "top", limits = c(0, 10)) +
  scale_color_ipsum(name = "A real legend title") +
  labs(
    x = "Description of the value", y = NULL,
    title = "A good plot title"
  ) +
  theme_ipsum_rc(grid = "X") +
  theme(legend.position = "top")

image

Do not hesitate to ask any and all q's abt ^^. Very glad to lend some assistance.

hrbrmstr avatar Jun 06 '19 13:06 hrbrmstr

@hrbrmstr awe...some! Thanks for the effort!

pedrocr83 avatar Jun 06 '19 14:06 pedrocr83

Well met, @pedrocr83!

This is a super interesting request (i.e. making this a more generic function). Lemme ponder a bit what said API would look like.

In the interim, here's an example for how to build this with with three points (it works for n points, really) "manually"

library(hrbrthemes)
library(tidyverse)

tibble(
  val1 = c(3, 2, 4),
  val2 = c(1, 4, 5),
  val3 = c(5, 8, 6),
  cat = factor(month.name[1:3], levels = rev(month.name[1:3]))
) -> xdf

ggplot() +
  # this draws leading eye-tracker dotted line from the category name
  # to the first point (helps readers follow the values to the categories)
  geom_segment(
    data = gather(xdf, measure, val, -cat) %>% 
      group_by(cat) %>% 
      top_n(-1) %>% 
      slice(1) %>%
      ungroup(),
    aes(x = 0, xend = val, y = cat, yend = cat),
    linetype = "dotted", size = 0.5, color = "gray80"
  ) +
  # this draws the underlying full segment intelligently choosing
  # the correct min/max points
  geom_segment(
    data = gather(xdf, measure, val, -cat) %>% 
      group_by(cat) %>% 
      summarise(start = range(val)[1], end = range(val)[2]) %>% 
      ungroup(),
    aes(x = start, xend = end, y = cat, yend = cat),
    color = "gray80", size = 2
  ) +
  # this draws the 
  geom_point(
    data = gather(xdf, measure, value, -cat),
    aes(value, cat, color = measure), 
    size = 4
  ) +
  # i just extended the scale a bit + put axis on top; choose aesthetics that work 
  # for you
  scale_x_comma(position = "top", limits = c(0, 10)) +
  scale_color_ipsum(name = "A real legend title") +
  labs(
    x = "Description of the value", y = NULL,
    title = "A good plot title"
  ) +
  theme_ipsum_rc(grid = "X") +
  theme(legend.position = "top")

image

Do not hesitate to ask any and all q's abt ^^. Very glad to lend some assistance.

Hello @hrbrmstr, thanks for your code! I am trying to use the same idea where I have 3 values on 1 dumbbell. However, when I tried to run your exact code, I run into error. Below I copy the error message generated by R, and would greatly appreciate your help in pointing out where I could go and fix:

Selecting by val Error: Aesthetics must be valid data columns. Problematic aesthetic(s): y = cat, yend = cat. Did you mistype the name of a data column or forget to add after_stat()? Run rlang::last_error() to see where the error occurred.

After running rlang::last_error() :

<error/rlang_error> Aesthetics must be valid data columns. Problematic aesthetic(s): y = cat, yend = cat. Did you mistype the name of a data column or forget to add after_stat()? Backtrace:

  1. (function (x, ...) ...
  2. ggplot2:::print.ggplot(x)
  3. ggplot2:::ggplot_build.ggplot(x)
  4. ggplot2:::by_layer(function(l, d) l$compute_aesthetics(d, plot))
  5. ggplot2:::f(l = layers[[i]], d = data[[i]])
  6. l$compute_aesthetics(d, plot)
  7. ggplot2:::f(..., self = self) Run rlang::last_trace() to see the full context.

My R version (if it is relevant)

version _
platform x86_64-w64-mingw32
arch x86_64
os mingw32
system x86_64, mingw32
status
major 4
minor 0.0
year 2020
month 04
day 24
svn rev 78286
language R
version.string R version 4.0.0 (2020-04-24) nickname Arbor Day

Much appreciated for your help in advance!

Elly

ellyyuyang avatar Dec 16 '20 14:12 ellyyuyang

@ellyyuyang without the tibble its kinda of hard to debug but i'll give my example after that awesome piece of code @hrbrmstr gave me:

d <- tibble(normal = value1 , notnormal = value2 , sample = value3 , metric = factor(stringvalue))

getComparisonPlot<-function(d) { plot <- ggplot() + geom_segment(data = gather(d, measure, val, -metric) %>% group_by(metric) %>% summarise(start = 0, end = range(val)[2]) %>% ungroup(), aes(x = start, xend = end, y = 0, yend = 0), color = '#DCDDDA', size = 10, lineend = "round") + geom_segment(data = gather(d, measure, val, -metric) %>% group_by(metric) %>% summarise(start = range(val)[1], end = 0) %>% ungroup(), aes(x = start, xend = end, y = 0, yend = 0), color = '#7DB500', size = 10, lineend = "round") return(plot) }

pedrocr83 avatar Dec 16 '20 22:12 pedrocr83

@ellyyuyang without the tibble its kinda of hard to debug but i'll give my example after that awesome piece of code @hrbrmstr gave me:

d <- tibble(normal = value1 , notnormal = value2 , sample = value3 , metric = factor(stringvalue))

getComparisonPlot<-function(d) { plot <- ggplot() + geom_segment(data = gather(d, measure, val, -metric) %>% group_by(metric) %>% summarise(start = 0, end = range(val)[2]) %>% ungroup(), aes(x = start, xend = end, y = 0, yend = 0), color = '#DCDDDA', size = 10, lineend = "round") + geom_segment(data = gather(d, measure, val, -metric) %>% group_by(metric) %>% summarise(start = range(val)[1], end = 0) %>% ungroup(), aes(x = start, xend = end, y = 0, yend = 0), color = '#7DB500', size = 10, lineend = "round") return(plot) }

Hello @pedrocr83 , appreciate your help! It may be due to something iffy happened to my R studio. My issue has been resolved after restarting R studio. Cheers! @hrbrmstr thanks for your code again!

ellyyuyang avatar Dec 17 '20 01:12 ellyyuyang

@ellyyuyang without the tibble its kinda of hard to debug but i'll give my example after that awesome piece of code @hrbrmstr gave me:

d <- tibble(normal = value1 , notnormal = value2 , sample = value3 , metric = factor(stringvalue))

getComparisonPlot<-function(d) { plot <- ggplot() + geom_segment(data = gather(d, measure, val, -metric) %>% group_by(metric) %>% summarise(start = 0, end = range(val)[2]) %>% ungroup(), aes(x = start, xend = end, y = 0, yend = 0), color = '#DCDDDA', size = 10, lineend = "round") + geom_segment(data = gather(d, measure, val, -metric) %>% group_by(metric) %>% summarise(start = range(val)[1], end = 0) %>% ungroup(), aes(x = start, xend = end, y = 0, yend = 0), color = '#7DB500', size = 10, lineend = "round") return(plot) }

@pedrocr83 I was trying to play around with the code and explore the possibly of reordering the dumbbells according to one of the variables but failed, would like to see if you would have any insights to it! Much appreciated!

So I tried to reorder the order of months (in @hrbrmstr 's original code) by the val3. so I added the following code before ggplot

xdf %>% mutate(cat = fct_reorder(cat, desc(val3))) %>%

But this does not perform the reordering of the dumbbells. Do you have any thoughts on this?

Best, Elly

ellyyuyang avatar Dec 17 '20 03:12 ellyyuyang

@ellyyuyang without the tibble its kinda of hard to debug but i'll give my example after that awesome piece of code @hrbrmstr gave me: d <- tibble(normal = value1 , notnormal = value2 , sample = value3 , metric = factor(stringvalue)) getComparisonPlot<-function(d) { plot <- ggplot() + geom_segment(data = gather(d, measure, val, -metric) %>% group_by(metric) %>% summarise(start = 0, end = range(val)[2]) %>% ungroup(), aes(x = start, xend = end, y = 0, yend = 0), color = '#DCDDDA', size = 10, lineend = "round") + geom_segment(data = gather(d, measure, val, -metric) %>% group_by(metric) %>% summarise(start = range(val)[1], end = 0) %>% ungroup(), aes(x = start, xend = end, y = 0, yend = 0), color = '#7DB500', size = 10, lineend = "round") return(plot) }

@pedrocr83 I was trying to play around with the code and explore the possibly of reordering the dumbbells according to one of the variables but failed, would like to see if you would have any insights to it! Much appreciated!

So I tried to reorder the order of months (in @hrbrmstr 's original code) by the val3. so I added the following code before ggplot

xdf %>% mutate(cat = fct_reorder(cat, desc(val3))) %>%

But this does not perform the reordering of the dumbbells. Do you have any thoughts on this?

Best, Elly

Sorry @ellyyuyang are you still having this issue? somehow i missed the notification

pedrocr83 avatar Apr 01 '21 12:04 pedrocr83