如何利用散點圖矩陣進行數據可視化
本文介紹了如何在 Python 中利用散點圖矩陣(Pairs Plots)進行數據可視化。
如何快速構建強大的探索性數據分析可視化
當你得到一個很不錯的干凈數據集時,下一步就是探索性數據分析(Exploratory Data Analysis,EDA)。EDA 可以幫助發現數據想告訴我們什么,可用于尋找模式、關系或者異常來指導我們后續的分析。盡管在 EDA 中有很多種可以使用的方法,但是其中最有效的啟動工具之一就是散點圖矩陣(pairs plot,也叫做 scatterplot matrix)。散點圖矩陣允許同時看到多個單獨變量的分布和它們兩兩之間的關系。散點圖矩陣是為后續分析識別趨勢的很棒方法,幸運的是,用 Python 實現也是相當簡單的。
本文,我們將介紹如何使用 Seaborn 可視化庫(https://seaborn.pydata.org/)在 Python 中啟動和運行散點圖矩陣。我們將看到如何為快速檢查數據而創建默認散點圖矩陣,以及如何為了更深入的分析定制可視化方案。
代碼地址:https://github.com/WillKoehrsen/Data-Analysis/blob/master/pairplots/Pair%20Plots.ipynb
我們將探索一個現實世界數據集,它由***的社會經濟數據組成,這些數據都是 Gapminder 收集的。
Seaborn 中的散點圖矩陣
我們需要先了解一下數據,以便開始后續的進展。我們可以 pandas 數據幀的形式加載這些社會經濟數據,然后我們會看到下面這些列:

每一行代表一個國家一年的觀察數據,列代表變量(這種格式的數據被稱作整潔數據,tidy data),其中有兩個類別列(國家和洲)和四個數值列。這些列簡單易懂:life_exp 是出生時的預期壽命,以年為單位,popis 是人口數量,gdp_per_cap 是人均 GDP(以國際元)為單位。
seaborn 中的默認散點圖矩陣僅僅畫出數值列,盡管我們隨后也會使用類別變量來著色。創建默認的散點圖矩陣很簡單:我們加載 seaborn 庫,然后調用 pairplot 函數,向它傳遞我們的數據幀即可:
- # Seaborn visualization library
- import seaborn as sns
- # Create the default pairplot
- sns.pairplot(df)

我仍舊大為吃驚,一行簡單的代碼就能夠讓我們得到整個圖。散點圖矩陣會構建兩種基本圖形:直方圖和散點圖。位于對角線位置的直方圖讓我們看到了每一個變量的分布,而對角線上下的散點圖則展示了變量兩兩之間的關系。例如,第二行最左側的散點圖展示了 life_exp 和 year 之間的關系。
默認的散點圖矩陣通常能夠提供有價值的洞見。我們可以看到 life-exp 和 gdp_per_cap 是正相關的,這表明較高收入國家的國民要活得更久一些(盡管這并不能表明二者存在因果關系)。令人欣慰的是,這也顯示出世界范圍內的人口壽命隨著時間逐漸增長。我們可以從直方圖中了解到人口和 GDP 變量呈嚴重右偏態分布。為了在以后的圖中更好地展示這些變量,我們可以通過對列數值取對數來進行列變換:
- # Take the log of population and gdp_per_capita
- df['log_pop'] = np.log10(df['pop'])
- df['log_gdp_per_cap'] = np.log10(df['gdp_per_cap'])
- # Drop the non-transformed columns
- df = df.drop(columns = ['pop', 'gdp_per_cap'])
盡管這一張圖在分析中就很有用,然而我們發現基于類別變量(例如洲)對圖進行著色能夠讓它更有價值。這在 seaborn 中也是極其簡單的。我們唯一要做的就是在調用 sns.pairplot 函數的時候使用關鍵詞 hue。
- sns.pairplot(df, hue = 'continent')

現在我們發現大洋洲和歐洲趨向于擁有***的期望壽命,而亞洲擁有最多的人口量。注意我們對人口和 GDP 的對數變換使得這些變量呈正態分布,這提供了一個關于這些變量更加全面的表征。
這張圖具有更多的信息,但是還存在一些問題:正如對角線上看到的一樣,我認為堆疊的直方圖可解釋性不是很好。展示來自多類別的單變量分布的一個更好方法就是密度圖(density plot)。我們可以通過調用函數將直方圖變成密度圖。向散點圖輸入一些關鍵詞,改變點的透明度、大小和邊緣顏色。
- # Create a pair plot colored by continent with a density plot of the # diagonal and format the scatter plots.
- sns.pairplot(df, hue = 'continent', diag_kind = 'kde',
- plot_kws = {'alpha': 0.6, 's': 80, 'edgecolor': 'k'},
- size = 4)

對角線上的密度圖使得對比洲之間的分布相對于堆疊的直方圖更加容易。改變散點圖的透明度增加了圖的可讀性,因為這些圖存在相當多的重疊(ovelapping)。
現在是默認散點圖矩陣的***一個例子。為減少復雜度,我們僅畫出 2000 年以后的數據。我們仍舊把洲著色,但是不畫出「年」這一列。為了限制畫出的列的數量,我們給函數傳遞了一個 vars 列表。為了更好的闡明這個圖,我們還加上了標題。
- # Plot colored by continent for years 2000-2007
- sns.pairplot(df[df['year'] >= 2000],
- vars = ['life_exp', 'log_pop', 'log_gdp_per_cap'],
- hue = 'continent', diag_kind = 'kde',
- plot_kws = {'alpha': 0.6, 's': 80, 'edgecolor': 'k'},
- size = 4);
- # Title
- plt.suptitle('Pair Plot of Socioeconomic Data for 2000-2007',
- size = 28);

現在開始變得相當好看了!如果繼續建模,我們可能會使用這些圖中的信息指導我們的選擇。例如,我們知道 log_gdp_per_cap 與 life_exp 是成正相關的,所以我們會創建一個線性模型來量化這種關系。本文主要集中在畫圖上面,如果希望更多地探索數據,我們可以使用 PairGrid 類定制散點圖。
使用 PairGrid 的定制化
與 sns.pairplot 函數相反,sns.PairGrid 是一個類,這意味著它不能自動填充圖。我們創建一個類實例,然后為網格的不同部分匹配特定的函數。為了給數據創建 PairGrid 實例,我們使用了以下的代碼,這也限制了我們所展示的變量:
- # Create an instance of the PairGrid class.
- grid = sns.PairGrid(data= df_log[df_log['year'] == 2007],
- vars = ['life_exp', 'log_pop',
- 'log_gdp_per_cap'], size = 4)
如果我們要顯示內容的話,則會得到一個空圖,因為我們還沒有為網格部分匹配任何函數。一個 PairGrid 需要填充三個網格部分:上三角、下三角和對角線。為了給這些部分匹配圖,我們使用在這一部分使用 grid.map 方法。例如,為了給上三角匹配一個散點圖,我們使用:
- # Map a scatter plot to the upper triangle
- grid = grid.map_upper(plt.scatter, color = 'darkred')
map_upper 方法采用任意接受兩個變量數組的函數(例如 plt.scatter),以及相關的關鍵詞(例如 color)。map_lower 方法幾乎與其相同,但是它填充的是網格的下三角。map_diag 與這兩者稍有不同,因為它采用接受單個數組的函數(回想一下,對角線只顯示單個變量)。一個例子是 plt.hist,我們使用它來填充對角線部分:
- # Map a histogram to the diagonal
- grid = grid.map_diag(plt.hist, bins = 10, color = 'darkred',
- edgecolor = 'k')
- # Map a density plot to the lower triangle
- grid = grid.map_lower(sns.kdeplot, cmap = 'Reds')
在這個例子中,我們在下三角中使用二維核密度估計(即密度圖)。將上面的內容合在一起,這段代碼就會給出下圖:

當我們想要創建自定義函數將不同的信息匹配到該圖時,使用 PairGrid 類的實際好處就會顯露出來。例如,我可能希望在散點圖上增加兩個變量的皮爾遜相關系數。為了做到這一點,我會寫一個使用兩個數組的函數,用它來計算統計數據,然后畫在圖上。下面的代碼展示的就是如何做到這件事(來源:https://stackoverflow.com/questions/30942577/seaborn-correlation-coefficient-on-pairgrid)。
- # Function to calculate correlation coefficient between two arrays
- def corr(x, y, **kwargs):
- # Calculate the value
- coef = np.corrcoef(x, y)[0][1]
- # Make the label
- label = r'$\rho$ = ' + str(round(coef, 2))
- # Add the label to the plot
- ax = plt.gca()
- ax.annotate(label, xy = (0.2, 0.95), size = 20, xycoords = ax.transAxes)
- # Create a pair grid instance
- grid = sns.PairGrid(data= df[df['year'] == 2007],
- vars = ['life_exp', 'log_pop', 'log_gdp_per_cap'], size = 4)
- # Map the plots to the locations
- grid = grid.map_upper(plt.scatter, color = 'darkred')
- grid = grid.map_upper(corr)
- grid = grid.map_lower(sns.kdeplot, cmap = 'Reds')
- grid = grid.map_diag(plt.hist, bins = 10, edgecolor = 'k', color = 'darkred');
我們的新函數被匹配到上三角中了,因為我們需要兩個數組來計算相關系數(還要注意到,我們可以將多個函數匹配到網格部分中)。這樣就得到了下圖:

現在相關系數已經出現在上面的散點圖上了。這是一個比較直接的例子,但是我們可以使用 PairGrid 映射任何一個我們想要映射到圖上的函數。我們可以按照需要增加相關的信息,這可以幫助我們解決如何寫這個函數的問題!***一個例子,下圖對角線上展示了總結統計信息:

雖然還需要一些整理,但是它展示了一個通用的思想:除了使用庫中現有的函數將數據映射到圖上,例如 matplotlib,我們可以寫自己的函數來展示自定義信息。
總結
散點圖矩陣(pairs plots)是一款強大的工具,可以快速探索數據集中的分布和關系。為了讓散點圖矩陣可定制、可擴展,Seaborn 通過 Pair Grid 類提供了一個簡單的默認方法。在數據分析項目中,大部分的價值通常不是來自于酷炫的機器學習,而是來自對數據的直接可視化。散點圖矩陣給我們提供了對數據的概覽,是數據分析項目很棒的起點。