library(usedist)
library(tidyverse)
library(ggforce)
library(ggsci)
library(gt)
library(ggrepel)
library(ggdendro)
First, we make a fake data set:
make_fake <- function(group, n) {
tibble(x = rnorm(n, sample(10, 1), sample(2, 1)),
y = rnorm(n, sample(10, 1), sample(2, 1)),
group = group)
}
set.seed(2019)
df <- map(LETTERS[1:5], make_fake, n = 6) %>%
bind_rows() %>%
group_by(group) %>%
mutate(SampleID = paste0(group, 1:n())) %>%
ungroup()
Visualization of the data:
ggplot(df, aes(x, y)) +
geom_point(aes(shape = group), show.legend = FALSE) +
geom_mark_hull(aes(label = group, fill = group),
show.legend = FALSE) +
scale_fill_npg() +
theme_void()

Make a distance object:
Dist <- dist(df %>%
select(-group) %>%
column_to_rownames("SampleID")) ## euclidean
Which samples are in which group?
Gr <- df$group
names(Gr) <- df$SampleID
Make sure the order of the grouping is the same as Dist:
Gr <- Gr[colnames(as.matrix(Dist))]
Create a data frame of distances between groups of items using dist_groups from usedist package (https://github.com/kylebittinger/usedist):
Dist_Gr <- dist_groups(Dist, Gr)
Dist_Gr %>%
head() %>%
gt()
| Item1 | Item2 | Group1 | Group2 | Label | Distance |
|---|---|---|---|---|---|
| A1 | A2 | A | A | Within A | 2.597939 |
| A1 | A3 | A | A | Within A | 2.884004 |
| A1 | A4 | A | A | Within A | 6.262525 |
| A1 | A5 | A | A | Within A | 1.457591 |
| A1 | A6 | A | A | Within A | 3.458273 |
| A1 | B1 | A | B | Between A and B | 4.746163 |
What are the distances between groups?
Dist_Between_Groups <- Dist_Gr %>%
arrange(Label, Distance) %>%
filter(!duplicated(Label)) %>%
mutate(Distance = ifelse(str_detect(Label, "^Within "), 0, Distance)) %>%
mutate(rowItem = str_remove_all(Label, "^Between | and .+|^Within ")) %>%
mutate(colItem = str_remove_all(Label, "^.+ and |^Within ")) %>%
select(Item1, Item2, rowItem, colItem, Label, Distance)
Dist_Matix <- Dist_Between_Groups %>%
select(-Item1, -Item2, -Label) %>%
pivot_wider(names_from = "colItem", values_from = "Distance", values_fill = list(Distance = 0)) %>%
column_to_rownames("rowItem")
Dist_Matix <- Dist_Matix[, rownames(Dist_Matix)] # arrange columns
Dist_Matix <- Dist_Matix + t(Dist_Matix) # make a symmetric matrix
Now distances between groups are:
Dist_Matix %>%
rownames_to_column() %>%
gt(rowname_col = "rowname")
| A | B | C | D | E | |
|---|---|---|---|---|---|
| A | 0.000000 | 3.2739052 | 1.1348300 | 1.0844749 | 2.7477679 |
| B | 3.273905 | 0.0000000 | 0.5067407 | 4.8572852 | 4.0505295 |
| C | 1.134830 | 0.5067407 | 0.0000000 | 3.4722629 | 3.5756031 |
| D | 1.084475 | 4.8572852 | 3.4722629 | 0.0000000 | 0.2894509 |
| E | 2.747768 | 4.0505295 | 3.5756031 | 0.2894509 | 0.0000000 |
Which samples achieve the minimum distances? How about dendrogram?
whichSamplesFirst <- Dist_Between_Groups %>%
select(Item1, Label) %>%
mutate(Item1 = as.character(Item1)) %>%
left_join(df %>% select(-group), by = c("Item1" = "SampleID")) %>%
rename(x1 = x, y1 = y)
whichSamplesSecond <- Dist_Between_Groups %>%
select(Item2, Label) %>%
mutate(Item2 = as.character(Item2)) %>%
left_join(df %>% select(-group), by = c("Item2" = "SampleID")) %>%
rename(x2 = x, y2 = y)
whichSampleCombined <- whichSamplesFirst %>%
left_join(whichSamplesSecond, by = "Label") %>%
filter(str_detect(Label, "^Between "))
g_distance <- df %>%
mutate(repel_label = ifelse(SampleID %in% c(whichSampleCombined$Item1, whichSampleCombined$Item2),
SampleID, "")) %>%
ggplot(aes(x, y)) +
geom_point(aes(shape = group), show.legend = FALSE) +
geom_mark_hull(aes(label = group, fill = group),
show.legend = FALSE) +
scale_color_d3() +
scale_fill_npg() +
geom_text_repel(aes(label = repel_label)) +
geom_segment(data = whichSampleCombined,
aes(x = x1, y = y1, xend = x2, yend = y2, color = Label)) +
theme_void()
hc <- hclust(as.dist(Dist_Matix))
hcdata <- dendro_data(hc, type = "rectangle")
g_dendro <- segment(hcdata) %>%
ggplot() +
geom_segment(aes(x, y, xend = xend, yend = yend)) +
geom_text(data = hcdata$labels, aes(x, y, label = label),
hjust = -1, size = 3) +
coord_flip() +
scale_y_reverse() +
theme_classic() +
theme(axis.line.y = element_blank(),
axis.text.y = element_blank(),
axis.ticks.y = element_blank(),
axis.title = element_blank())
g_distance

g_dendro
