數據可視化入門

數據可視化入門

來自專欄 Data Science4 人贊了文章

前言

數據可視化是探索性數據分析(EDA)中非常重要的環節,能直觀反映出數據間的關係。

本文是關於數據嗨客的數據可視化課程的筆記。

本文需要對Python的語法有基本的了解。並且具體講解了著名的Python繪圖第三方包:Matplotlib和Seaborn。


數據可視化基礎

數據可視化初探

  • 為什麼要進行數據可視化

存儲的大部分數據都是原始數據,它們一般價值極低,需要我們從中提取出信息後,才能發掘其中的價值。

人類在執行視覺搜索時需要消耗大量腦部資源,通過數據表格難以快速發現其中包含的數據關係。而通過可視化則能讓我們快速獲取信息甚至迅速傳遞關鍵信息。

  • 數據的基礎知識

傳統數據可大致分成兩類:數值型(Numerical,又叫Quantitative)和類別型(Categorical,又叫Qualitative)。

數值型可分為連續型(Continuous)和離散型(Discrete)。

類別型可分為打擊型(Ordinal)和名義型(Nominal)。

數據類型的組織結構圖

新型數據的分類多種多樣,無法給出統一的指標。分類包括但不限於:空間數據(如GPS坐標),關係數據(如人脈關係),文本數據(如搜索記錄)等。

  • 數據的維度

傳統數據都是以表格的形式出現的。

表格的每一行可以叫記錄(Record)。

表格的每一列可以叫屬性或特徵(Attribute或者Feature)。

我們應該避免使用高維數據,這不僅會給可視化增加難度,而且還會失去焦點,甚至對以後做機器學習的效果也會產生巨大影響。

傳統可視化圖表類型

  • 條形圖與直方圖(Bar Chart and Histogram)

共同點:條形圖和直方圖的y軸都表示頻率(即有多少)。

不同點:條形圖的x軸可以是離散數值型或者是類別型;直方圖的x軸為連續數值型。

條形圖與直方圖的比較

條形圖與直方圖能最快展示數據分布是否均勻。在很多情況下,我們首先要確定數據的分布,因為很多後續的演算法或模型都要求我們的數據有一定的標準分布(如高斯分布)。

條形圖與直方圖也可以檢測出離群值(Outlier)。也就是說若某一個數據分布得特別遠,那麼在直方圖上就會有一個很小的方塊出現在很遠的位置。

  • 餅圖(Pie Chart)

餅圖實際上是條形圖的一個變種,這種變種能很好地展示各個分類變數所佔總體的份數。

餅圖中圖形的面積對應該分類中頻數佔總頻數的百分比。

餅圖舉例

空心餅圖(Donut Chart)是餅圖的經典變中。

空心餅圖舉例

使用餅圖要注意餅圖的部分不宜過多,每一部分的比例也不宜過少。

過多使用3D特性可能或造成負面效果。

  • 散點圖和氣泡圖(Scatter Plot and Bubble Chart)

散點圖用來描述兩個連續數值的特徵(或應保證至少有一個特徵是連續數值型),例如身高和體重。

散點圖能很好地表示出兩個變數之間的關係,也具有檢測離群值的功能。

散點圖舉例

氣泡圖是散點圖的一個變種,除了x,y軸的兩個特徵外,它還能展示第三個連續型數值的特徵。氣泡的大小反映了這個特徵的大小。

氣泡圖舉例

氣泡圖中的點不宜過多,且應較為分散,否則大的氣泡將會覆蓋小的氣泡。

  • 箱線圖(Box Plot)

箱線圖又稱盒圖,用來展示一個連續數值特徵的分布。

箱線圖舉例

圖中的「盒」(即,圖中方形)的位置由數據中的三個統計量決定:第一四分位數(又稱下四分位數)、中位數、和第三四分位數(又稱上四分位數)。盒子的下邊緣代表的是第一四分位數,中間的黑線代表中位數,盒子的上邊緣代表第三四分位數。

