hrbrmstr / ggalt

:earth_americas: Extra Coordinate Systems, Geoms, Statistical Transformations & Scales for 'ggplot2'
https://cran.r-project.org/web/packages/ggalt/vignettes/ggalt_examples.html
Other
654 stars 99 forks source link

dumbbell three X arguments #59

Open pedrocr83 opened 5 years ago

pedrocr83 commented 5 years ago

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 commented 5 years ago

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

hrbrmstr commented 5 years ago

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.

pedrocr83 commented 5 years ago

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

ellyyuyang commented 3 years ago

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

pedrocr83 commented 3 years ago

@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) }

ellyyuyang commented 3 years ago

@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 commented 3 years ago

@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

pedrocr83 commented 3 years ago

@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