眾所周知亿胸,Seaborn“可能”是Python下最友好汹想、易用的可視化工具了,可視化效果也非常好腺律。但是截止目前奕短,并沒有一份中文教程供廣大國內Python使用者查閱學習宜肉。怎么能因為語言的問題,讓大家錯過這么好用的一個可視化工具呢翎碑?
思考再三谬返,我決定花一些時間將官方的英文文檔整理出來,為大家提供一份最權威的中文教程日杈∏猜粒考慮到我的時間比較碎片化,這項工作可能會在未來的幾周內完成莉擒,感興趣的朋友可以先關注和收藏下酿炸,后續(xù)的更新會在簡書和我的個人博客中(www.data-insights.cn/www.data-insight.cn)發(fā)布。
今天涨冀,我整理了第一部分:如何用Seaborn可視化變量之間的關系填硕,這一部分非常實用,基本上學習后馬上就能給我們帶來很多幫助鹿鳖。那么接下來扁眯,就開始我們的學習吧!
文章較長翅帜,建議收藏后在PC站或者我的個人博客閱讀姻檀。
統(tǒng)計分析是這樣的一個過程:嘗試去理解一個數(shù)據(jù)集中變量之間的關系,以及這些關系如何受到其他變量的影響涝滴⌒灏妫可視化是這個過程的核心元素,因為當數(shù)據(jù)以非常恰當?shù)姆绞秸故境鰜頃r狭莱,我們可以非常直觀地觀察到某些趨勢或者模式僵娃,而這些,就揭示了變量之間的關系腋妙。
在這篇教程中,我們會討論三個seaborn
函數(shù)讯榕。我們用的最多的是relplot()
骤素。這是一個圖形級別的函數(shù),它用散點圖和線圖兩種常用的手段來表現(xiàn)統(tǒng)計關系愚屁。relplot()
使用兩個坐標軸級別的函數(shù)來結合了FacetGrid
:
-
scatterplot()
:(使用kind="scatter"
济竹,這是默認參數(shù)) -
lineplot()
:(使用`kind="line")
正如我們所見,這些函數(shù)非常直白霎槐,因為它們用了簡單易懂的展示方式送浊,卻可以呈現(xiàn)復雜的數(shù)據(jù)集結構。這是因為通過hue
丘跌、size
袭景、style
參數(shù)唁桩,我們可以在二維圖形中表現(xiàn)出更多的變量(除了x軸和y軸的兩個,還可以通過不同的方式額外展示3個變量)耸棒。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
sns.set(style="darkgrid")
一荒澡、用散點圖展示相關變量
散點圖是統(tǒng)計圖形中的中流砥柱。它用一系列的散點將兩個變量的聯(lián)合分布描繪出來与殃,其中每個點就是一個觀測樣本单山。這種描述方式可以讓我們從視覺上推斷出大量信息,來判斷兩個變量之間是否存在某種有意義的關系幅疼。
在seaborn
中米奸,我們有數(shù)種方法可以實現(xiàn)散點圖的繪制。最基本的一種適用于兩個變量都是數(shù)值型變量的情況爽篷,它就是scatterplot()
悴晰。在分類可視化教程中,我們會看到如何繪制分類數(shù)據(jù)的散點圖狼忱。relplot()
的默認類型(kind
)就是scatterplot()
(當然膨疏,我們也可以強制指定參數(shù)kind="scatter"
,這和不指定這一參數(shù)時效果是一樣的)钻弄。
tips = sns.load_dataset("tips")
sns.relplot(x="total_bill", y="tip", data=tips);
當我們已經將散點繪制在二維的平面上時佃却,我們還可以根據(jù)第三個變量來對這些點施以不同的顏色,從而引入一個新的維度窘俺。在seaborn
中饲帅,我們用hue
參數(shù)實現(xiàn)了這種想法,因為點的顏色是有意義的瘤泪。
sns.relplot(x="total_bill", y="tip", hue="smoker", data=tips);
如果我們想要強調不同分類之間的差異灶泵,同時增加易讀性,我們可以對不同的分類使用不同的標記樣式:
sns.relplot(x="total_bill", y="tip", hue="smoker", style="smoker", data=tips);
我們也可以同時展示四個變量对途,只需要將hue
和style
參數(shù)單獨調整到不同的分類變量即可赦邻。但是我們要謹慎使用這種方法,因為我們的眼睛對于形狀的敏感性遠遠不如對顏色的敏感性实檀。
sns.relplot(x="total_bill", y="tip", hue="smoker", style="time", data=tips);
在上邊這個例子中惶洲,hue
參數(shù)對應的變量是分類型數(shù)據(jù),因此seaborn
自動為它應用了默認的定性(分類)調色板膳犹。如果hue
參數(shù)對應的變量是數(shù)值型的(可轉化為浮點數(shù)的)恬吕,那么默認的顏色也會隨之變?yōu)檫B續(xù)的定量調色板。
sns.relplot(x="total_bill", y="tip", hue="size", data=tips);
上述兩種情況下(分類或連續(xù)數(shù)據(jù))须床,我們都可以自定義我們的調色板铐料。有很多選項可以實現(xiàn)這一目的。下面,我們使用cubehelix_palette()
的字符串接口來定制我們的連續(xù)調色板:
sns.relplot(x="total_bill", y="tip", hue="size",
palette="ch:r=-.5,l=.75", data=tips);
我們還可以使用點的大小來引入第三個額外的變量:
sns.relplot(x="total_bill", y="tip", size="size", data=tips);
與matplotlib.pyplot.scatter()
不同的是钠惩,這里并不是使用原始數(shù)據(jù)中的數(shù)值來為每個點選擇面積大小柒凉,seaborn
將原始數(shù)據(jù)歸一化(正則化)到了某個范圍,這個范圍可以由我們來指定:
sns.relplot(x="total_bill", y="tip", size="size", sizes=(15, 200), data=tips);
更多關于如何定制不同參數(shù)來展示統(tǒng)計關系的示例可以在scatterplot()
的API中找到妻柒。
二扛拨、使用線圖表現(xiàn)連續(xù)性
散點圖很高效,但是沒有哪種可視化類型可以完美應對所有情況举塔。事實上绑警,我們的可視化呈現(xiàn)方式要適應數(shù)據(jù)集的種類以及我們想要通過圖形回答的問題。
在某些數(shù)據(jù)集中央渣,我們可能想要理解某個變量隨著時間的變化規(guī)律计盒,或者想要理解某個連續(xù)型的變量。這種情況下芽丹,線圖會是一個不錯的選擇北启。在seaborn
中,我們可以通過lineplot()
函數(shù)或者使用帶有kind="line"
參數(shù)的relplot()
來實現(xiàn)線圖的繪制拔第。
df = pd.DataFrame(dict(time=np.arange(500), value=np.random.randn(500).cumsum()))
g = sns.relplot(x="time", y="value", kind="line", data=df)
g.fig.autofmt_xdate()
由于lineplot()
假設用戶在大多數(shù)情況下是在嘗試描繪y相對于x的函數(shù)(變化規(guī)律)咕村,因此它在繪制之前會默認先對x做一個排序。不過我們可以禁止它蚊俺。
df = pd.DataFrame(np.random.randn(500, 2).cumsum(axis=0), columns=["x", "y"])
sns.relplot(x="x", y="y", sort=False, kind="line", data=df);
聚合并展示不確定性
在更多復雜的數(shù)據(jù)集中懈涛,會出現(xiàn)一個x軸變量對應了多個觀測值(y)的情況。seaborn
會默認將多個觀測值聚合起來泳猬,并且將它們的均值以及95%的置信區(qū)間展示出來:
fmri = sns.load_dataset("fmri")
sns.relplot(x="timepoint", y="signal", kind="line", data=fmri);
置信區(qū)間是通過自助采樣法(bootstrapping)計算的批钠,這在遇到大型數(shù)據(jù)集時可以幫助我們節(jié)省時間。當然得封,我們也可以禁止它埋心。
sns.relplot(x="timepoint", y="signal", ci=None, kind="line", data=fmri);
另一個不錯的選擇是,我們可以用標準差替代置信區(qū)間來展示每個時間點下觀測值的分布忙上,當數(shù)據(jù)集比較大時這一選擇尤其明智拷呆。
sns.relplot(x="timepoint", y="signal", kind="line", ci="sd", data=fmri);
如果想要關閉所有的聚合操作,我們可以設置estimator=None
疫粥。不過當同一時間點存在多個觀測值時洋腮,我們的圖會看起來有些奇怪。
sns.relplot(x="timepoint", y="signal", estimator=None, kind="line", data=fmri);
通過參數(shù)映射可視化數(shù)據(jù)子集
lineplot()
與scatterplot()
一樣具有很強的靈活性:它也可以通過hue
手形、size
和style
參數(shù)來展示額外的三個變量。它和scatterplot()
使用了相同的API悯恍,因此我們不需要停下來絞盡腦汁地思考哪些參數(shù)是用來控制線條库糠、哪些參數(shù)是用來控制散點。
使用不同的參數(shù)會決定我們的數(shù)據(jù)如何聚合。比如瞬欧,增加一個具有兩個水平的分類變量作為hue
參數(shù)贷屎,會將我們的圖形分為兩條線以及兩個誤差帶,并分別施以不同的顏色來區(qū)分數(shù)據(jù)的分類歸屬艘虎。
sns.relplot(x="timepoint", y="signal", hue="event",
kind="line", data=fmri);
我們可以增加一個style
參數(shù)唉侄,以不同的線條樣式來展示不同的分類:
sns.relplot(x="timepoint", y="signal", hue="region",
style="event", kind="line", data=fmri);
我們還可以設置不同分類的標記樣式,標記樣式既可以和線條樣式同時設置野建,也可以各自單獨設置属划。
sns.relplot(x="timepoint", y="signal", hue="region", style="event",
dashes=False, markers=True, kind="line", data=fmri);
跟散點圖一樣,我們要慎重使用這些參數(shù)來展示太多變量候生。有些時候它們會展示豐富的信息同眯,但是有些時候它們會使圖形太過復雜導致我們難以解析和解釋它。然而當你僅打算考慮額外的一個變量時唯鸭,同時修改它們的顏色和樣式會很有幫助须蜗。當需要考慮到色盲人群時,將圖形顏色設置為黑白色調則是一個不錯的選擇目溉。
sns.relplot(x="timepoint", y="signal", hue="event", style="event",
kind="line", data=fmri);
當我們需要應對重復測量的數(shù)據(jù)時明肮,我們可以將不同的抽樣單元(單次實驗觀測到的數(shù)據(jù)系列)分離開來展示,這并不需要我們使用一個語義參數(shù)(hue/style/size
)缭付。后者會導致圖例看起來像一個災難(想象一下幾十個分類的情況):
sns.relplot(x="timepoint", y="signal", hue="region",
units="subject", estimator=None, kind="line",
data=fmri.query("event == 'stim'"));
與scatterplot()
類似柿估,lineplot()
默認的調色板以及圖例處理方式也取決于hue
對應的數(shù)據(jù)是分類型還是連續(xù)數(shù)值型。
dots = sns.load_dataset("dots").query("align == 'dots'")
sns.relplot(x="time", y="firing_rate",
hue="coherence", style="choice",
kind="line", data=dots);
當hue
參數(shù)對應的變量的數(shù)據(jù)是均勻分布在對數(shù)刻度上的(即數(shù)據(jù)分布范圍非常大蛉腌,比如從1到1億)官份,即使是連續(xù)的調色板也無法很好地應對這種情況。但是我們可以使用列表或者字典對每條線指定一個顏色烙丛。
palette = sns.cubehelix_palette(light=.8, n_colors=6)
sns.relplot(x="time", y="firing_rate",
hue="coherence", style="choice",
palette=palette,
kind="line", data=dots);
或者我們可以直接修改調色板數(shù)值的正則方式:
from matplotlib.colors import LogNorm
palette = sns.cubehelix_palette(light=.7, n_colors=6)
sns.relplot(x="time", y="firing_rate", hue="coherence", style="choice",
hue_norm=LogNorm(), kind="line", data=dots);
不要忘了我們還有個size
參數(shù)舅巷,它用來修改線條的寬度:
sns.relplot(x="time", y="firing_rate", size="coherence",
style="choice", kind="line", data=dots);
size
一般接受連續(xù)數(shù)值型變量,但是我們也可以傳入分類型變量河咽。但是要慎重考慮這種做法钠右,因為這樣比“粗線 vs. 細線”的區(qū)分難多了。然而忘蟹,當數(shù)據(jù)具有非常高頻的變異性時飒房,我們使用style
表現(xiàn)的不同線條樣式會很難區(qū)分,這時使用不同的線條寬度就是一個更高效的選擇了:
sns.relplot(x="time", y="firing_rate",
hue="coherence", size="choice",
palette=palette,
kind="line", data=dots);
繪制時間序列數(shù)據(jù)
線圖常用來描繪日期媚值、時間相關的詩句狠毯。這些方法以原始格式傳入更底層的matplotlib函數(shù)中,這樣它們就可以利用matplotlib的能力來格式化日期數(shù)據(jù)褥芒。但是所有的時間格式化過程都是在matplotlib層實現(xiàn)的嚼松,想要知道更多實現(xiàn)的細節(jié)就需要去看一下matplotlib中關于這部分的文檔:
df = pd.DataFrame(dict(time=pd.date_range("2017-1-1", periods=500),
value=np.random.randn(500).cumsum()))
g = sns.relplot(x="time", y="value", kind="line", data=df)
g.fig.autofmt_xdate()
三、用更多子圖展示多重關系
前邊我們已經強調過,雖然我們可以在一張圖中展示數(shù)個不同的語義變量(通過hue/style/size
參數(shù))献酗,但是這么做并不是總會高效寝受。那么當我們真的很想理解在某些額外變量的影響下兩個變量之間的關系有什么不同時怎么辦呢?
最好的辦法就是畫更多的圖罕偎。由于relplot()
是基于FacetGrid
的很澄,因此這很容易做到。當我們想要表現(xiàn)出一個額外變量的影響時颜及,我們可以不用將它賦給前邊提到的語義參數(shù)(hue/style/size
)甩苛,而是用它來將圖形“面板”化。這意味著我們會創(chuàng)建多個坐標軸器予,分別用來繪制不同的子數(shù)據(jù)集:
sns.relplot(x="total_bill", y="tip", hue="smoker", col="time", data=tips);
我們還可以同時使用col
(列)和row
(行)參數(shù)來展示兩個變量的影響浪藻。當我們在圖中增加了更多的變量時(會有更多的子圖),我們可能會想要調整圖形的大小乾翔。要記住在FacetGrid
中爱葵,我們用height
(子圖高度)和aspect
(高寬比)來定制每個子圖的大小:
sns.relplot(x="timepoint", y="signal", hue="subject", col="region",
row="event", height=3, kind="line", estimator=None, data=fmri);
當我們想要檢驗某個具有大量水平的變量的影響時反浓,我們可以將這個變量賦給col
參數(shù)萌丈,同時我們通過col_wrap
參數(shù)設置每行達到多少列就換行:
sns.relplot(x="timepoint", y="signal", hue="event", style="event",
col="subject", col_wrap=5,
height=3, aspect=.75, linewidth=2.5,
kind="line", data=fmri.query("region == 'frontal'"));
這種常被叫做“格子圖”或“small-multiples”的可視化方式,非常高效雷则,因為它們呈現(xiàn)數(shù)據(jù)的方式使得我們很容易同時發(fā)現(xiàn)整體的模式以及不同模式之間的偏差辆雾。當你需要利用scatterplot()
和relplot()
的靈活性來表現(xiàn)更多信息時,一定要記住月劈,多幅簡單的圖通常比一幅復雜的圖更加高效度迂。