I am unable to pass other color schemes into rect_border
but it somehow works when the colour palette lancet
is specified. How can I adjust the second plot to work on the smooth_rainbow
colours?
See reprex below:
library(factoextra)
library(ggplot2)
library(khroma)
df <- scale(mtcars) # Standardize the data
dist <- dist(df, method = "euclidean") # df = standardized data
hc <- hclust(dist, method = "ward.D2")
p <- fviz_dend(hc, k = 4, # Cut in four groups
cex = 0.6, # label size
k_colors = "lancet",
color_labels_by_k = TRUE, # color labels by groups
rect = TRUE, # Add rectangle around groups
rect_border = "lancet",
rect_fill = TRUE,
rotate = TRUE) +
theme_dark()
#> Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
#> "none")` instead.
p$layers[[1]]$data$col[p$layers[[1]]$data$col == "black"] <- "white"
p$layers[[2]]$data$angle <- 0
p
smooth_rainbow <- khroma::colour("smooth rainbow")
p2 <-
fviz_dend(hc, k = 4, # Cut in four groups
cex = 0.6, # label size
k_colors = smooth_rainbow(n = 4),
color_labels_by_k = TRUE, # color labels by groups
rect = TRUE, # Add rectangle around groups
rect_border = smooth_rainbow(n = 4),
rect_fill = TRUE,
rotate = TRUE) +
ggplot2::theme_dark()
#> Warning: `guides(<scale> = FALSE)` is deprecated. Please use `guides(<scale> =
#> "none")` instead.
#> Error in if (color == "cluster") color <- "default": the condition has length > 1
p2
#> Error in eval(expr, envir, enclos): object 'p2' not found
Created on 2023-05-07 by the reprex package (v2.0.1)
> sessionInfo()
R version 4.2.3 (2023-03-15 ucrt)
Platform: x86_64-w64-mingw32/x64 (64-bit)
Running under: Windows 10 x64 (build 19044)
Matrix products: default
locale:
[1] LC_COLLATE=English_South Africa.utf8 LC_CTYPE=English_South Africa.utf8
[3] LC_MONETARY=English_South Africa.utf8 LC_NUMERIC=C
[5] LC_TIME=English_South Africa.utf8
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] khroma_1.10.0 cluster_2.1.4 MTGmeta_0.0.0.9000 factoextra_1.0.7
[5] magrittr_2.0.3 here_1.0.1 forcats_0.5.1 stringr_1.4.0
[9] dplyr_1.0.9 purrr_0.3.5 readr_2.1.2 tidyr_1.2.0
[13] tibble_3.1.8 ggplot2_3.3.6 tidyverse_1.3.2
This is a bug and it would be great if you could report it to the package maintainer. The error originates from a conditional statement in factoextra:::.rect_dendrogram
, which tests if your color argument is color == "cluster"
. This only works if your color argument is a vector of length 1. (e.g., passing a palette name works, as you have demonstrated).
When passing a vector of colors, this naturally fails, as R doesn't like to compare vectors of length > 1 with a ==
. If you replace the conditional statement, e.g. with all(color == "cluster")
, it works.
NB I have copied the adjusted functions for your convenience - there are a few uncommented modifications, in particular adding required factoextra:::
to some un-exported functions.
library(factoextra)
library(ggplot2)
library(khroma)
##adjusted functions
fviz_dend2 <-
function (x, k = NULL, h = NULL, k_colors = NULL, palette = NULL,
show_labels = TRUE, color_labels_by_k = TRUE, label_cols = NULL,
labels_track_height = NULL, repel = FALSE, lwd = 0.7, type = c("rectangle",
"circular", "phylogenic"), phylo_layout = "layout.auto",
rect = FALSE, rect_border = "gray", rect_lty = 2, rect_fill = FALSE,
lower_rect, horiz = FALSE, cex = 0.8, main = "Cluster Dendrogram",
xlab = "", ylab = "Height", sub = NULL, ggtheme = theme_classic(),
...) {
if (missing(k_colors) & !is.null(palette)) {
k_colors <- palette
palette <- NULL
}
if (!color_labels_by_k & is.null(label_cols))
label_cols <- "black"
type <- match.arg(type)
circular <- type == "circular"
phylogenic <- type == "phylogenic"
rectangle <- type == "rectangle"
if (inherits(x, "HCPC")) {
k <- length(unique(x$data.clust$clust))
x <- x$call$t$tree
}
if (inherits(x, "hcut")) {
k <- x$nbclust
dend <- as.dendrogram(x)
method <- x$method
}
else if (inherits(x, "hkmeans")) {
k <- length(unique(x$cluster))
dend <- as.dendrogram(x$hclust)
method <- x$hclust$method
}
else if (inherits(x, c("hclust", "agnes", "diana"))) {
dend <- as.dendrogram(x)
method <- x$method
}
else if (inherits(x, "dendrogram")) {
dend <- x
method <- ""
}
else stop("Can't handle an object of class ", paste(class(x),
collapse = ", "))
if (is.null(method))
method <- ""
else if (is.na(method))
method <- ""
if (is.null(sub) & method != "")
sub = paste0("Method: ", method)
if (!is.null(dendextend::labels_cex(dend)))
cex <- dendextend::labels_cex(dend)
dend <- dendextend::set(dend, "labels_cex", cex)
dend <- dendextend::set(dend, "branches_lwd", lwd)
k <- factoextra:::.get_k(dend, k, h)
if (!is.null(k)) {
if (ggpubr:::.is_col_palette(k_colors))
k_colors <- ggpubr:::.get_pal(k_colors, k = k)
else if (is.null(k_colors))
k_colors <- ggpubr:::.get_pal("default", k = k)
dend <- dendextend::set(dend, what = "branches_k_color",
k = k, value = k_colors)
if (color_labels_by_k)
dend <- dendextend::set(dend, "labels_col", k = k,
value = k_colors)
}
if (!is.null(label_cols)) {
dend <- dendextend::set(dend, "labels_col", label_cols)
}
leaflab <- ifelse(show_labels, "perpendicular", "none")
if (xlab == "")
xlab <- NULL
if (ylab == "")
ylab <- NULL
max_height <- max(dendextend::get_branches_heights(dend))
if (missing(labels_track_height))
labels_track_height <- max_height/8
if (max_height < 1)
offset_labels <- -max_height/100
else offset_labels <- -0.1
if (rectangle | circular) {
p <- factoextra:::.ggplot_dend(dend, type = "rectangle", offset_labels = offset_labels,
nodes = FALSE, ggtheme = ggtheme, horiz = horiz,
circular = circular, palette = palette, labels = show_labels,
label_cols = label_cols, labels_track_height = labels_track_height,
...)
if (!circular)
p <- p + labs(title = main, x = xlab, y = ylab)
}
else if (phylogenic) {
p <- .phylogenic_tree(dend, labels = show_labels, label_cols = label_cols,
palette = palette, repel = repel, ggtheme = ggtheme,
phylo_layout = phylo_layout, ...)
}
if (circular | phylogenic | is.null(k))
rect <- FALSE
if (rect_fill & missing(rect_lty))
rect_lty = "blank"
if (missing(lower_rect))
lower_rect = -(labels_track_height + 0.5)
if (rect) {
p <- p + rect_dendrogram(dend, k = k, palette = rect_border,
rect_fill = rect_fill, rect_lty = rect_lty, size = lwd,
lower_rect = lower_rect)
}
attr(p, "dendrogram") <- dend
structure(p, class = c(class(p), "fviz_dend"))
return(p)
}
rect_dendrogram <- function(dend, k = NULL, h = NULL, k_colors = NULL, palette = NULL,
rect_fill = FALSE, rect_lty = 2, lower_rect = -1.5, ...) {
if (missing(k_colors) & !is.null(palette))
k_colors <- palette
prop_k_height <- 0.5
if (!dendextend::is.dendrogram(dend))
stop("x is not a dendrogram object.")
k <- factoextra:::.get_k(dend, k, h)
tree_heights <- dendextend::heights_per_k.dendrogram(dend)[-1]
tree_order <- stats::order.dendrogram(dend)
if (is.null(k))
stop("specify k")
if (k < 2) {
stop(gettextf("k must be between 2 and %d", length(tree_heights)),
domain = NA)
}
cluster <- dendextend::cutree(dend, k = k)
clustab <- table(cluster)[unique(cluster[tree_order])]
m <- c(0, cumsum(clustab))
which <- 1L:k
xleft <- ybottom <- xright <- ytop <- list()
for (n in seq_along(which)) {
next_k_height <- tree_heights[names(tree_heights) ==
k + 1]
if (length(next_k_height) == 0) {
next_k_height <- 0
prop_k_height <- 1
}
xleft[[n]] = m[which[n]] + 0.66
ybottom[[n]] = lower_rect
xright[[n]] = m[which[n] + 1] + 0.33
ytop[[n]] <- tree_heights[names(tree_heights) == k] *
prop_k_height + next_k_height * (1 - prop_k_height)
}
df <- data.frame(xmin = unlist(xleft), ymin = unlist(ybottom),
xmax = unlist(xright), ymax = unlist(ytop), stringsAsFactors = TRUE)
color <- k_colors
if (all(color == "cluster"))
color <- "default"
if (ggpubr:::.is_col_palette(color))
color <- ggpubr:::.get_pal(color, k = k)
else if (length(color) > 1 & length(color) < k) {
color <- rep(color, k)[1:k]
}
if (rect_fill) {
fill <- color
alpha <- 0.2
}
else {
fill <- "transparent"
alpha <- 0
}
df$color <- color
df$cluster <- as.factor(paste0("c", 1:k))
ggpubr::geom_exec(geom_rect, data = df, xmin = "xmin", ymin = "ymin",
xmax = "xmax", ymax = "ymax", fill = fill, color = color,
linetype = rect_lty, alpha = alpha, ...)
}
fviz_dend2(hc, k = 4, # Cut in four groups
cex = 0.6, # label size
k_colors = smooth_rainbow(4),
# color_labels_by_k = TRUE, # color labels by groups
rect = TRUE, # Add rectangle around groups
rect_border = smooth_rainbow(4),
rect_fill = TRUE,
rotate = TRUE) +
ggplot2::theme_dark()
#> Warning: The `<scale>` argument of `guides()` cannot be `FALSE`. Use "none" instead as
#> of ggplot2 3.3.4.
#> ℹ The deprecated feature was likely used in the factoextra package.
#> Please report the issue at <https://github.com/kassambara/factoextra/issues>.
Created on 2023-05-07 with reprex v2.0.2