如果當數據集不能整分的時候,分位數就會變成小數,此時的計算比較麻煩,我們就不細談了。第三四分位數和第一四分位數中間的差,稱為四分位間距(Interquartile Range, IQR)。第三四分位數加上IQR的1.5倍定義了上極限,任何大於這個極限的數在箱線圖中將被以點的形式標出來,稱作離群值(例如最上方的點15)。同理,第一四分位數減去IQR的1.5倍定義了下極限,任何小於這個極限的數在箱線圖中也會被以點的形式標識出來。

最後,箱線圖中最上面的一條線和最下面的一條線代表最大但不超過上極限和最小但不低於下極限的兩個數據,這兩個線我們稱作最大可觀測值和最小可觀測值。

  • 核密度估計(Kernel Density Estimation)

核密度估計圖的「估計」代表估計一個隨機變數的值。一般情況下,當我們已知全部數據的時候不會用核密度估計。

只有當我們無法得到所研究對象的所有樣本時,才會用核密度估計。

舉一個最簡單的例子,假如我們手裡有一個密度不均的骰子,然後隨機擲了100次,結果發現出現3的次數最多,其次是2,4,然後是1,5,6。這種情況下我們會考慮使用核密度估計圖。

密度圖舉例

要注意,在核密度估計圖中的y軸不代表頻數(有多少),而是代表x等於該值的可能性。

最後,我們再簡單了解下概率密度函數(Probability Density Function, PDF)。在概率密度函數中,這條曲線我們可以用一個方程f(x)來表示,因而我們可以通過這個方程求的任意x值發生的概率是多少。同理概率密度函數的曲線與x軸圍成的面積也為1。

複雜可視化圖表類型

  • 平行坐標圖(Parallel Coordinates Plot)

平行坐標圖可用來描繪數據在每個特徵之間的變化走向,且不限於數據的類型。

鳶尾花數據集平行坐標圖

  • 詞雲(Word Cloud)

詞雲主要是用字體大小來表示文字文檔中各個詞出現的頻率。

詞雲舉例

  • 和弦圖(Chord Diagram)

和弦圖主要用來表現兩兩之間的關係。適用於和弦圖的數據集大部分為表示關係的矩陣。

假設我們有如下關係。學生AB之間每天有10條信息往來,學生AC之間每天有10條信息往來,學生BC之間每天也有10條信息往來。那麼此關係的和弦圖表示如下:

和弦圖舉例

  • 其他

更多的可視化方案可參考這個網站。

格式塔視覺原理

說到可視化設計,我們就不得不先學習一下視覺的感知與認知,這就引出了著名的格式塔原理(Gestalt Laws of Grouping),又叫做完形原理(Principles of Grouping)。格式塔原理是由一群德國心理學家提出的。

格式塔原理其核心理論為:人們總是先看到整體然後再去關注局部,人們對事物的整體感受不等於局部感受的加和

視覺感知

人類對於不同的視覺感知存在著差異。因此,在設計可視化的時候,同樣的數據採用不同的可視化方案,給人的感知是不一樣的。因此,不恰當的選擇可視化方案可能帶來負面的影響。

人類對不同視覺元素的刺激會有不用的準確度

  • 數墨比(Data-ink Ratio)和 圖表垃圾(Chart Junk)

DataInk Ratio=frac{	ext{描繪數據所使用的油墨量}}{	ext{描繪所有其他信息的油墨}}

數墨比越高就代表可視化越精鍊,也就是說,可視化中所使用的大部分油墨,都用來描繪數據點本身,而沒有用在其他無用的地方。反之,數墨比越低,就代表大部分油墨都用在其他地方,而用在描繪數據上面的就很少。

因此,對於可視化方案,我們希望得到儘可能高的數墨比。

可視化軟體介紹

  • Tableau
  • SAS
  • Excel
  • Python
  • D3
  • R

Matplotlib入門

圖形基礎知識

  • 圖形的組成

以下是一個採用Matplotlib繪製的基本繪圖元素示意圖:

圖形的基本組成元素介紹

  • 圖形的層次

繪圖框與子圖的概念,一個繪圖框可以包含多個子圖

Matplotlib的層次結構

Matplotlib包括三個層次:

Matplotlib的三個層次

  • 後台層

負責用戶交互,保證在不同操作系統,平台,環境下軟體包均可以正常運行,並將最後的圖形正確的顯示出來。

  • 藝術家層

