如何更简便实现树状图与分面时间序列的对齐?
时间序列聚类:树状图与时间序列形态对齐的简便实现方法
问题描述
我正在开展时间序列聚类工作,希望将树状图(dendrogram)与时间序列形态对齐,目前已通过以下代码实现了近似效果:
library(ggplot2) library(reshape2) library(stats) library(patchwork) library(ggdendro) # Added ggdendro library # ---- Example data: 4 time series ---- set.seed(1) time <- 1:50 data <- data.frame( time = time, A = cumsum(rnorm(50, 0, 1)), B = cumsum(rnorm(50, 0, 1)), C = cumsum(rnorm(50, 0.2, 1)), D = cumsum(rnorm(50, -0.2, 1)) ) # Melt to long format for ggplot df_long <- melt(data, id.vars = "time", variable.name = "series", value.name = "value") # ---- Dendrogram (right panel) ---- # Compute hierarchical clustering on series mat <- t(as.matrix(data[ , -1])) # series as rows d <- dist(mat) hc <- hclust(d) # Convert dendrogram to a ggplot-friendly format and adjust orientation # We'll create the dendrogram first so we can order the time series plots accordingly dendro_plot <- ggdendrogram(hc, rotate = TRUE, theme_dendro = FALSE) + scale_x_reverse() + # Reverse the x-axis to put leaves on the left theme_minimal() + theme(axis.text.y = element_blank(), axis.ticks.y = element_blank(), # Remove y-axis labels and ticks on dendrogram plot.margin = margin(t = 0, r = 0, b = 0, l = 0)) # Adjust margins # Reorder the data based on dendrogram order ordered_series <- hc$labels[hc$order] df_long$series <- factor(df_long$series, levels = ordered_series, ordered = TRUE) # ---- Time series plots (left panel) ---- # Create a single ggplot object with facets for each series, ordered by the dendrogram time_series_plot <- ggplot(df_long, aes(x = time, y = value)) + geom_line(linewidth = 1) + geom_hline(yintercept = 0, linetype="dashed") + facet_grid(series ~ ., switch = "y") + # Facet by series with free y-scales and y-axis on the left theme_minimal() + labs(x = "Time", y = "Value") + theme(axis.text.y = element_blank(), axis.ticks.y = element_blank(), # Remove y-axis labels and ticks on individual facets axis.title.y = element_blank(), # Remove overall y-axis title strip.text.y.left = element_text(angle = 0), # Orient facet labels horizontally on the left panel.spacing.y = unit(0.1, "lines"), # Reduce space between facets plot.margin = margin(t = 0, r = 0, b = 0, l = 0)) # Adjust margins # ---- Combine ---- time_series_plot + dendro_plot + plot_layout(widths = c(3, 1), heights = unit(1,"null")) # Use heights = unit(1,"null") to make them proportional
效果为时间序列图与右侧树状图垂直对齐,系列顺序与树状图叶子顺序一致。请问是否存在更简便的实现方法?
更简便的实现方法
方法1:用tidyverse简化数据处理流程
用dplyr+tidyr替代reshape2,整合聚类与排序步骤,代码更紧凑:
library(tidyverse) library(patchwork) library(ggdendro) # 生成示例数据 set.seed(1) data <- tibble(time = 1:50) %>% mutate( A = cumsum(rnorm(50, 0, 1)), B = cumsum(rnorm(50, 0, 1)), C = cumsum(rnorm(50, 0.2, 1)), D = cumsum(rnorm(50, -0.2, 1)) ) # 一步完成聚类与排序提取 hc <- data %>% select(-time) %>% t() %>% dist() %>% hclust() ordered_series <- hc$labels[hc$order] # 时间序列图(自动按聚类顺序分面) ts_plot <- data %>% pivot_longer(-time, names_to = "series", values_to = "value") %>% mutate(series = factor(series, levels = ordered_series)) %>% ggplot(aes(time, value)) + geom_line(linewidth = 1) + geom_hline(yintercept = 0, linetype = "dashed") + facet_grid(series ~ ., switch = "y") + theme_minimal() + labs(x = "Time") + theme( axis.text.y = element_blank(), axis.ticks.y = element_blank(), axis.title.y = element_blank(), strip.text.y.left = element_text(angle = 0), panel.spacing.y = unit(0.1, "lines"), plot.margin = margin(0, 0, 0, 0) ) # 树状图 dendro_plot <- ggdendrogram(hc, rotate = TRUE, theme_dendro = FALSE) + scale_x_reverse() + theme_minimal() + theme( axis.text.y = element_blank(), axis.ticks.y = element_blank(), plot.margin = margin(0, 0, 0, 0) ) # 组合图 ts_plot + dendro_plot + plot_layout(widths = c(3, 1))
方法2:用tsclust包一键生成对齐图
tsclust是时间序列聚类专用包,内置对齐可视化函数,无需手动处理布局:
library(tsclust) # 转换数据为时间序列对象 ts_data <- data %>% select(-time) %>% as.matrix() %>% t() %>% tslist() # 层次聚类 hc_ts <- tsclust(ts_data, type = "hierarchical", distance = "Euclidean") # 直接绘制时间序列+树状图对齐图 plot(hc_ts, type = "sc")
该方法无需手动分面和组合,代码量大幅减少,且对齐精度更高。
方法3:用ggside在同一图中嵌入树状图
ggside可在ggplot侧边添加辅助图,无需额外组合步骤:
library(ggside) # 准备排序后的长格式数据 df_long <- data %>% pivot_longer(-time, names_to = "series", values_to = "value") %>% mutate(series = factor(series, levels = ordered_series)) # 提取树状图数据 dendro_data <- ggdendro::dendro_data(hc, type = "rectangle") # 主图+右侧树状图 ggplot(df_long, aes(time, value)) + geom_line(linewidth = 1) + geom_hline(yintercept = 0, linetype = "dashed") + facet_grid(series ~ ., switch = "y") + theme_minimal() + labs(x = "Time") + theme( axis.text.y = element_blank(), axis.ticks.y = element_blank(), axis.title.y = element_blank(), strip.text.y.left = element_text(angle = 0), panel.spacing.y = unit(0.1, "lines") ) + # 添加右侧树状图 geom_ggside(data = dendro_data$segments, aes(x = y, y = x), inherit.aes = FALSE) + geom_ggside(data = dendro_data$labels, aes(x = y, y = x, label = label), inherit.aes = FALSE, hjust = 1) + scale_yside_reverse() + theme_ggside_minimal() + theme( ggside.panel.scale = 0.3, ggside.axis.text.y = element_blank(), ggside.axis.ticks.y = element_blank() )
此方法将树状图嵌入同一ggplot对象,布局更紧凑统一。
内容的提问来源于stack exchange,提问作者Neal Fultz
相关产品推荐
相关产品推荐

