如何在簇内进行聚类

4
我有一组地图上的点,每个点都有一个给定的参数值。我想要:
  1. 将它们在空间上聚类,并忽略任何少于10个点的聚类。我的数据框应该有一个列(Clust),用于表示每个点所属的聚类 [已完成]
  2. 在每个聚类中对参数值进行子聚类; 添加一个列到我的数据框(subClust),用于按子聚类对每个点进行分类。

除了可能使用循环之外,我不知道如何做第二部分。

图像显示了颜色编码为聚类的一组空间分布点(左上角),并按参数值在右上角绘制。底部行显示具有> 10个点的聚类(左侧)和按参数值排序的每个聚类的面板(右侧)。这些面板是我想能够根据最小聚类分离距离(d = 1)按子聚类进行颜色编码的面板。

欢迎任何指针/帮助。我的可重复代码如下。

enter image description here

# TESTING
library(tidyverse)
library(gridExtra)

# Create a random (X, Y, Value) dataset
set.seed(36)
x_ex <- round(rnorm(200,50,20))
y_ex <- round(runif(200,0,85))
values <- rexp(200, 0.2)
df_ex <- data.frame(ID=1:length(y_ex),x=x_ex,y=y_ex,Test_Param=values)

# Cluster data by (X,Y) location
d = 4
chc <- hclust(dist(df_ex[,2:3]), method="single")

# Distance with a d threshold - used d=40 at one time but that changes...
chc.d40 <- cutree(chc, h=d) 
# max(chc.d40)

# Join results 
xy_df <- data.frame(df_ex, Clust=chc.d40)

# Plot results
breaks = max(chc.d40)
xy_df_filt <- xy_df %>% dplyr::group_by(Clust) %>% dplyr::mutate(n=n()) %>% dplyr::filter(n>10)# %>% nrow

p1 <- ggplot() +
  geom_point(data=xy_df, aes(x=x, y=y, colour = Clust)) +
  scale_color_gradientn(colours = rainbow(breaks)) +
  xlim(0,100) + ylim(0,100) 

p2 <- xy_df %>% dplyr::arrange(Test_Param) %>%
ggplot() +
  geom_point(aes(x=1:length(Test_Param),y=Test_Param, colour = Test_Param)) +
  scale_colour_gradient(low="red", high="green")

p3 <- ggplot() +
  geom_point(data=xy_df_filt, aes(x=x, y=y, colour = Clust)) +
  scale_color_gradientn(colours = rainbow(breaks)) +
  xlim(0,100) + ylim(0,100) 

p4 <- xy_df_filt %>% dplyr::arrange(Test_Param) %>%
ggplot() +
  geom_point(aes(x=1:length(Test_Param),y=Test_Param, colour = Test_Param)) +
  scale_colour_gradient(low="red", high="green") +
  facet_wrap(~Clust, scales="free")

grid.arrange(p1, p2, p3, p4, ncol=2, nrow=2)

这个代码片段无法工作——无法在dplyr mutate()中使用管道...

# Second Hierarchical Clustering: Try to sub-cluster by Test_Param within the individual clusters I've already defined above
xy_df_filt %>% # This part does not work
  dplyr::group_by(Clust) %>% 
  dplyr::mutate(subClust = hclust(dist(.$Test_Param), method="single") %>% 
                  cutree(, h=1))

以下是使用循环的解决方法 - 但我真的更想学习如何使用dplyr或其他非循环方法来完成此操作。下面是显示子聚类图案的更新图像。
sub_df <- data.frame()
for (i in unique(xy_df_filt$Clust)) {
  temp_df <- xy_df_filt %>% dplyr::filter(Clust == i)
  # Cluster data by (X,Y) location
  a_d = 1
  a_chc <- hclust(dist(temp_df$Test_Param), method="single")

  # Distance with a d threshold - used d=40 at one time but that changes... 
  a_chc.d40 <- cutree(a_chc, h=a_d) 
  # max(chc.d40)

  # Join results to main df
  sub_df <- bind_rows(sub_df, data.frame(temp_df, subClust=a_chc.d40)) %>% dplyr::select(ID, subClust)
}
xy_df_filt_2 <- left_join(xy_df_filt,sub_df, by=c("ID"="ID"))

p4 <- xy_df_filt_2 %>% dplyr::arrange(Test_Param) %>%
ggplot() +
  geom_point(aes(x=1:length(Test_Param),y=Test_Param, colour = subClust)) +
  scale_colour_gradient(low="red", high="green") +
  facet_wrap(~Clust, scales="free")

grid.arrange(p1, p2, p3, p4, ncol=2, nrow=2)

enter image description here

2个回答

1
你可以为你的子集群做这个...
xy_df_filt_2 <- xy_df_filt %>% 
                group_by(Clust) %>% 
                mutate(subClust = tibble(Test_Param) %>% 
                                  dist() %>% 
                                  hclust(method="single") %>% 
                                  cutree(h=1))

嵌套管道是可以的。我认为你版本的问题在于没有向dist传递正确类型的对象。如果您只向dist传递单个列,则不需要使用tibble术语,但是我已经将其保留,以防您想要像对主聚类一样使用多个列。
您可以使用相同的公式,但没有group_by,从df_ex计算xy_df

你的tibble(x,y)应该改为tibble(Test_Param),这样才能正确,因为第二个聚类是基于Test_Param距离而不是x,y。但是你的方法也可以。谢谢 - val
是的,当然 - 对不起。我已经修改了答案。 - Andrew Gustar
当我运行这段代码时,我会收到一堆警告(Warning in mutate_impl(.data, dots) : binding character and factor vector, coercing into character vector),似乎与此问题有关(https://github.com/tidyverse/dplyr/issues/2911),但我无法解决它;我想使用factor()或as.factor()将subClust转换为因子,并且我想知道tibble()是否妨碍了它。Camille的答案没有这个问题。 - val
@val 是的,我认为这只是一个指示,即“mutate”需要添加因子水平,它通过转换为字符来实现。这只是一个警告 - 我在其他事情上也遇到过它,但这并不一定意味着计算不起作用。 - Andrew Gustar

1

有一种使用dotidy的组合方法来实现,但是我总是很难通过do来使事情按照我想要的方式排列。相反,我通常会结合R基础库中的splitpurrr中的map_dfrsplit将数据框按Clust拆分,并给出一个数据框列表,然后您可以对其进行映射。 map_dfr映射每个数据框并返回单个数据框。

我从你的xy_df_filt开始生成了我认为应该与你从for循环中得到的xy_df_filt_2相同的结果。我做了两个图表,尽管两组聚类有点难以看清。

xy_df_filt_2 <- xy_df_filt %>%
    split(.$Clust) %>%
    map_dfr(function(df) {
        subClust <- hclust(dist(df$Test_Param), method = "single") %>% cutree(., h = 1)

        bind_cols(df, subClust = subClust)
    })

ggplot(xy_df_filt_2, aes(x = x, y = y, color = as.factor(subClust), shape = as.factor(Clust))) +
    geom_point() +
    scale_color_brewer(palette = "Set2")

更清晰的分面(数据可视化)
ggplot(xy_df_filt_2, aes(x = x, y = y, color = as.factor(subClust), shape = as.factor(Clust))) +
    geom_point() +
    scale_color_brewer(palette = "Set2") +
    facet_wrap(~ Clust)

这段文字是由reprex package(v0.2.0)在2018年4月14日创建的。


我认为这也是一个不错的答案 - 使用我不熟悉的工具。谢谢。 - val
请看我在安德鲁的回答中的评论;你的方法没有产生警告,而他的方法却有。 - val

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接