Skip to content

Updates to better support dendrograms #818

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open

Conversation

jrowen
Copy link

@jrowen jrowen commented Nov 30, 2016

These changes were made to better support dendrograms, particularly those created with dendextend. It adds support for

  • fontface in geom_text
  • a rough translation of hjust and vjust in geom_text
  • -Inf and Inf in geom_rect
  • guide = "none"

Feedback is very welcome. Below is some sample code.

library(ggplot2)
library(plotly)
library(dendextend)

dend <- iris[1:30,-5] %>% dist %>% hclust %>% as.dendrogram %>%
  set("branches_k_color", k=3) %>% set("branches_lwd", c(1.5,1,1.5)) %>%
  set("branches_lty", c(1,1,3,1,1,2)) %>%
  set("labels_colors") %>% set("labels_cex", c(.9,1.2)) %>% 
  set("nodes_pch", 19) %>% set("nodes_col", c("orange", "black", "plum", NA))
ggd1 <- as.ggdend(dend)
p = ggplot(ggd1, horiz = TRUE, theme = NULL)
ggplotly(p)

p1 = ggplot(cars) +
  geom_point(aes(x = speed, y = dist)) +
  geom_rect(xmin = 5, xmax = 10, ymin = -Inf, ymax = Inf)
ggplotly(p1)

p2 = ggplot(cars) +
  geom_point(aes(x = speed, y = dist)) +
  geom_rect(ymin = 25, ymax = 50, xmin = -Inf, xmax = Inf)
ggplotly(p2)

p3 = ggplot(cars) +
  geom_point(aes(x = speed, y = dist)) +
  geom_rect(xmin = 5, xmax = 10, ymin = -Inf, ymax = Inf) +
  coord_flip()
ggplotly(p3)

p4 = ggplot(cars) +
  geom_point(aes(x = speed, y = dist)) +
  geom_rect(ymin = 25, ymax = 50, xmin = -Inf, xmax = Inf) +
  coord_flip()
ggplotly(p4)

p5 = ggplot(data.frame(x = seq(5, 25, 5), y = 60)) +
  geom_point(aes(x = x, y = y)) +
  geom_text(x = 5, y = 60, label = "nothing") +
  geom_text(x = 10, y = 60, label = "bold", fontface = "bold", hjust = 0, vjust = 0) +
  geom_text(x = 15, y = 60, label = "italic", fontface = "italic", hjust = 1, vjust = 1) +
  geom_text(x = 20, y = 60, label = "bold italic", fontface = "bold italic", hjust = 0, vjust = 1) +
  geom_text(x = 25, y = 60, label = "plain", fontface = "plain", hjust = 1, vjust = 0)
ggplotly(p5)

grepl("italic", data[["fontface"]]),
paste0("<i>", text, "</i>"),
text
)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer if this was done with if() instead of ifelse()

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was using ifelse as I was anticipating data[["fontface"]] could be a vector (I think it can be specified via aes). Is this an incorrect assumption?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oops, yea, but in that case I prefer something like:

text[grepl("italic", data[["fontface"]])]  <- paste0("<i>", text, "</i>")

cbind(x = ifelse(xmax == Inf, layout$x_max, xmax),
y = ifelse(ymin == -Inf, layout$y_min, ymin), others))
})
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a lot of redundant code...

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, I've simplified things a bit.

R/ggplotly.R Outdated
@@ -702,6 +702,7 @@ gg2list <- function(p, width = NULL, height = NULL, tooltip = "all",
)

# if theme(legend.position = "none") is used, don't show a legend _or_ guide
npscales$scales <- Filter(function(x) x$guide != "none", npscales$scales)
if (npscales$n() == 0 || identical(theme$legend.position, "none")) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see this as potentially dangerous -- a scale without a guide doesn't necessarily mean it doesn't exist.

I would prefer to instead set layout.showlegend to FALSE for discrete scales and remove the colorbar for continuous ones.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about instead handling the discrete scale by updating this line to something like

sc$is_discrete() && sc$guide != "none"

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That won't work either for similar reasons

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made one more attempt, hopefully we're close. Thanks.

@cpsievert
Copy link
Collaborator

Sorry for the delay @jrowen, this is looking pretty good...before I merge, would you mind writing a couple simple tests?

@jrowen
Copy link
Author

jrowen commented Feb 20, 2017

Sorry, I too have been away from this for a while. I'll see if I can pull those together shortly. Thanks.

@talgalili
Copy link
Contributor

Hi @jrowen
Any chance you could get some tests made for this?
This would be helpful for heatmaply (since it now has a plot_method="plotly" which could use these features).

Thanks!

@jrowen
Copy link
Author

jrowen commented Apr 12, 2017

I added some checks for geom_text and geom_rect, but I'm not sure how to test the guide = "none" change without introducing a dependency on dendextend (see code below which now works as expected). It also looks like the R:devel build is failing with the R:release build is fine.

library(ggplot2)
library(plotly)
library(dendextend)

dend <- iris[1:30,-5] %>% dist %>% hclust %>% as.dendrogram %>%
  set("branches_k_color", k=3) %>% set("branches_lwd", c(1.5,1,1.5)) %>%
  set("branches_lty", c(1,1,3,1,1,2)) %>%
  set("labels_colors") %>% set("labels_cex", c(.9,1.2)) %>% 
  set("nodes_pch", 19) %>% set("nodes_col", c("orange", "black", "plum", NA))
ggd1 <- as.ggdend(dend)
p = ggplot(ggd1, horiz = TRUE, theme = NULL)
ggplotly(p)

@cpsievert
Copy link
Collaborator

cpsievert commented Apr 13, 2017

Don't worry about the build failure @jrowen. I'll have a closer look here next week

@talgalili
Copy link
Contributor

Hi Carson,
Any chance you can give this a look?

Thanks,
Tal

@cpsievert
Copy link
Collaborator

I may get to this next week. #929 will take precedence.

@cpsievert cpsievert added this to the ggplotly parity milestone May 11, 2017
@cpsievert
Copy link
Collaborator

cpsievert commented May 29, 2018

Hi @jrowen, sorry for forgetting about this PR! I thought I was going to have the bandwidth to complete #929, but I'm not sure anymore...I have a couple small questions about this PR, but we should try to get this merged before I submit to CRAN by the end of June

textposition = paste0(
ifelse(data[["vjust"]] < 0.5, "top ",
ifelse(data[["vjust"]] > 0.5, "bottom ", "")
),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't the trailing whitespace be removed here (e.g., "top" not "top ")?

Copy link
Collaborator

@cpsievert cpsievert May 29, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh, nevermind. I see why it's done this why

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should default to "middle" (not "") though

cbind(x = ifelse(xmax == Inf, x_max, xmax),
y = ifelse(ymax == Inf, y_max, ymax), others),
cbind(x = ifelse(xmax == Inf, x_max, xmax),
y = ifelse(ymin == -Inf, y_min, ymin), others))
})
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps it's better (more efficient) to use is.finite() here?

@@ -139,3 +139,36 @@ test_that('Specifying alpha in hex color code works', {
expect_match(info$data[[1]]$fillcolor, "rgba\\(0,0,0,0\\.0[6]+")
})

p1 = ggplot(data.frame(x = 1, y = 1)) +
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be great to have another test that specifying ymin = 0.5/ymax = 1.5 gives the same result

@talgalili
Copy link
Contributor

Hey @cpsievert :)
Any chance to add this support?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants