4

I am doing some time series clustering, and would like to align the dendrogram with the time series shapes. This is almost there:

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

Series + dendrogram

Is there some easier way to do this that I'm just missing?

2 Answers 2

2

The height of facet_grid panels are the same. So if you set panel.spacing.y = uint(0) at timeseries plot and set scale_x_continuous(limits = c(0.5, n_distinct(df_long$series) + 0.5), expand = 0) at dendrogram, 1:n_distinct(df_long$series) are just align with center of left panels.

(And I feel that panel.spacing.y = unit(0.1, "lines") has no practical problem when in use. )

dendro_plot <- ggdendrogram(hc, rotate = TRUE, heme_dendro = FALSE) +
  theme_minimal() +
  scale_x_reverse() + 
  scale_x_continuous(limits = c(0.5, n_distinct(df_long$series) + 0.5), expand = 0) +  # !! CORE CODE !!
  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


# ---- 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
  facet_grid(series ~ .) +  # just check
  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

enter image description here

Sign up to request clarification or add additional context in comments.

Comments

-1

It's easy to create such plots using ggalign, which offers an integrative, composable visualization framework for ggplot2..


library(ggalign)
set.seed(1)
time <- 1:50
data <- t(data.frame(
    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))
))

stack_discreteh(data) -
    # set default theme for all plots in the layout
    scheme_theme(theme_minimal()) +

    # add line plot
    ggfree(size = 3) +
    scheme_data(function(d) {
        d$time <- time[d$.column_index]
        d$.panel <- factor(d$.panel)
        names(d$.panel)
        d
    }) +
    geom_line(aes(time, value)) +
    geom_hline(yintercept = 0, linetype = "dashed") +
    facet_grid(rows = vars(.panel)) +
    theme(strip.text = element_blank(), strip.background = element_blank()) +

    # add the dendrogram
    align_dendro(aes(color = branch), k = 4, plot_cut_height = FALSE) +
    scale_color_brewer(palette = "Dark2", guide = "none") +
    theme(axis.text.y = element_text())

enter image description here

1 Comment

Please review stackoverflow.com/help/promotion and edit your answer accordingly, as well as your other similar answers.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.