Using PCA and Clustering To Analyze Genes and Leukemia | by Leihua Ye, PhD | Towards Data Science

In the world of Machine Learning, unsupervised learning is the process of drawing inference without labeled outcomes. Principal Component Analysis (PCA) and Clustering are two common unsupervised learning techniques.
PCA is the process of reducing high dimensions into a few layers of key features. After dimension reduction, we only have to deal with a small number of features and leave others out.
Clustering is the task of assigning data inputs with similar features to a specific group. After clustering, data points within the same group should be similar but dramatically different from the one in other groups.
In this post, I’ll apply PCA and Hierarchical Clustering to a life science dataset to analyze how specific genes affect the leukemia type.
The dataset was originally collected by Yeoh et al. (2002) with 3141 genes, a class of 7 leukemia subtypes from 327 patients (here).
# Load the dataset and transform data type
Change the data type of leukemia_data from character to factor.
knitr::opts_chunk$set(echo = TRUE)library(dplyr)library(readr)leukemia_data <- read_csv(“leukemia_data.csv”)leukemia_data = leukemia_data %>%  mutate(Type=as.factor(leukemia_data$Type))#Display different types of leukemia and count the numbers of each type#doing explorative data analysis is always preferred for all types of researchtable(leukemia_data$Type)BCR-ABL  E2A-PBX1 Hyperdip50     MLL    OTHERS     T-ALL    TEL-AML1 15         27         64         20       79         43         79
Some descriptive data: BCR-ABL is the least common type of leukemia and TEL-AML1 is the most common in the dataset.
# Dimension reduction
Since there are 3141 genes, we should use PCA to reduce the number down to be interpretable.
# generate PCA results;# scale=TRUE and center=TRUE --> mean 0 and variance 1pr.out=prcomp(select(leukemia_data,-1),scale=TRUE, center=TRUE)# pr.out$sdev: the standard deviations of the principal components; # (pr.out$sdev)2: variance of the principal componentspr.var=(pr.out$sdev)2# pve: variance explained by the principal componentpve = pr.var/sum(pr.var)# cumulative explained variancecumulative_pve <-cumsum(pve)# Plot them par(mfrow=c(1, 2))plot(pve, type=”l”, lwd=3) plot(cumulative_pve, type=”l”, lwd=3)
These two plots provide a visual illustration that a few key features could explain a large portion of the total variance.
# Question: which genes have the highest loadings (in absolute value) in the first principal component (aka. PC1)?
#project loadings on PC1Loadings_PC_1 <- pr.out$rotation[,1]#in absolute valueLoadings_PC_1_ABS <- abs(Loadings_PC_1)#print and sort the resultsprint(head(sort(Loadings_PC_1_ABS,decreasing = TRUE)))SEMA3F       CCT2       LDHB      COX6C     SNRPD2       ELK3 0.04517148 0.04323818 0.04231619 0.04183480 0.04179822 0.04155821
The top six genes with the highest loadings on PC1 are: SEMA3F, CCT2, LDHB, COX6C, SNRPD2, and ELK3.
#plot the data into the first two principal component dimensionsProjection_1_PC = pr.out$x[,1]Projection_2_PC = pr.out$x[,2]rainbow_colors <- rainbow(7)plot_colors <- rainbow_colors[leukemia_data$Type]plot(Projection_1_PC,Projection_2_PC,cex=0.4,col=plot_colors)text(Projection_1_PC,Projection_2_PC,labels=leukemia_data$Type,col=plot_colors)
Only T-ALLs (top right corner) are picked out from the crowd; other types of leukemia are still sticking together. In other words, the first two components are not the ideal choice of separation.
Let’s try the first and third PCs.
Projection_3_PC = pr.out$x[,3]rainbow_colors <- rainbow(7)plot_colors <- rainbow_colors[leukemia_data$Type]plot(Projection_1_PC,Projection_3_PC,cex=0.4,col=plot_colors)text(Projection_1_PC,Projection_3_PC,labels=leukemia_data$Type,col=plot_colors)
In comparison, the 1st and 3rd PCs did a better job because the distribution scatters across different regions.
#project to PCs
#install.packages(c(“ggridges”,”ggplot2",”tidyverse”))library(ggplot2)library(ggridges)library(tibble)#project on PC1z1 = pr.out$x[,1]#create a new tibble where the first column is the project of the data on the first PC and the second is the leukemia typeTB_1 = tibble(z1,leukemia_data$Type)#plot it ggplot(TB_1, aes(x = z1, y = leukemia_data$Type, fill = leukemia_data$Type)) + geom_density_ridges()
It seems z1 can’t tell different types apart.
Let’s try z3.
z3 = pr.out$x[,3]TB_3 = tibble(z3,leukemia_data$Type)ggplot(TB_3, aes(x = z3, y = leukemia_data$Type, fill = leukemia_data$Type)) + geom_density_ridges()
Great, we have moved E2A-PBX1 (the orange distribution at the bottom) slightly to the left and Hyperdip50 (the green distribution just above E2A-PBX1) slightly to the right.
#Let’s apply Hierarchical Clustering to group different types of Leukemia
Create a sub-group including three types of leukemia: T-ALL, TEL-AML1, or Hyperdip50.
library(dplyr)#subsetleukemia_subset <- filter(leukemia_data,Type==”T-ALL”|Type==”TEL-AML1"|Type==”Hyperdip50")#scale() function helps standardizationscaled_leukemia_subset <- scale(leukemia_subset[,-1],center=TRUE, scale=TRUE)#remove the first column and scale it#euclidean distance matrixscaled_leukemia_subset_dist <- dist(scaled_leukemia_subset,method=”euclidean” )##hierarchical clustering using complete linkageset.seed(1)leukemia.hclust = hclust(scaled_leukemia_subset_dist,method=”complete”)plot(leukemia.hclust, labels=leukemia_subset$Type, main=’Hierarchical Clusting of Leukemia Subset Data’)
The hierarchical clustering did an awesome job, as the same type of leukemia stay together and different types stay away.
Please find me on LinkedIn and Youtube.
Also, check my other posts on Artificial Intelligence and Machine Learning.