許多數(shù)據(jù)集含有多個(gè)定量變量(數(shù)值型變量)锦募,而我們分析的目的往往是將他們關(guān)聯(lián)起來弥激。我們曾討論過通過兩個(gè)變量的聯(lián)合分布來實(shí)現(xiàn)這一點(diǎn)百炬。然而懒豹,使用統(tǒng)計(jì)模型來為兩組帶有噪聲數(shù)據(jù)的觀測值評估出一個(gè)簡單的關(guān)系可以是非常有用的具钥。這一章節(jié)我們討論的函數(shù)將會(huì)在線性回歸的框架下實(shí)現(xiàn)這種預(yù)測滔以。
seaborn
中的回歸圖主要是為了在EDA(探索數(shù)據(jù)分析)階段為發(fā)掘數(shù)據(jù)中存在的規(guī)律提供一些視覺指引,也就是說氓拼,seaborn
本身并非是一個(gè)用于統(tǒng)計(jì)分析的庫你画。想要得到關(guān)于回歸模型擬合效果的一些量化指標(biāo),你需要使用statsmodels
庫桃漾。seaborn
的終極目標(biāo)就是讓我們通過可視化快速坏匪、輕易地探索數(shù)據(jù),畢竟對于探索數(shù)據(jù)來說撬统,可視化的重要性不比得到一個(gè)統(tǒng)計(jì)表格低适滓。
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
sns.set(color_codes=True)
tips = sns.load_dataset("tips")
一、繪制線性回歸模型
seaborn
主要通過兩個(gè)函數(shù)來展示通過回歸得到的線性關(guān)系恋追,regplot()
和lmplot()
凭迹。它們緊密相關(guān)罚屋,而且共享大多數(shù)的核心功能。但是弄清楚他們的區(qū)別非常重要嗅绸,這樣我們就可以在針對特定工作時(shí)快速判斷哪個(gè)工具更適合脾猛。
在最基本的調(diào)用過程中,他們都會(huì)畫出關(guān)于x鱼鸠、y兩個(gè)變量的散點(diǎn)圖猛拴,同時(shí)用數(shù)據(jù)擬合一個(gè)y ~ x
的模型出來,并將對應(yīng)的直線和95%的置信區(qū)間繪制出來:
sns.regplot(x="total_bill", y="tip", data=tips);
sns.lmplot(x="total_bill", y="tip", data=tips);
我們注意到除了圖片的形狀略有差異蚀狰,其他地方都是一致的愉昆。需要知道的是,他們之間最主要的區(qū)別在于regplot()
中的x, y
參數(shù)接受多種數(shù)據(jù)類型麻蹋,包括numpy
數(shù)組跛溉、pandas
序列(Series
),或者將pandas DataFrame
傳遞給data
參數(shù)扮授。作為對比倒谷,lmplot()
的data
參數(shù)是不能為空的,同時(shí)x
和y
參數(shù)必須以字符串形式指定糙箍。這種數(shù)據(jù)格式(這里是指regplot()
支持而lmplot()
不支持的類似一維數(shù)組的數(shù)據(jù)格式)被稱為“l(fā)ong-form data”或“tidy data”。除了這一輸入格式的靈活性以外牵祟,regplot()
僅提供了lmplot()
特性的一部分深夯,所以我們用后者來演示它們的使用。
seaborn
支持其中一個(gè)變量屬于離散變量的情況诺苹,不過這種數(shù)據(jù)集產(chǎn)生的散點(diǎn)圖效果往往一般:
sns.lmplot(x="size", y="tip", data=tips);
增加一些隨機(jī)的偏移量會(huì)讓這些分布看起來更清晰咕晋,需要注意的是這些偏移量僅僅會(huì)影響散點(diǎn)圖的效果,不會(huì)對擬合的回歸線產(chǎn)生干擾:
sns.lmplot(x="size", y="tip", data=tips, x_jitter=.05);
另一個(gè)選擇是將每個(gè)離散桶內(nèi)的觀測值隱藏起來收奔,用代表集中趨勢的統(tǒng)計(jì)量以及置信區(qū)間作為替代:
sns.lmplot(x="size", y="tip", data=tips, x_estimator=np.mean);
二掌呜、擬合不同的模型
簡單線性回歸的模型非常容易擬合,然而它并不適用于所有數(shù)據(jù)集坪哄。Anscombe's quartet
數(shù)據(jù)集展示了一些例子质蕉,在這些例子中簡單線性回歸提供了變量間關(guān)系一致的估計(jì),但是卻與我們視覺上的直觀判斷存在一些差異翩肌。比如模暗,在第一個(gè)例子中,線性回歸是一個(gè)很不錯(cuò)的模型:
anscombe = sns.load_dataset("anscombe")
sns.lmplot(x="x", y="y", data=anscombe.query("dataset == 'I'"),
ci=None, scatter_kws={"s": 80});
第二個(gè)數(shù)據(jù)集中有著同樣的線性關(guān)系念祭,但是我們瞬間就能判斷線性回歸并不是一個(gè)最佳的模型:
sns.lmplot(x="x", y="y", data=anscombe.query("dataset == 'II'"),
ci=None, scatter_kws={"s": 80});
在展示這種更高階的關(guān)系時(shí)兑宇,lmplot()
和regplot()
可以通過擬合多項(xiàng)式回歸模型來應(yīng)對數(shù)據(jù)集中的一些簡單的非線性趨勢:
sns.lmplot(x="x", y="y", data=anscombe.query("dataset == 'II'"),
order=2, ci=None, scatter_kws={"s": 80});
另一個(gè)問題是由異常觀測點(diǎn)導(dǎo)致的,這些觀測點(diǎn)明顯偏離了我們想要得到的主要趨勢關(guān)系:
sns.lmplot(x="x", y="y", data=anscombe.query("dataset == 'III'"),
ci=None, scatter_kws={"s": 80});
在異常觀測值存在時(shí)粱坤,我們可以擬合一個(gè)魯棒回歸(穩(wěn)定回歸)隶糕,它使用了不同的損失函數(shù)瓷产,對較大的殘差做了降權(quán):
sns.lmplot(x="x", y="y", data=anscombe.query("dataset == 'III'"),
robust=True, ci=None, scatter_kws={"s": 80});
當(dāng)因變量y
是二元變量時(shí),簡單線性回歸會(huì)提供一個(gè)不太有說服力的預(yù)測:
tips["big_tip"] = (tips.tip / tips.total_bill) > .15
sns.lmplot(x="total_bill", y="big_tip", data=tips,
y_jitter=.03);
這種情況下的解決方案是擬合一個(gè)邏輯回歸模型枚驻,此時(shí)回歸線的含義是在給定的x
下y = 1
的概率:
sns.lmplot(x="total_bill", y="big_tip", data=tips,
logistic=True, y_jitter=.03);
要注意的是濒旦,相對于簡單線性回歸,邏輯回歸估算具有相當(dāng)高的計(jì)算復(fù)雜度(穩(wěn)健回歸也是)测秸“坦溃考慮到圍繞著回歸線的置信區(qū)間是通過自主抽樣過程計(jì)算的(也會(huì)花費(fèi)大量時(shí)間),在擬合邏輯回歸等模型時(shí)最好把置信區(qū)間的計(jì)算給關(guān)掉(ci=None
)霎冯。
一個(gè)完全不同的方法是通過局部加權(quán)回歸散點(diǎn)平滑法(LOWESS)擬合一個(gè)非參數(shù)回歸模型铃拇。這種方法有最少的假設(shè)前提,不過它也會(huì)耗費(fèi)大量計(jì)算資源沈撞,因此置信區(qū)間并不會(huì)被計(jì)算:
sns.lmplot(x="total_bill", y="tip", data=tips, lowess=True);
residplot()
函數(shù)可以檢查一個(gè)簡單的回歸模型對于某個(gè)數(shù)據(jù)集是否合適慷荔。它先擬合一個(gè)簡單線性回歸模型并移除它,然后將每個(gè)觀測點(diǎn)與預(yù)測值的殘差畫出來缠俺。理想情況下显晶,這些殘差應(yīng)該隨機(jī)地分布在x軸上下方:
sns.residplot(x="x", y="y", data=anscombe.query("dataset == 'I'"),
scatter_kws={"s": 80});
如果殘差的分布具有某種規(guī)律,那說明簡單線性回歸或許并不是一個(gè)好的選擇:
sns.residplot(x="x", y="y", data=anscombe.query("dataset == 'II'"),
scatter_kws={"s": 80});
三壹士、考慮其他變量的影響
上邊我們展示了很多探索一對變量之間關(guān)系的可視化方式磷雇。然而在很多情況下,“兩個(gè)變量之間的關(guān)系如何受到第三個(gè)變量的影響”是一個(gè)更加有趣的問題躏救。在這里regplot()
和lmplot()
的差異就出現(xiàn)了:regplot()
只能展示簡單的關(guān)系唯笙,而lmplot()
則融合了regplot()
和FacetGrid
。因此盒使,lmplot()
可以輕易地探索最多三個(gè)額外變量帶來的交互作用崩掘。
分離一個(gè)關(guān)系最好的方式就是將不通水平的數(shù)據(jù)繪制在同一個(gè)坐標(biāo)軸體系內(nèi),并且用顏色來區(qū)分他們:
sns.lmplot(x="total_bill", y="tip", hue="smoker", data=tips);
除了顏色少办,我們還可以通過標(biāo)記樣式來區(qū)分不同的水平(分類)苞慢,這樣就可以更好的應(yīng)對黑白色調(diào)或色盲讀者。我們可以控制調(diào)色板:
sns.lmplot(x="total_bill", y="tip", hue="smoker", data=tips,
markers=["o", "x"], palette="Set1");
我們可以通過更多的子圖(行英妓、列)來引入新的變量:
sns.lmplot(x="total_bill", y="tip", hue="smoker", col="time", data=tips);
sns.lmplot(x="total_bill", y="tip", hue="smoker",
col="time", row="sex", data=tips);
四挽放、控制圖形大小和形狀
前邊我們提到regplot()
和lmplot()
所創(chuàng)建的圖形在默認(rèn)大小和形狀上不一致。這是因?yàn)?code>regplot()是一個(gè)坐標(biāo)軸級別的繪圖函數(shù)蔓纠,這意味著我們可以在一張圖中生成多個(gè)子圖骂维,隨心所欲地控制它們的大小和位置。如果我們沒有指定坐標(biāo)軸參數(shù)(ax
)的haul贺纲,它會(huì)默認(rèn)使用當(dāng)前活躍的坐標(biāo)軸航闺,這就是為什么它創(chuàng)建的圖形有著和其他matplotlib
函數(shù)繪制的圖形一樣的默認(rèn)大小和形狀。想要控制它們的話,我們需要自己創(chuàng)建一個(gè)圖形對象:
f, ax = plt.subplots(figsize=(5, 6))
sns.regplot(x="total_bill", y="tip", data=tips, ax=ax);
不同的是潦刃,lmplot()
繪制的圖形是通過FacetGrid
接口中的height
和aspect
(寬高比)參數(shù)控制大小形狀的侮措,這些指定的大小和比例是針對每張子圖而非整張圖的:
sns.lmplot(x="total_bill", y="tip", col="day",
data=tips, col_wrap=2, height=3);
sns.lmplot(x="total_bill", y="tip", col="day",
data=tips, aspect=.5);
五、在其他背景中添加回歸圖
有一些seaborn
函數(shù)會(huì)在其他更大乖杠、更復(fù)雜的圖形背景中使用regplot()
分扎,第一個(gè)就是我們在數(shù)據(jù)分布教程中提到的jointplot()
函數(shù)。除了我們之前討論過的其他樣式胧洒,jointplot()
可以通過kind="reg"
來調(diào)用regplot()
繪制線性關(guān)系:
sns.jointplot(x="total_bill", y="tip", data=tips, kind="reg");
給pairplot()
傳入kind="reg"
參數(shù)則會(huì)融合regplot()
與PairGrid
來展示變量間的線性關(guān)系畏吓。注意這里和lmplot()
的區(qū)別,lmplot()
繪制的行(或列)是將一個(gè)變量的多個(gè)水平(分類卫漫、取值)展開菲饼,而在這里,PairGrid
則是繪制了不同變量之間的線性關(guān)系:
sns.pairplot(tips, x_vars=["total_bill", "size"], y_vars=["tip"],
height=5, aspect=.8, kind="reg");
與lmplot()
類似列赎,但是與jointplot()
不同宏悦,額外的分類變量是使用hue
參數(shù)傳遞給pairplot()
來處理的:
sns.pairplot(tips, x_vars=["total_bill", "size"], y_vars=["tip"],
hue="smoker", height=5, aspect=.8, kind="reg");