產生畫圖所需的所有元素,並將它們組合起來。

  • 腳本層

負責協調後台層和藝術家層,讓我們能用簡單的編程語言與其他兩層交流。

Matplotlib的層次結構示意圖

基本作圖

  • 導入必要的模塊

import matplotlib.pyplot as pltimport pandas as pdimport numpy as np

  • 明確圖表類型

在實際作圖過程中,首先要明確圖表的類型。

幾個常用繪圖函數的總結

常用繪圖參數的可選值及其意義一覽表

自定義圖表樣式——添加文字

在明確圖表類型後,需對圖表樣式進行修飾,比如對圖表添加一些相關的文字。

以一個例子作為參考:

# 載入相關包import matplotlib.pyplot as pltimport pandas as pdimport numpy as np# 魔法命令(Jupyter Notebook)%matplotlib inline# 創建sin函數的x,y坐標Fs = 360sample = 360x = np.arange(sample)y = np.sin(2 * np.pi * x / Fs)# 由於共有360個點,因此看上去像連續的曲線plt.plot(x, y)

接下來,需要添加一些文字,包括標題,x軸標籤,y軸標籤,自定義文字和圖例。

添加標題:plt.title()

添加x軸標籤:plt.xlabel()

添加y軸標籤:plt.ylabel()

添加自定義文字:plt.text()【前兩個參數為自定義文字第一個字元的坐標】

添加圖例:plt.legend()【用 labels 來添加圖例的標籤】

我們還可以調整文字的大小和顏色等。顏色的參數和之前一樣為 color,標題和圖例的位置都用 loc 表示。標題的常用位置是(left,center,right),圖例的常用位置是(upper left,center,upper right,lower left, lower center,lower right)。透明度是 alpha,字體大小是 fontsize,軸標籤與軸的距離可以用 labelpad 參數調整。

plt.plot(x, y)plt.title(This is a title, fontsize=24, loc=right)plt.xlabel(Red x label, fontsize=16, color=red, labelpad=50)plt.ylabel(Blue y label, color=blue, alpha=0.2)plt.text(200, 0, f(x)=sin(x), fontsize=18)plt.legend(labels=[sin function], fontsize=18, loc=lower left)

自定義圖表樣式——調整坐標軸

當給圖表添加文字後,有時需要修改坐標軸,即是添加主、副刻度網格,修改最大值和最小值等。

x軸修改最大(小)值:plt.xlim()【如:plt.xlim(0, 100)】

y軸修改最大(小)值:plt.ylim()【如:plt.ylim(-1, 2)】

讓坐標軸(x軸)以log的形式顯示:plt.xscale(log)

讓坐標軸(y軸)以log的形式顯示:plt.yscale(log)

添加主、副刻度網格:plt.grid()【只傳入參數 True 則採用默認網格;which參數填寫要添加的網格種類】

# 載入相關包import matplotlib.pyplot as pltimport pandas as pdimport numpy as np# 魔法命令(Jupyter Notebook)%matplotlib inline# 創建sin函數的x,y坐標Fs = 360sample = 360x = np.arange(sample)y = np.sin(2 * np.pi * x / Fs)plt.plot(x, y, --, color=r)plt.grid(which=major, linewidth=2, linestyle=--)

plt.plot(x, y, --, color=r)plt.grid(which=minor, linewidth=0.3, linestyle=:, color=b)# 需要這個開啟副刻度顯示選項才能顯示副刻度plt.minorticks_on()

我們可以通過 xticks() 來修改主刻度顯示範圍。對應的也有 yticks()。。注意這兩個參數接受的是ndarray的結構,因此需要導入numpy包來創建ndarray。我們可以使用 np.array() 來創建顯示範圍,或使用 np.arange() 來指定大小值及其間距來創建顯示範圍。注意在使用 np.arange() 創建顯示範圍時,設定的最大值要稍大於需要顯示的最大值,否則最後一個值將無法顯示。

plt.plot(x, y, -., color=r)plt.xticks(np.array([0, 60, 120, 180, 240, 300, 360]))plt.yticks(np.arange(min(y), max(y)+0.1, 0.5))

