You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何更简便实现树状图与分面时间序列的对齐?

时间序列聚类:树状图与时间序列形态对齐的简便实现方法

问题描述

我正在开展时间序列聚类工作,希望将树状图(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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 03:15:56