You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: week3/clustering.qmd
+35-43Lines changed: 35 additions & 43 deletions
Original file line number
Diff line number
Diff line change
@@ -16,7 +16,7 @@ execute:
16
16
message: false
17
17
---
18
18
19
-
## Clustering
19
+
## Introduction
20
20
In this session we continue with the gene expression dataset from <https://www.ebi.ac.uk/gxa/experiments/E-GEOD-72806/>
21
21
22
22
First load the proper libraries and filter/transform the data using the functions that you created yesterday. To make the functions available in the current R session, you can use the `source()` function to run the R script.
coldata <- as.data.frame(colData(se)[, c("environmental_stress"), drop = FALSE])
34
34
```
35
35
36
-
## Introduction
37
-
38
36
We have already seen that the 12 samples represent 4 treatments, with 3 replicates per treatment. We expect the replicates to have similar gene expression values. To check this, we can cluster samples based on these expression values. You were already introduced to clustering on day 2 of the course. Then you used hierarchical clustering, today we will also look at another common clustering method called *k*-means.
39
37
40
38
In clustering we group samples together that are similar, but how do we determine how similar samples are? We need a similarity measure. This is usually derived from a distance measure, which is inversely related to the similarity.
@@ -125,41 +123,11 @@ We can try the same for the two most variable genes:
Now we could copy the plotting code from above, but copying a block of code is generally not a good idea, it is better to turn that into a function that you can call. So please go ahead and create a function that takes the list of genes as input, as well as the data that is needed to make the plot (logcounts and coldata). Add this function to your **rnaseq_functions.R** script
130
128
131
129
```{r}
132
-
133
-
# ---- extract expression and transpose ----
134
-
expr_df <- as.data.frame(t(logcounts[genes, , drop = FALSE]))
title = "Samples projected onto the two most variable genes",
157
-
x = paste(genes[1], "(log(cpm) expression)"),
158
-
y = paste(genes[2], "(log(cpm) expression)")
159
-
) +
160
-
theme_bw()
161
-
162
-
p2
130
+
two_gene_scatter_plot(genes, logcounts, coldata)
163
131
```
164
132
:::
165
133
@@ -176,7 +144,8 @@ The gene IDs are not really informative to learn more about the kinds of genes t
176
144
We can use the 2-gene representation of the samples to find clusters of similar samples (like you already did on day 2 of the course). Hierarchical clustering starts with each sample in its own cluster and then step-wise merges the two most similar clusters based on a distance measure (Euclidean, correlation-based, etc.) until all samples are in one cluster. This is typically depicted as a tree/dendrogram. This merging can be stopped if a certain number of clusters is reached. How the distance is calculated depends on the distance measure, but also on the positions in the clusters the distance is calculated between. For this several so-called **agglomeration** or **linkage methods** are used that give different results. With **average linkage**, the distance between two cluster is calculated as the average of all distances between the members of both cluster. In **complete linkage**, the largest distance between any two members of two cluster is used, while **single linkage** uses the smallest distance between any two members of both clusters. You can imagine that both the distance measure as well as the linkage methods affect the clustering.
177
145
178
146
```{r}
179
-
d_samples <- dist(df[,genes], method = "euclidean") # calculate Euclidean distance between all samples
147
+
X <- df[, genes] # select only the gene expression columns for clustering
148
+
d_samples <- dist(X, method = "euclidean") # calculate Euclidean distance between all samples
180
149
hc_samples <- hclust(d_samples, method = "complete") # cluster using complete-linkage
181
150
plot(hc_samples, hang=-1) # this actually calls plot.hclust, hang=-1 makes sure the tree starts (or ends, depending on your perspective) at 0
182
151
```
@@ -308,9 +277,10 @@ df <- merge(
308
277
)
309
278
310
279
k <- 4
280
+
X <- df[, genes] # select only the gene expression columns for clustering
title = "k-means clustering in 2-gene expression space",
306
+
title = "K-means clustering in 2-gene expression space",
337
307
subtitle = paste("k =", k),
338
308
x = paste(genes[1], "(log(cpm) expression)"),
339
309
y = paste(genes[2], "(log(cpm) expression)")
@@ -349,7 +319,6 @@ We can print the cluster assignment in a table with the cluster numbers as colum
349
319
350
320
```{r}
351
321
print(table(km$cluster))
352
-
353
322
```
354
323
355
324
Given the randomness of the initialization of the centroid locations, this might not be the best we can get.
@@ -358,12 +327,14 @@ Given the randomness of the initialization of the centroid locations, this might
358
327
Write a for-loop to repeat the *K*-means clustering 10 times and report the number of samples per cluster for each repetition
359
328
360
329
```{r}
330
+
X <- df[, genes]
331
+
361
332
362
333
km <- kmeans(
363
-
df[, genes],
334
+
X,
364
335
centers = k,
365
336
)
366
-
337
+
print(table(km$cluster))
367
338
368
339
```
369
340
@@ -378,6 +349,27 @@ Ideally we would want to run *K*-means many times and keep only the best result.
378
349
```
379
350
:::
380
351
352
+
Choosing *K* is important for *K*-means clustering. We used the number of treatments, but we can also let the data inform us of the optimal *K*. For this we use the total sum of the the squared distance of every sample to the center of its cluster, the total Within Sum of Squares or total WSS. Total WSS will be largest when *K* is 1 and smallest (zero) if we set *K* to the number of samples. If we plot a trend line of the total WSS (an elbow plot), we can look for a bend that indicates a point after which adding more clusters does not improve the total WSS as much anymore.
353
+
354
+
355
+
356
+
```{r}
357
+
set.seed(42)
358
+
X <- t(logcounts) # now we take all genes into account. With the transpose t() we would cluster the genes instead of the samples.
359
+
wss <- numeric(10) # vector to keep track of the total Within Sum of Squares values per K
360
+
for (k in 1:10) {
361
+
km <- kmeans(X, centers = k, nstart = 25)
362
+
wss[k] <- km$tot.withinss # the kmeans object already has the total WSS
363
+
}
364
+
365
+
plot(1:10, wss,
366
+
type = "b", # a line plot with points
367
+
pch = 19,
368
+
xlab = "Number of clusters (k)",
369
+
ylab = "Total within-cluster sum of squares",
370
+
main = "Elbow Plot")
371
+
```
372
+
381
373
## Clustering genes
382
374
383
375
Next to clustering the samples, we can also cluster the genes based on their expression patterns. If genes have similar expression, they may have related functions. We will use a different data set for this, a seed germination time series from *Arabidopsis thaliana* from this paper: ["Extensive transcriptomic and epigenomic remodelling occurs during Arabidopsis thaliana germination."](https://europepmc.org/article/MED/28911330)
@@ -442,7 +434,7 @@ for (cluster in 1:3) {
442
434
main = paste("Cluster",cluster) )
443
435
}
444
436
```
445
-
As a bonus (so not part of the examn) we can investigate which biological processes these genes are involved in to dive into the biology. For this a common approach is [Gene Ontology enrichment analysis](https://geneontology.org/docs/go-enrichment-analysis/), which basically comes down to finding biological progresses that occur more frequently in the set of genes in a cluster than would expected by chance. The code below does that for all three clusters. The dotplots show the top 20 most enriched biological processes per cluster.
437
+
As a bonus (so not part of the exam) we can investigate which biological processes these genes are involved in to dive into the biology. For this a common approach is [Gene Ontology enrichment analysis](https://geneontology.org/docs/go-enrichment-analysis/), which basically comes down to finding biological progresses that occur more frequently in the set of genes in a cluster than would expected by chance. The code below does that for all three clusters. The dotplots show the top 20 most enriched biological processes per cluster.
446
438
447
439
```{r}
448
440
if (!requireNamespace("org.At.tair.db", quietly = TRUE))
@@ -474,4 +466,4 @@ for (cluster in 1:3) {
474
466
print(dotplot(ego, showCategory = 20, title = paste("Cluster",cluster)))
475
467
}
476
468
```
477
-
One cluster is clearly composed of seed expressed genes, another more focuessed on cell wall organisation, and one clearly enriched in photosynthesis related genes.
469
+
One cluster is clearly composed of seed expressed genes, another more focused on cell wall organisation, and one clearly enriched in photosynthesis related genes.
0 commit comments