通過一個例子創建自定義x軸和y軸的主刻度標籤:

plt.plot(x, y, -., color=r)# 自定義主刻度標籤x_label = [A, B, C, D, E, F, G]y_label = [first, second, third, fourth, fifth]plt.xticks(np.array([0, 60, 120, 180, 240, 300, 360]), x_label, rotation=vertical)plt.yticks(np.array([0.1, 0.2, 0.3, 0.6, 0.9]), y_label)

最後,plt.tick_params() 函數可以調節主刻度標籤大小,主刻度長短,坐標軸標籤顏色等。使用 axis 參數來指定要修改的坐標軸,color 為主刻度顏色,size 為主刻度長度,labelcolor 為主刻度標籤顏色,labelsize 為主刻度標籤大小,如圖所示:

plt.plot(x, y, :, color=r)plt.tick_params(axis=x, color=green, size=30)plt.tick_params(axis=y, labelcolor=red, labelsize=15)

自定義圖表樣式——繪製子圖

通過一個例子來說明:

plt.subplot(1,2,1) # 一行兩列第一個子圖plt.plot(x, y)plt.title(sin 三角函數(1))plt.xlabel(x軸)plt.ylabel(y軸)plt.text(200, 0, f(x)=sin(x))plt.legend(labels=[sin function])plt.subplot(1,2,2) # 一行兩列第二個子圖plt.scatter(x, y)plt.title(sin 三角函數(2))plt.xlabel(x軸)plt.ylabel(y軸)plt.text(200, 0, f(x)=sin(x))plt.legend(labels=[sin function])# 自動調整子圖間的間距plt.tight_layout()# 添加繪圖框標題plt.suptitle(Title, fontsize=16, color=r, alpha=0.5, x=0.55, y=1.05) # x和y參數表示繪圖框標題的位移# 上調繪圖標題# plt.subplots_adjust(top=0.8)plt.show()

自定義樣式表——總結

編程風格

  • pyplot

之前所有的繪圖代碼都調用了 matplotlib.pyplot 模塊繪製圖形,這是最簡便快速生成圖形的一種方式。

  • 面向對象

對象實例:

1)FigureCanvas:Figure對象的容器。

2)Figure:多個Axes對象實例的容器。

3)Axes:包含圖形基本元素(如文本,直線,曲線等)的矩形區域。

FigureCanvas,Figure,Axes之間的關係

實質上,pyplot 模塊的函數也就是對 Axes 對象相應函數的再次封裝。

  • 混合的編程風格

實際中常將上面兩種風格進行混合使用。

函數 plt.gcf():返回 Figure對象

函數 plt.gca();figure.gca():返回 AxesSubPlot對象

x = np.arange(0,10,0.5)y1 = x**3y2 = 10*x + 5fig, axes_list = plt.subplots(1,2) # 一行兩列axes_list[0].plot(x, y1, r--)axes_list[0].set_title(y1)axes_list[1].plot(x, y2, b^)axes_list[1].set_title(y2)fig.tight_layout()


Seaborn入門

導入必要的模塊

import matplotlib.pyplot as pltimport pandas as pdimport numpy as npimport seaborn as sns

sns 是Seaborn的別名,這個別名已在國際上達成共識,建議不要修改。Seaborn主要是運用在數據探索性分析(Exploratory Data Analysis, EDA)過程中。

可視化單一特徵

  • 連續型特徵

連續型特徵的繪圖函數包括:

sns.displot():直方圖

【kde:可選,布爾值,是否顯示核密度估計圖,默認為 True;

rug:可選,布爾值,是否顯示頻率線條,默認為False;

bins:可選,整數值,設置直方圖的條形個數;

hist:可選,布爾值,是否顯示直方圖,默認為True。】

sns.distplot(house[sqft_living])# sns.distplot(house[sqft_living], bins=50, kde=False)# sns.distplot(house[sqft_living], rug=True, hist=False)

sns.kdeplot():核密度圖

【shade:可選,添加陰影,默認為 False;

bw:可選,控制核密度估計與真實數據的擬合度,有點像剛才直方圖中的 bins 參數;

