#r #spatial #hierarchical-clustering
#r #пространственная #иерархическая кластеризация
Вопрос:
Допустим, у меня есть этот набор данных, содержащий центроид каждой деревни (x_cent, y_cent), а также несколько непространственных параметров, таких как общая численность населения и количество начальных школ, дороги с твердым покрытием и размер деревни.
set.seed(1234)
dat <-
expand.grid(
district = c(1:2),
sub_district = c(1:7),
sub_sub_district = c(1:19),
village_id = c(1:2)
) %>%
dplyr::group_by(district, sub_district, sub_sub_district, village_id) %>%
dplyr::mutate(
# Total population
tot_pop = rnorm(n = 1, mean = 100, sd = 5000),
# Number of primary schools
p_schl = rnorm(n = 1, mean = 2, sd = 6),
# Paved road
p_road = sample(0:1, size = dplyr::row_number(), replace = FALSE)
) %>%
dplyr::group_by(district, sub_district, sub_sub_district, village_id) %>%
dplyr::mutate(
# Size of village in hectares
town_hec = rnorm(n = 1, mean = 300, sd = 320)
) %>%
dplyr::group_by(district, sub_district, sub_sub_district, village_id) %>%
dplyr::mutate(
# Coordinates
x_cent = rnorm(n = 1, mean = 99.9, sd = 0.66),
y_cent = rnorm(n = 1, mean = 33.3, sd = 0.33)
) %>%
dplyr::ungroup()
Я хочу создавать кластеры деревень на основе пространственной близости, а также этих непространственных параметров (tot_pop, p_schl, p_road и town_hec). Я также хочу взвесить алгоритм таким образом, чтобы пространственная близость была важнее, чем сопоставление по другим ковариатам. Наконец, я хочу иметь возможность ограничивать количество наблюдений для каждого кластера.
Я предполагаю, что это будет модель иерархической кластеризации, но я не уверен, как реализовать это в R, и возможна ли кластеризация по пространственным и непространственным ковариатам.
Заранее спасибо за любые предложения.
Ответ №1:
Поскольку метод иерархической кластеризации зависит от матрицы расстояний, простой способ придать больший вес некоторой переменной — это масштабировать эти переменные в соответствии с важностью, которую вы хотите, чтобы они имели, перед вычислением матрицы расстояний.
Комментарии:
1. Спасибо @Wawvi — как бы вы порекомендовали реализовать свое предложение в этом контексте?
2. Вы можете масштабировать свои непространственные параметры до [0; 1], а координаты x и y — до [0; 4], если хотите, чтобы пространственные параметры имели вес в 4 раза больше, чем ваши непространственные параметры. Затем вычислите матрицу расстояний для этих масштабируемых переменных с помощью функции dist(), которую затем можно передать в hclust() .
3. Понятно — спасибо за разъяснение. Я собираюсь добавить возможное решение, используя пакет scclust в разделе ответов.
Ответ №2:
Потенциальное решение, помеченное как ответ на данный момент:
Сначала измените масштаб переменных, которые вы хотите включить в свою матрицу расстояний. В этом случае я присваиваю больший вес (10) переменным координат (x_cent и y_cent).
dat$x_cent <- scales::rescale(dat$x_cent, to = c(0, 10))
dat$y_cent <- scales::rescale(dat$y_cent, to = c(0, 10))
dat$tot_pop <- scales::rescale(dat$tot_pop, to = c(0, 1))
Во-вторых, подмножество данных, включающее только ковариаты, с помощью которых вы вычисляете расстояние:
dat <- dat[, c("x_cent", "y_cent", "tot_pop")]
Затем вычислите матрицу расстояний:
dist <- distances::distances(as.data.frame(dat))
Вычислите кластеры с помощью scclust
пакета и добавьте значения к исходному набору данных. Этот пакет позволяет вам вводить ограничения на размер вашего кластера.
clust <- scclust::hierarchical_clustering(distances = dist, size_constraint = 10)
final <- dplyr::bind_cols(dat, clust) %>% dplyr::rename(block = `...4`)
Вы можете видеть, сколько наблюдений существует в кластере:
investigate_cluster <- dplyr::group_by(final, block) %>% dplyr::summarise(count = length(block))
head(investigate_cluster)
# A tibble: 6 x 2
block count
<scclust> <int>
1 0 10
2 1 10
3 2 10
4 3 10
5 4 10
6 5 10
И легко визуализируйте свои кластеры:
ggplot(final, mapping = aes(x = x_cent, y = y_cent, color = factor(block)))
geom_point()
ggConvexHull::geom_convexhull(alpha = .5, aes(fill = factor(block)))
theme_bw()
theme(legend.position = "none")