大數據文摘出品
編譯:汪小七、張馨月、雲舟
主成分分析(PCA:Principal Component Analysis)非常有助於我們理解高維數據,我利用Stack Overflow的每日訪問數據對主成分分析進行了實踐和探索,你可以在rstudio :: conf 2018上找到其中一篇演講的錄音。演講的重點主要是我對於PCA的理解,而這篇文章中,我將主要介紹我是如何實現PCA的,以及我是如何製作演講中使用到的圖表的。
rstudio :: conf 2018
https://www.rstudio.com/resources/videos/understanding-pca-using-shiny-and-stack-overflow-data/
高維數據
此次分析使用的是去年Stack Overflow上註冊用戶訪問量前500的標籤數據。為了簡化處理,本文只使用了10%的註冊流量數據進行分析,但實際上我已經對所有流量數據進行了類似的分析,並獲得了幾乎相同的結果。
標籤數據
https://stackoverflow.com/tags
現在,把每個註冊用戶都想像成高維空間中的一個點,空間的坐標軸是R、JavaScript、C++等技術。那麼,在這個高維空間中,做相似工作的人對應的點就會彼此接近。接下來PCA會把這個高維空間轉變成一個新的具有特殊特徵的「特殊」高維空間。
在資料庫中適當地抽取數據後,最開始的數據看起來就像下面這樣:
library(tidyverse)library(scales)tag_percents## # A tibble: 28,791,663 x 3## User Tag Value## ## 1 1 exception-handling 0.000948## 2 1 jsp 0.000948## 3 1 merge 0.00284## 4 1 casting 0.00569## 5 1 io 0.000948## 6 1 twitter-bootstrap-3 0.00569## 7 1 sorting 0.00474## 8 1 mysql 0.000948## 9 1 svg 0.000948## 10 1 model-view-controller 0.000948## # ... with 28,791,653 more rows
可以看出,數據很乾淨,每行只有用戶編號和技術標籤。這裡的User列是隨機ID,而非Stack Overflow的標識符。在Stack Overflow中,我們公開了大量數據,但流量數據(即哪些用戶訪問過哪些問題)是沒有公開的。
對高維數據進行真正的匿名化其實是非常困難的,而這裡為了進行脫敏處理,我的做法是隨機化數據順序,並用數字替換Stack Overflow的標識符。Value列表示過去一年該用戶對該標籤的瀏覽量佔該標籤總瀏覽量的比例。
部分數據連結:
https://stackoverflow.blog/2010/06/13/introducing-stack-exchange-data-explorer/
https://cloud.google.com/bigquery/public-data/stackoverflow,
https://meta.stackexchange.com/questions/19579/where-are-the-stack-exchange-data-dumps
先不考慮脫敏的問題,我們首先看看用戶主要瀏覽的技術標籤有哪些,這張圖表給了我們一個直觀的概念。.
tag_percents %>%group_by(Tag) %>% summarise(Value = mean(Value)) %>% arrange(desc(Value)) %>% top_n(15) %>% mutate(Tag = reorder(Tag, Value)) %>% ggplot(aes(Tag, Value, label = Tag, fill = Tag)) + geom_col(alpha = 0.9, show.legend = FALSE) + geom_text(aes(Tag, 0.001), hjust = 0, color = "white", size = 4, family = "IBMPlexSans-Bold") + coord_flip() + labs(x = NULL, y = "Average % of a user's traffic") + scale_y_continuous(labels = percent_format(), expand = c(0.015,0)) +theme(axis.text.y=element_blank())
實施PCA
我們喜歡乾淨的數據,一是因為它就是我們查詢資料庫的結果,二是因為它可用於實現PCA等機器學習算法的探索性數據分析。為了實現PCA,我們需要一個矩陣,在這個例子裡稀疏矩陣(sparse matrix)就是最佳選擇——因為大多數開發人員只訪問一小部分技術標籤,因此我們的矩陣中會有很多零。tidytext軟體包中有一個函數cast_sparse(),它可以把上面的數據轉換為稀疏矩陣。
sparse_tag_matrix %tidytext::cast_sparse(User, Tag, Value)
R中有幾個實現PCA的算法是體會不到稀疏矩陣的美感的,比如prcomp()——此算法的第一步就是將剛剛製作好的稀疏矩陣強制轉換成一個常規矩陣,然後你要在那裡幹坐一輩子等它運行完,因為在它運行的時候電腦根本沒有內存讓你去做其他事了(別問我是怎麼知道的)。當然,R中也有一個程序包利用了稀疏矩陣的優勢——irlba。
在建立模型前,也別忘記先用scale()函數將你的矩陣規範化,這對於PCA的實現非常重要。
tags_scaled tags_pca
其中prcomp_irlba()函數的參數n代表我們想要得到的主成分個數。
那麼這一步究竟發生了什麼?我們會在接下來的章節中慢慢介紹。
class(tags_pca)## [1] "irlba_prcomp" "prcomp"names(tags_pca)## [1] "scale" "totalvar" "sdev" "rotation" "center" "x"