label:可選,圖例的標籤。】

sns.kdeplot(house[sqft_living], shade=True, bw=30, label=Living Area)

sns.boxplot():箱線圖

【width:可選,改變箱線圖的寬度。】

# sns.boxplot(x=price, data=house)sns.boxplot(y=price, data=house, width=0.2)

sns.violinplot():小提琴圖和箱線圖十分類似。在小提琴圖中,圖形越寬的地方代表數據越密集。這樣的好處是可以觀察數據的分布情況。有的數據是分布在兩頭,即有兩個高峰。這種情況只有通過小提琴圖才可以看出來,一般的箱線圖則無法展示這個信息。

sns.violinplot(x=price, data=house)

sns.stripplot():將所有數據畫在一個直線上。

【jitter:可選,可設置參數 jitter=True 對數據添加雜訊。】

sns.stripplot(x=price, data=house)sns.stripplot(x=price,data=house, jitter=True) # 加入雜訊

  • 類別型或離散型特徵

類別型或離散型特徵的繪圖函數包括:

sns.barplot():條形圖

condition = house[condition].value_counts().index.tolist()count = house[condition].value_counts().values.tolist()sns.barplot(x=condition, y=count)

sns.countplot():計數圖,可自動統計矩形條的高度,是 barplot 的簡便版本。

【oder:可選,修改條形顯示的順序;

saturation:可選,修改顏色的飽和度;

alpha:調整顏色的透明度。】

sns.countplot(x=condition, data=house, order=[3, 4, 5, 1, 2], saturation=0.3, alpha=0.5)

可視化兩個特徵

  • 兩個連續型特徵

繪製兩個連續性特徵一般會使用散點圖,散點圖在Seaborn中為函數 sns.jointplot() 。這個函數支持直接輸入數據框,例如:

# sns.jointplot(x=sqft_living, y=price, data=house)sns.jointplot(x=sqft_living, y=price, data=house, kind=kde, color=m)

  • 單個類別型和單個數值型特徵

sns.boxplot();sns.violinplot();sns.stripplot();sns.barplot()等。

類似的,在上一節單個連續型特徵在添加一個參數,該參數是另一個特徵的數據。例如:

sns.boxplot(x=condition, y=price, data=house)sns.violinplot(x=condition, y=price, data=house)sns.stripplot(x=condition, y=price, data=house)sns.stripplot(x=condition, y=price, data=house, jitter=True)sns.barplot(x=condition, y=price, data=house)

  • 兩個類別型特徵

sns.countplot() 例如:

sns.countplot(y=grade, hue=condition, data=house)

可視化多個特徵

  • 對先有函數添加新參數 hue

在sns.boxplot();sns.violinplot();sns.stripplot();sns.barplot()中,可以添加一個新參數 hue 。它可以用來在圖上添加第三個類別型特徵。但是要注意這第三個類別型特徵的類別不易過多,一般2-3個即可。

  • 使用新函數

sns.factorplot() 可以用來繪製多個特徵,且他的工作原理不同於 hue 參數。sns.factorplot() 里有一個可選參數 kind ,用來控制第三個特徵樣式(其可選值為:point,bar,count,box,violin和strip)。第二個可選參數 col(或者 row)可以根據指定的第四(或者第五)個特徵來橫向(豎向)拆分圖形。

因此可使用 sns.factorplot() 函數畫出4個特徵(不建議畫5個特徵)。

sns.factorplot(x=grade, y=price, hue=condition, data=data)sns.factorplot(x=grade, y=price, hue=condition, data=data, kind=bar)# 等價於:sns.barplot(x=grade, y=price, hue=condition, data=data)sns.factorplot(x=grade, y=price,hue=waterfront, col=condition, data=data)

sns.heatmap():熱力圖。

sns.clustermap():聚類熱力圖。

sns.pairplot():這個函數的使用十分廣泛,且經常作為可視化及數據處理的第一步。它能給我們展示所有特徵倆倆之間的關係,從而能輕鬆發現有用特徵、數據中的異常值和相關性等。這種圖稱為散點圖矩陣。例如:

data = house[[price, sqft_living, condition, bedrooms]]sns.pairplot(data)

