旋转 ggplot 树状图的标签
Rotate labels for ggplot dendrogram
我正在尝试使用包 dendextend 创建树状图。它创建了非常好的 gg 树状图,但不幸的是,当你把它变成 "circle" 时,标签跟不上。我将在下面提供一个例子。
我的远方对象在这里:http://speedy.sh/JRVBS/mydist.RDS
library(dendextend)
library(ggplot2)
#library(devtools) ; install_github('kassambara/factoextra')
library(factoextra)
clus <- hcut(mydist, k = 6, hc_func = 'hclust',
hc_method = 'ward.D2', graph = FALSE, isdiss = TRUE)
dend <- as.dendrogram(clus)
labels(dend) <- paste0(paste0(rep(' ', 3), collapse = ''), labels(dend))
dend <- sort(dend, decreasing = FALSE)
ggd1 <- ggplot(dend %>%
set('branches_k_color', k = 6) %>%
set('branches_lwd', 0.6) %>%
set('labels_colors', k = 6) %>%
set('labels_cex', 0.6),
theme = theme_minimal(),
horiz = TRUE)
ggd1 <- ggd1 + theme(panel.grid.major = element_blank(),
axis.text = element_blank(),
axis.title = element_blank())
ggd1 <- ggd1 + ylim(max(get_branches_heights(dend)), -3)
这基本上给了我这个图像:
这很棒。但是,我想把它变成一个圆圈,所以使用:
ggd1 + coord_polar(theta = 'x')
我得到下面这张图。这正是我想要的,但我只需要旋转标签。
感谢任何帮助。我知道在幕后,dendextend 基本上是创建一些 data.frames,然后在它们上调用 geom_segment()
和 geom_text()
来创建树状图和标签。我相信我可以公开相关的 data.frame 如下:
back.df1 <- dendextend::as.ggdend(dend)
back.df2 <- dendextend::prepare.ggdend(back.df1)
另一种策略可能是在绘图时使用 ggplot(labels = FALSE...)
,然后以某种方式手动添加 geom_text()
,以保留颜色但允许我使用 geom_text(angle = )
。
我还怀疑各种 ggplot 魔法的某种组合可以让我采用 back.df2
并再次创建第一个和第二个图,而且还可以控制标签的角度。但是,我不知道该怎么做,并且已经使用 dendextend 包构建了很多,并且理想情况下希望避免使用任何新包来创建树状图对象,因为我真的很喜欢标签之外的这个!
解决方案
我基于下面 Richard Telford 的解决方案。我首先创建了 ggplot.ggdend()
的编辑版本。这与下面的答案中提供的相同。接下来我创建了一个函数来自动创建 angle 和 hjust 向量,以便标签旋转从 6 点钟切换到 12 点钟以提高可读性。
createAngleHJustCols <- function(labeldf) {
nn <- length(labeldf$y)
halfn <- floor(nn/2)
firsthalf <- rev(90 + seq(0,360, length.out = nn))
secondhalf <- rev(-90 + seq(0,360, length.out = nn))
angle <- numeric(nn)
angle[1:halfn] <- firsthalf[1:halfn]
angle[(halfn+1):nn] <- secondhalf[(halfn+1):nn]
hjust <- numeric(nn)
hjust[1:halfn] <- 0
hjust[(halfn+1):nn] <- 1
return(list(angle = angle, hjust = hjust))
}
然后我使用以下代码制作了绘图:
gdend <- dendextend::as.ggdend(dend %>%
set('branches_k_color', k = 6) %>%
set('branches_lwd', 0.6) %>%
set('labels_colors', k = 6) %>%
set('labels_cex', 0.6))
gdend$labels$angle <- ifelse(horiz, 0, 90)
gdend$labels$hjust <- 0
gdend$labels$vjust <- 0.5
# if polar, change the angle and hjust so that the labels rotate
if(polarplot) {
newvalues <- createAngleHJustCols(gdend$labels)
gdend$labels$angle <- newvalues[['angle']]
gdend$labels$hjust <- newvalues[['hjust']]
}
ggresult <- newggplot.ggdend(gdend, horiz = TRUE, offset_labels = -2)
ggresult <- ggresult + ggtitle(plottitle)
ggresult <- ggresult + theme(plot.margin = margin(c(2,2,2,2),
axis.text = element_blank(),
plot.title = element_text(margin = margin(10,2,2,2)))
ggresult <- ggresult + ylim(max(get_branches_heights(dend)), -5)
ggresult <- ggresult + coord_polar(theta = 'x', direction = 1)
这最终产生了最后的情节!
(我更改了数据中的一些内容,因此图中的某些顺序可能会有所不同)
这是可能的,但您需要先编辑 dendextend:::ggplot.ggdend
以使其接受 angle
审美(以及 hjust
和 vjust
)
第 1 步:编辑 dendextend:::ggplot.ggdend
newggplot.ggdend <- function (data, segments = TRUE, labels = TRUE, nodes = TRUE,
horiz = FALSE, theme = theme_dendro(), offset_labels = 0, ...) {
data <- prepare.ggdend(data)
#angle <- ifelse(horiz, 0, 90)
#hjust <- ifelse(horiz, 0, 1)
p <- ggplot()
if (segments) {
p <- p + geom_segment(data = data$segments, aes_string(x = "x", y = "y", xend = "xend", yend = "yend", colour = "col", linetype = "lty", size = "lwd"), lineend = "square") +
guides(linetype = FALSE, col = FALSE) + scale_colour_identity() +
scale_size_identity() + scale_linetype_identity()
}
if (nodes) {
p <- p + geom_point(data = data$nodes, aes_string(x = "x", y = "y", colour = "col", shape = "pch", size = "cex")) +
guides(shape = FALSE, col = FALSE, size = FALSE) +
scale_shape_identity()
}
if (labels) {
data$labels$cex <- 5 * data$labels$cex
data$labels$y <- data$labels$y + offset_labels
p <- p + geom_text(data = data$labels, aes_string(x = "x", y = "y", label = "label", colour = "col", size = "cex", angle = "angle", hjust = "hjust", vjust = "vjust"))#edited
}
if (horiz) {
p <- p + coord_flip() + scale_y_reverse(expand = c(0.2, 0))
}
if (!is.null(theme)) {
p <- p + theme
}
p
}
assignInNamespace(x = "ggplot.ggdend", ns = "dendextend", value = newggplot.ggdend)
第 2 步:制作数据对象
gdend <- dendextend::as.ggdend(dend %>%
set('branches_k_color', k = 6) %>%
set('branches_lwd', 0.6) %>%
set('labels_colors', k = 6) %>%
set('labels_cex', 0.6),
theme = theme_minimal(),
horiz = TRUE)
gdend$labels$angle <- seq(90, -270, length = nrow(gdend$labels))
gdend$labels$vjust <- cos(gdend$labels$angle * pi) / (180)
gdend$labels$hjust <- sin(gdend$labels$angle * pi) / (180)
第 3 步:绘图
ggd1 <- ggplot(gdend)
ggd1 <- ggd1 + theme(panel.grid.major = element_blank(),
axis.text = element_blank(),
axis.title = element_blank())
ggd1 <- ggd1 + ylim(max(get_branches_heights(dend)), -3)
ggd1
ggd1 + coord_polar(theta = 'x')
我正在尝试使用包 dendextend 创建树状图。它创建了非常好的 gg 树状图,但不幸的是,当你把它变成 "circle" 时,标签跟不上。我将在下面提供一个例子。
我的远方对象在这里:http://speedy.sh/JRVBS/mydist.RDS
library(dendextend)
library(ggplot2)
#library(devtools) ; install_github('kassambara/factoextra')
library(factoextra)
clus <- hcut(mydist, k = 6, hc_func = 'hclust',
hc_method = 'ward.D2', graph = FALSE, isdiss = TRUE)
dend <- as.dendrogram(clus)
labels(dend) <- paste0(paste0(rep(' ', 3), collapse = ''), labels(dend))
dend <- sort(dend, decreasing = FALSE)
ggd1 <- ggplot(dend %>%
set('branches_k_color', k = 6) %>%
set('branches_lwd', 0.6) %>%
set('labels_colors', k = 6) %>%
set('labels_cex', 0.6),
theme = theme_minimal(),
horiz = TRUE)
ggd1 <- ggd1 + theme(panel.grid.major = element_blank(),
axis.text = element_blank(),
axis.title = element_blank())
ggd1 <- ggd1 + ylim(max(get_branches_heights(dend)), -3)
这基本上给了我这个图像:
ggd1 + coord_polar(theta = 'x')
我得到下面这张图。这正是我想要的,但我只需要旋转标签。
感谢任何帮助。我知道在幕后,dendextend 基本上是创建一些 data.frames,然后在它们上调用 geom_segment()
和 geom_text()
来创建树状图和标签。我相信我可以公开相关的 data.frame 如下:
back.df1 <- dendextend::as.ggdend(dend)
back.df2 <- dendextend::prepare.ggdend(back.df1)
另一种策略可能是在绘图时使用 ggplot(labels = FALSE...)
,然后以某种方式手动添加 geom_text()
,以保留颜色但允许我使用 geom_text(angle = )
。
我还怀疑各种 ggplot 魔法的某种组合可以让我采用 back.df2
并再次创建第一个和第二个图,而且还可以控制标签的角度。但是,我不知道该怎么做,并且已经使用 dendextend 包构建了很多,并且理想情况下希望避免使用任何新包来创建树状图对象,因为我真的很喜欢标签之外的这个!
解决方案
我基于下面 Richard Telford 的解决方案。我首先创建了 ggplot.ggdend()
的编辑版本。这与下面的答案中提供的相同。接下来我创建了一个函数来自动创建 angle 和 hjust 向量,以便标签旋转从 6 点钟切换到 12 点钟以提高可读性。
createAngleHJustCols <- function(labeldf) {
nn <- length(labeldf$y)
halfn <- floor(nn/2)
firsthalf <- rev(90 + seq(0,360, length.out = nn))
secondhalf <- rev(-90 + seq(0,360, length.out = nn))
angle <- numeric(nn)
angle[1:halfn] <- firsthalf[1:halfn]
angle[(halfn+1):nn] <- secondhalf[(halfn+1):nn]
hjust <- numeric(nn)
hjust[1:halfn] <- 0
hjust[(halfn+1):nn] <- 1
return(list(angle = angle, hjust = hjust))
}
然后我使用以下代码制作了绘图:
gdend <- dendextend::as.ggdend(dend %>%
set('branches_k_color', k = 6) %>%
set('branches_lwd', 0.6) %>%
set('labels_colors', k = 6) %>%
set('labels_cex', 0.6))
gdend$labels$angle <- ifelse(horiz, 0, 90)
gdend$labels$hjust <- 0
gdend$labels$vjust <- 0.5
# if polar, change the angle and hjust so that the labels rotate
if(polarplot) {
newvalues <- createAngleHJustCols(gdend$labels)
gdend$labels$angle <- newvalues[['angle']]
gdend$labels$hjust <- newvalues[['hjust']]
}
ggresult <- newggplot.ggdend(gdend, horiz = TRUE, offset_labels = -2)
ggresult <- ggresult + ggtitle(plottitle)
ggresult <- ggresult + theme(plot.margin = margin(c(2,2,2,2),
axis.text = element_blank(),
plot.title = element_text(margin = margin(10,2,2,2)))
ggresult <- ggresult + ylim(max(get_branches_heights(dend)), -5)
ggresult <- ggresult + coord_polar(theta = 'x', direction = 1)
这最终产生了最后的情节!
(我更改了数据中的一些内容,因此图中的某些顺序可能会有所不同)
这是可能的,但您需要先编辑 dendextend:::ggplot.ggdend
以使其接受 angle
审美(以及 hjust
和 vjust
)
第 1 步:编辑 dendextend:::ggplot.ggdend
newggplot.ggdend <- function (data, segments = TRUE, labels = TRUE, nodes = TRUE,
horiz = FALSE, theme = theme_dendro(), offset_labels = 0, ...) {
data <- prepare.ggdend(data)
#angle <- ifelse(horiz, 0, 90)
#hjust <- ifelse(horiz, 0, 1)
p <- ggplot()
if (segments) {
p <- p + geom_segment(data = data$segments, aes_string(x = "x", y = "y", xend = "xend", yend = "yend", colour = "col", linetype = "lty", size = "lwd"), lineend = "square") +
guides(linetype = FALSE, col = FALSE) + scale_colour_identity() +
scale_size_identity() + scale_linetype_identity()
}
if (nodes) {
p <- p + geom_point(data = data$nodes, aes_string(x = "x", y = "y", colour = "col", shape = "pch", size = "cex")) +
guides(shape = FALSE, col = FALSE, size = FALSE) +
scale_shape_identity()
}
if (labels) {
data$labels$cex <- 5 * data$labels$cex
data$labels$y <- data$labels$y + offset_labels
p <- p + geom_text(data = data$labels, aes_string(x = "x", y = "y", label = "label", colour = "col", size = "cex", angle = "angle", hjust = "hjust", vjust = "vjust"))#edited
}
if (horiz) {
p <- p + coord_flip() + scale_y_reverse(expand = c(0.2, 0))
}
if (!is.null(theme)) {
p <- p + theme
}
p
}
assignInNamespace(x = "ggplot.ggdend", ns = "dendextend", value = newggplot.ggdend)
第 2 步:制作数据对象
gdend <- dendextend::as.ggdend(dend %>%
set('branches_k_color', k = 6) %>%
set('branches_lwd', 0.6) %>%
set('labels_colors', k = 6) %>%
set('labels_cex', 0.6),
theme = theme_minimal(),
horiz = TRUE)
gdend$labels$angle <- seq(90, -270, length = nrow(gdend$labels))
gdend$labels$vjust <- cos(gdend$labels$angle * pi) / (180)
gdend$labels$hjust <- sin(gdend$labels$angle * pi) / (180)
第 3 步:绘图
ggd1 <- ggplot(gdend)
ggd1 <- ggd1 + theme(panel.grid.major = element_blank(),
axis.text = element_blank(),
axis.title = element_blank())
ggd1 <- ggd1 + ylim(max(get_branches_heights(dend)), -3)
ggd1
ggd1 + coord_polar(theta = 'x')