基于base与dplyr按标签实现欧盟27国金融数据层次时间序列聚类
欧盟27国HICP时间序列层次聚类问题解决
问题背景
用户需对欧统局(eurostat)提供的欧盟27国HICP金融数据开展层次时间序列聚类,目标是按各国时间序列曲线分组,并在树状图中显示国家名称标签。但尝试以下代码时触发invalid dendrogram input错误:
df_long <- aggregate(values~time, data=data, FUN=mean) hc <- hclust(dist(df_long["values"], method="minkowski", p=1.5), method="average") plot(hc, labels=data$name, hang=-1)
用户已实现按国家均值/总和聚类的方案,但希望改为基于「各国全时间序列数据」的聚类,询问当前方法是否可行,或是需要其他时间序列聚类方案。
用户提供的数据与现有代码
数据预览
> head(data) geo unit coicop time values name label 1 AT RCH_A CP00 2001-04-01 2.6 Austria Austria 2 AT RCH_A CP00 2007-03-01 1.9 Austria Austria ...
现有完整代码(均值/总和聚类)
data <- get_eurostat("prc_hicp_manr") data <- filter(data, time >= "2000-03-01" & time <= "2022-09-30" & coicop == "CP00") country_name <- eurostat::eu_countries data <- merge(data, country_name, by.x = "geo", by.y = "code") data <- filter(data, name != "" & name != "United Kingdom") # 绘制时间序列曲线 ggplot(data) + aes(x = time, y = values, colour = name) + geom_line() + scale_color_hue(direction = 1) + labs(title = "Przebiegi HICP dla krajow UE") + theme_bw() + theme(plot.title = element_text(size = 20L, hjust = 0.5)) # 按国家求和后聚类 data_by_country <- data %>% group_by(name) %>% summarize(value = sum(values)) %>% ungroup() %>% as.matrix() data_by_country <- as.data.frame(data_by_country) rownames(data_by_country) <- data_by_country$name plot(hclust(dist(data_by_country[, "value", drop=F], method = "minkowski", p = 1,5), method = "complete"), main = "Clustering countries based on HICP", xlab = NA, sub = NA) hc <- hclust(dist(data_by_country[, "value", drop=F], method = "minkowski", p = 1,5), method = "complete") clusters <- cutree(hc, 4) rect.hclust(hc, k = 4, border = "red")
问题分析与解决方案
1. 原错误代码的核心问题
错误代码的本质是数据结构完全不符合时间序列聚类要求:
aggregate(values~time, data=data, FUN=mean)是按时间维度计算所有国家的均值,得到的是一条「平均时间序列」,仅包含时间点维度,无国家维度。- 用这个单变量序列做聚类,生成的树状图节点对应时间点,而非国家,但强行传入
labels=data$name(重复的国家名称),维度完全不匹配,因此触发invalid dendrogram input错误。
2. 时间序列聚类的正确思路
要基于各国完整时间序列聚类,必须先将数据转换为宽格式:每行对应一个国家,每列对应一个时间点的数值。这样每个国家就成为一个多维向量(维度等于时间点数量),才能计算国家间的时间序列距离。
3. 具体实现步骤
步骤1:转换数据为宽格式
用tidyr::pivot_wider将长格式数据转为宽格式:
# 转换为宽格式:行=国家,列=时间,值=HICP数值 data_wide <- data %>% select(name, time, values) %>% pivot_wider(names_from = time, values_from = values) %>% column_to_rownames(var = "name")
步骤2:计算时间序列距离
时间序列聚类可选择两种距离计算方式:
- 通用数值距离:如闵可夫斯基距离(与原代码一致)
- 专业时间序列距离:如动态时间规整(DTW),适合形状相似但时间错位的序列(需
dtw包)
# 方式1:闵可夫斯基距离 dist_matrix <- dist(data_wide, method = "minkowski", p = 1.5) # 方式2:动态时间规整(DTW)距离(更贴合时间序列特征) # library(dtw) # dist_matrix <- dist(data_wide, method = function(x,y) dtw(x,y)$distance)
步骤3:层次聚类与可视化
用hclust完成层次聚类,绘图时直接用行名(国家名称)作为标签:
# 层次聚类(可替换method为"complete"/"ward.D2"等) hc <- hclust(dist_matrix, method = "average") # 绘制树状图,标签自动为国家名称,hang=-1让标签对齐 plot(hc, main = "欧盟27国HICP时间序列层次聚类", hang = -1, xlab = "", sub = "", cex = 0.7) # 切割为4类并标记 rect.hclust(hc, k = 4, border = "red")
4. 两种聚类方案对比
- 原均值/总和聚类:仅用到国家的单数值特征(总和/均值),丢失了时间序列的趋势、波动等关键信息,仅能反映整体水平高低,无法体现时间维度的相似性。
- 时间序列聚类:基于完整时间曲线特征,能捕捉各国HICP的趋势变化、周期波动等相似性,完全贴合「对比各国各日期数值」的需求。
完整可运行代码
library(eurostat) library(tidyverse) # 1. 获取并清洗数据 data <- get_eurostat("prc_hicp_manr") data <- filter(data, time >= "2000-03-01" & time <= "2022-09-30", coicop == "CP00") country_name <- eurostat::eu_countries data <- merge(data, country_name, by.x = "geo", by.y = "code") data <- filter(data, name != "" & name != "United Kingdom") # 2. 转换为宽格式(行=国家,列=时间) data_wide <- data %>% select(name, time, values) %>% pivot_wider(names_from = time, values_from = values) %>% column_to_rownames(var = "name") # 3. 计算距离矩阵(闵可夫斯基距离) dist_matrix <- dist(data_wide, method = "minkowski", p = 1.5) # 4. 层次聚类 hc <- hclust(dist_matrix, method = "average") # 5. 绘制树状图 plot(hc, main = "欧盟27国HICP时间序列层次聚类", hang = -1, xlab = "", sub = "", cex = 0.7) # cex调整标签大小 rect.hclust(hc, k = 4, border = "red") # 可选:查看聚类结果 clusters <- cutree(hc, k = 4) print(clusters)
内容的提问来源于stack exchange,提问作者niksok11
相关产品推荐
相关产品推荐