sns.pairplot(data=house, vars=[price, sqft_living, bedrooms], hue=condition, markers=+)

繪製子圖

在上方的散點圖矩陣和聚類熱力圖中,Seaborn採用了子圖矩陣的形式來展現更多的特徵。因此我們可以結合之前Matplotlib的方式自己繪製子圖。例如:

# 創建2乘2子圖矩陣繪圖框fig, ax = plt.subplots(nrows=2,ncols=2,figsize=(10,5))plt.suptitle(Subplots in Seaborn, y=1.03)# 繪製子圖sns.distplot(house[price], kde=False, ax=ax[0][0])sns.distplot(house[sqft_living], kde=False, ax=ax[0][1])sns.countplot(house[grade], ax=ax[1][0])sns.boxplot(house[sqft_lot], ax=ax[1][1])# 調整子圖間距plt.tight_layout

圖像的調整與美化

  • 設置全局美化模版

sns.set()

【context:使用情景。如果需要圖形列印在紙上或顯示在ppt里,可以修改該參數。允許值有 notebook,paper,talk和poster;

style : 設置坐標軸的相關美學元素。模版有 darkgrid,whitegrid,dark,white 和 ticks;

palette : 設置主題(調色盤)。這裡可以自動配置圖的顏色,模版有 deep,muted,bright,pastel,dark 和 colorblind;

font : 設置全局字體樣式。具體可用字體請參考Matplotlib字體;

font_scale : 快速調節全局文字大小;

rc : 字典類型,對圖像進行深度調節。】

sns.set(palette=pastel, font=fantasy, font_scale=1.6)sns.set(rc={"axes.labelsize":20, "xtick.major.size":10, "axes.titlesize":30, "ytick.labelsize":30, "lines.linewidth":3})

sns.reset_defualts():將sns.set()函數的參數重置。

  • 設置當前美化模板

最後說說如何設置只對當前繪圖有效的圖形模板。其實這個很簡單,只需使用 with 開頭即可。但Seaborn官方文檔中只提到 sns.color_palette() 函數支持 with 開頭,並不是所有的函數都支持這種寫法。

sns.color_palette() 函數是Seaborn調色板。

with sns.color_palette(PuBuGn_d): sns.countplot(x=condition)

  • 坐標軸小工具

sns.despine()

sns.despine(top=True, right=True, left=True, bottom=False)

sns.despine() 函數不會影響後面的繪圖。

Seaborn高級對象

  • FacetGrid

# 導入數據draft_score = pd.read_csv(./input/draft_data.csv)# 選擇數據draft_4_yr = draft_score[draft_score[Draft_Yr] >= 2014]# FacetGrid高級對象用法舉例g = sns.FacetGrid(draft_4_yr, col=Draft_Yr)# g.map(sns.distplot, PTS_per_G)g.map(sns.regplot,PTS_per_G, TRB_per_G)

  • PariGrid

PairGrid主要用來可視化數據集變數之間的關係。根據給定的數據集變數,PairGrid可以輸出一系列子圖,也就是Axes對象實例矩陣。對於矩陣的每一行Axes對象,它們共享同一個Y軸;對於矩陣的每一行Axes對象,它們共享同一個X軸。

# 下列代碼輸出的是一個只有網格背景的空圖矩陣,我們需要給每張子圖加上具體的圖案。g = sns.PairGrid(draft_4_yr[[MP_per_G,PTS_per_G,Draft_Yr]], hue=Draft_Yr, size=2.5, aspect=1.5)# 使用 map()函數為子圖繪製線條g = g.map_diag(plt.hist)g = g.map_offdiag(plt.scatter)g = g.add_legend()

  • JointGrid

g = sns.JointGrid(x=FG_perc, y=3P_Perc, data=draft_4_yr)g.plot(plt.regplot, sns.distplot)


推薦閱讀:

如何定義一個數據分析問題
要相信功不唐捐
【翻譯】《利用Python進行數據分析·第2版》第11章(中) 時間序列
數據分析入門一(某化妝品公司銷量數據分析)
四月將至,B站這些番值得回味

TAG:數據可視化 | 數據分析 | 數據科學 |