天天看點

資料科學 IPython 筆記本 8.17 使用 Seaborn 的可視化8.17 使用 Seaborn 的可視化

8.17 使用 Seaborn 的可視化

原文:Visualization with Seaborn

譯者:飛龍

協定:CC BY-NC-SA 4.0

本節是《Python 資料科學手冊》(Python Data Science Handbook)的摘錄。

Matplotlib 據證明是一種非常有用和流行的可視化工具,但即使狂熱的使用者也會承認它經常會有很多不足之處。有幾個對 Matplotlib 的有效的抱怨常常出現:

  • 在 2.0 版之前,Matplotlib 的預設值并不是最佳選擇。 它基于大約 1999 年的 MATLAB,經常是這樣。
  • Matplotlib 的 API 相對較低。 可以進行複雜的統計可視化,但通常需要大量的樣闆代碼。
  • Matplotlib 比 Pandas 早了十多年,是以不适合與 Pandas 的

    DataFrame`一起使用。 為了可視化來自 Pandas

    DataFrame

    的資料,你必須提取每個

    Series``并經常将它們連接配接成正确的格式。 如果有一個繪圖庫可以智能地在繪圖中使用

    DataFrame

    标簽會更好。

這些問題的答案是Seaborn。 Seaborn 在 Matplotlib 之上提供 API,為繪圖樣式和顔色預設值提供合理的選擇,為常見的統計繪圖類型定義簡單的進階函數,并與 Pandas

DataFrame

提供的功能內建。

公平地說,Matplotlib 團隊正在解決這個問題:它最近添加了“自定義 Matplotlib:配置和樣式表”中讨論的

plt.style

工具,并且正在開始 更無縫地處理 Pandas 資料。該庫的 2.0 版本将包含新的預設樣式表,它将改善現狀。但出于所讨論的所有原因,Seaborn 仍然是一個非常有用的插件。

Seaborn VS Matplotlib

下面是 Matplotlib 中簡單随機遊走圖的示例,使用其經典的繪圖格式和顔色。我們從典型的導入開始:

import matplotlib.pyplot as plt
plt.style.use('classic')
%matplotlib inline
import numpy as np
import pandas as pd           

複制

現在我們建立一些随機遊走資料:

# 建立一些資料
rng = np.random.RandomState(0)
x = np.linspace(0, 10, 500)
y = np.cumsum(rng.randn(500, 6), 0)           

複制

并執行簡單的繪圖:

# 使用 Matplotlib 預設值繪制資料
plt.plot(x, y)
plt.legend('ABCDEF', ncol=2, loc='upper left');           

複制

資料科學 IPython 筆記本 8.17 使用 Seaborn 的可視化8.17 使用 Seaborn 的可視化

雖然結果包含了我們希望傳達的所有資訊,但它的确以一種并非好看的方式,甚至在 21 世紀資料可視化的背景下看起來有點過時。

現在讓我們來看看它如何與 Seaborn 一起使用。我們将要看到,Seaborn 有許多自己的進階繪圖例程,但它也可以覆寫 Matplotlib 的預設參數,反過來甚至可以使簡單的 Matplotlib 腳本産生非常出色的輸出。我們可以通過調用 Seaborn 的

set()

方法來設定樣式。按照慣例,Seaborn 被導入為

sns

import seaborn as sns
sns.set()           

複制

現在讓我們重新運作與以前相同的兩行:

# 和上面一樣的繪圖代碼
plt.plot(x, y)
plt.legend('ABCDEF', ncol=2, loc='upper left');           

複制

資料科學 IPython 筆記本 8.17 使用 Seaborn 的可視化8.17 使用 Seaborn 的可視化

啊,好多了!

探索 Seaborn 繪圖

Seaborn 的主要思想是它提供進階指令,來建立用于統計資料探索,甚至是一些統計模型拟合的各種繪圖類型。

我們來看看Seaborn中可用的一些資料集和繪圖類型。 請注意,以下所有都可以使用原始 Matplotlib 指令完成(事實上,這是 Seaborn 所做的事情),但 Seaborn API 更友善。

直方圖,KDE,和密度

通常在統計資料可視化中,你隻需要繪制直方圖和變量的聯合分布。我們已經看到這在 Matplotlib 中相對簡單:

data = np.random.multivariate_normal([0, 0], [[5, 2], [2, 2]], size=2000)
data = pd.DataFrame(data, columns=['x', 'y'])

for col in 'xy':
    plt.hist(data[col], normed=True, alpha=0.5)           

複制

資料科學 IPython 筆記本 8.17 使用 Seaborn 的可視化8.17 使用 Seaborn 的可視化

我們可以使用核密度估計來獲得對分布的平滑估計,而不是直方圖,Seaborn 使用

sns.kdeplot

來執行:

for col in 'xy':
    sns.kdeplot(data[col], shade=True)           

複制

資料科學 IPython 筆記本 8.17 使用 Seaborn 的可視化8.17 使用 Seaborn 的可視化

直方圖和 KDE 可以使用

distplot

組合:

sns.distplot(data['x'])
sns.distplot(data['y']);           

複制

資料科學 IPython 筆記本 8.17 使用 Seaborn 的可視化8.17 使用 Seaborn 的可視化

如果我們将完整的二維資料集傳遞給

kdeplot

,我們将獲得資料的二維可視化:

sns.kdeplot(data);           

複制

資料科學 IPython 筆記本 8.17 使用 Seaborn 的可視化8.17 使用 Seaborn 的可視化

我們可以使用

sns.jointplot

檢視聯合分布和邊緣分布。對于此圖,我們将樣式設定為白色背景:

with sns.axes_style('white'):
    sns.jointplot("x", "y", data, kind='kde');           

複制

資料科學 IPython 筆記本 8.17 使用 Seaborn 的可視化8.17 使用 Seaborn 的可視化

還有其他參數可以傳遞給

jointplot

- 例如,我們可以使用基于六邊形的直方圖:

with sns.axes_style('white'):
    sns.jointplot("x", "y", data, kind='hex')           

複制

資料科學 IPython 筆記本 8.17 使用 Seaborn 的可視化8.17 使用 Seaborn 的可視化

配對繪圖

将聯合繪圖推廣到高維資料集時,最終會得到配對繪圖。 當你想要繪制所有值對于彼此的配對時,這對于探索多元資料之間的相關性非常有用。

我們将使用着名的鸢尾花資料集進行示範,該資料集列出了三種鸢尾花物種的花瓣和萼片的測量值:

iris = sns.load_dataset("iris")
iris.head()           

複制

sepal_length sepal_width petal_length petal_width species
5.1 3.5 1.4 0.2 setosa
1 4.9 3.0 1.4 0.2 setosa
2 4.7 3.2 1.3 0.2 setosa
3 4.6 3.1 1.5 0.2 setosa
4 5.0 3.6 1.4 0.2 setosa

可視化樣本之間的多元關系就像調用

sns.pairplot

一樣簡單:

sns.pairplot(iris, hue='species', size=2.5);           

複制

資料科學 IPython 筆記本 8.17 使用 Seaborn 的可視化8.17 使用 Seaborn 的可視化

分面直方圖

有時,檢視資料的最佳方式是通過子集的直方圖。 Seaborn 的

FacetGrid

使其非常簡單。我們将根據各種名額資料檢視一些資料,它們顯示餐廳員工在小費中收到的金額:

tips = sns.load_dataset('tips')
tips.head()           

複制

| | total_bill | tip | sex | smoker | day | time | size |

| — | — | — | — | — | — | — |

| 0 | 16.99 | 1.01 | Female | No | Sun | Dinner | 2 |

| 1 | 10.34 | 1.66 | Male | No | Sun | Dinner | 3 |

| 2 | 21.01 | 3.50 | Male | No | Sun | Dinner | 3 |

| 3 | 23.68 | 3.31 | Male | No | Sun | Dinner | 2 |

| 4 | 24.59 | 3.61 | Female | No | Sun | Dinner | 4 |

tips['tip_pct'] = 100 * tips['tip'] / tips['total_bill']

grid = sns.FacetGrid(tips, row="sex", col="time", margin_titles=True)
grid.map(plt.hist, "tip_pct", bins=np.linspace(0, 40, 15));           

複制

資料科學 IPython 筆記本 8.17 使用 Seaborn 的可視化8.17 使用 Seaborn 的可視化

因子圖

因子圖也可用于此類可視化。 這允許你檢視由任何其他參數定義的桶中的參數分布:

with sns.axes_style(style='ticks'):
    g = sns.factorplot("day", "total_bill", "sex", data=tips, kind="box")
    g.set_axis_labels("Day", "Total Bill");           

複制

資料科學 IPython 筆記本 8.17 使用 Seaborn 的可視化8.17 使用 Seaborn 的可視化

聯合分布

與我們之前看到的配對圖類似,我們可以使用

sns.jointplot

來顯示不同資料集之間的聯合分布以及相關的邊緣分布:

with sns.axes_style('white'):
    sns.jointplot("total_bill", "tip", data=tips, kind='hex')           

複制

資料科學 IPython 筆記本 8.17 使用 Seaborn 的可視化8.17 使用 Seaborn 的可視化

聯合圖甚至可以做一些自動的核密度估計和回歸:

sns.jointplot("total_bill", "tip", data=tips, kind='reg');           

複制

資料科學 IPython 筆記本 8.17 使用 Seaborn 的可視化8.17 使用 Seaborn 的可視化

條形圖

時間序列可以使用

sns.factorplot

繪制。 在下面的示例中,我們将使用我們在“聚合和分組”中首次看到的行星資料:

planets = sns.load_dataset('planets')
planets.head()           

複制

method number orbital_period mass distance year
Radial Velocity 1 269.300 7.10 77.40 2006
1 Radial Velocity 1 874.774 2.21 56.95 2008
2 Radial Velocity 1 763.000 2.60 19.84 2011
3 Radial Velocity 1 326.030 19.40 110.62 2007
4 Radial Velocity 1 516.220 10.50 119.47 2009
with sns.axes_style('white'):
    g = sns.factorplot("year", data=planets, aspect=2,
                       kind="count", color='steelblue')
    g.set_xticklabels(step=5)           

複制

資料科學 IPython 筆記本 8.17 使用 Seaborn 的可視化8.17 使用 Seaborn 的可視化

通過檢視每個行星的發現方法,我們可以了解更多資訊:

with sns.axes_style('white'):
    g = sns.factorplot("year", data=planets, aspect=4.0, kind='count',
                       hue='method', order=range(2001, 2015))
    g.set_ylabels('Number of Planets Discovered')           

複制

資料科學 IPython 筆記本 8.17 使用 Seaborn 的可視化8.17 使用 Seaborn 的可視化

對于使用 Seaborn 進行繪圖的更多資訊,請參閱 Seaborn 文檔,教程和 Seaborn 畫廊。

示例:探索馬拉松結束時間

在這裡,我們将使用 Seaborn 來幫我們可視化和了解馬拉松的結果。我從 Web 上的資料源抓取資料,彙總并删除任何身份資訊,并将其放在 GitHub 上,可以在那裡下載下傳(如果你有興趣使用 Python 抓取網頁,我建議閱讀 Ryan Mitchell 的《Web Scraping with Python》。我們首先從 Web 下載下傳資料并将其加載到 Pandas 中:

# !curl -O https://raw.githubusercontent.com/jakevdp/marathon-data/master/marathon-data.csv

data = pd.read_csv('marathon-data.csv')
data.head()           

複制

age gender split final
33 M 01:05:38 02:08:51
1 32 M 01:06:26 02:09:28
2 31 M 01:06:49 02:10:42
3 38 M 01:06:16 02:13:45
4 31 M 01:06:32 02:13:59

預設情況下,Pandas 将時間列加載為 Python 字元串(類型

object

);我們可以通過檢視

DataFrame

dtypes

屬性來看到它:

data.dtypes

'''
age        int64
gender    object
split     object
final     object
dtype: object
'''           

複制

讓我們通過為時間提供轉換器來解決這個問題:

def convert_time(s):
    h, m, s = map(int, s.split(':'))
    return pd.datetools.timedelta(hours=h, minutes=m, seconds=s)

data = pd.read_csv('marathon-data.csv',
                   converters={'split':convert_time, 'final':convert_time})
data.head()           

複制

age gender split final
33 M 01:05:38 02:08:51
1 32 M 01:06:26 02:09:28
2 31 M 01:06:49 02:10:42
3 38 M 01:06:16 02:13:45
4 31 M 01:06:32 02:13:59
data.dtypes

'''
age                 int64
gender             object
split     timedelta64[ns]
final     timedelta64[ns]
dtype: object
'''           

複制

這看起來好多了。 出于我們的 Seaborn 繪圖工具的目的,讓我們接下來添加以秒為機關的列:

data['split_sec'] = data['split'].astype(int) / 1E9
data['final_sec'] = data['final'].astype(int) / 1E9
data.head()           

複制

age gender split final split_sec final_sec
33 M 01:05:38 02:08:51 3938.0 7731.0
1 32 M 01:06:26 02:09:28 3986.0 7768.0
2 31 M 01:06:49 02:10:42 4009.0 7842.0
3 38 M 01:06:16 02:13:45 3976.0 8025.0
4 31 M 01:06:32 02:13:59 3992.0 8039.0

為了了解資料的樣子,我們可以在資料上繪制一個

jointplot

with sns.axes_style('white'):
    g = sns.jointplot("split_sec", "final_sec", data, kind='hex')
    g.ax_joint.plot(np.linspace(4000, 16000),
                    np.linspace(8000, 32000), ':k')           

複制

資料科學 IPython 筆記本 8.17 使用 Seaborn 的可視化8.17 使用 Seaborn 的可視化

虛線表示如果他們以完全穩定的速度跑馬拉松,那麼某人的時間會在哪裡。 分布高于此的事實表明(正如你所料)大多數人在馬拉松比賽過程中減速。如果你有競争力,那麼你就會知道那些在比賽後半段跑得更快的人 - 被稱為将比賽負分割(negative-split)。

讓我們在資料中建立另一個列,即分割分數,它測量每個運動員将比賽負分割或正分割(positive-split)的程度:

data['split_frac'] = 1 - 2 * data['split_sec'] / data['final_sec']
data.head()           

複制

age gender split final split_sec final_sec split_frac
33 M 01:05:38 02:08:51 3938.0 7731.0 -0.018756
1 32 M 01:06:26 02:09:28 3986.0 7768.0 -0.026262
2 31 M 01:06:49 02:10:42 4009.0 7842.0 -0.022443
3 38 M 01:06:16 02:13:45 3976.0 8025.0 0.009097
4 31 M 01:06:32 02:13:59 3992.0 8039.0 0.006842

如果此分割差異小于零,則這個人将比賽以這個比例負分割。讓我們繪制這個分割分數的分布圖:

sns.distplot(data['split_frac'], kde=False);
plt.axvline(0, color="k", linestyle="--");           

複制

資料科學 IPython 筆記本 8.17 使用 Seaborn 的可視化8.17 使用 Seaborn 的可視化
sum(data.split_frac < 0)

# 251           

複制

在近 40,000 名參與者中,隻有 250 人将馬拉松負分割。

讓我們看看這個分割分數和其他變量之間是否存在任何相關性。我們将使用

pairgrid

來繪制所有這些相關性:

g = sns.PairGrid(data, vars=['age', 'split_sec', 'final_sec', 'split_frac'],
                 hue='gender', palette='RdBu_r')
g.map(plt.scatter, alpha=0.8)
g.add_legend();           

複制

資料科學 IPython 筆記本 8.17 使用 Seaborn 的可視化8.17 使用 Seaborn 的可視化

看起來分割分數與年齡沒有特别的關聯,但确實與最終時間相關:更快的運動員往往将馬拉松時間等分。(我們在這裡看到,當涉及到繪圖樣式時,Seaborn 不是 Matplotlib 弊病的靈丹妙藥:特别是,

x

軸标簽重疊。因為輸出是一個簡單的 Matplotlib 圖,但是,“自定義刻度”中的方法可以用來調整這些東西。)

這裡男女之間的差別很有意思。 讓我們看看這兩組的分割分數的直方圖:

sns.kdeplot(data.split_frac[data.gender=='M'], label='men', shade=True)
sns.kdeplot(data.split_frac[data.gender=='W'], label='women', shade=True)
plt.xlabel('split_frac');           

複制

資料科學 IPython 筆記本 8.17 使用 Seaborn 的可視化8.17 使用 Seaborn 的可視化

這裡有趣的是,有更多的男人比女人更接近等分!這幾乎看起來像男女之間的某種雙峰分布。 讓我們看看,我們是否可以通過将分布看做年齡的函數,來判斷發生了什麼。

比較分布的好方法是使用提琴圖:

sns.violinplot("gender", "split_frac", data=data,
               palette=["lightblue", "lightpink"]);           

複制

資料科學 IPython 筆記本 8.17 使用 Seaborn 的可視化8.17 使用 Seaborn 的可視化

這是比較男女之間分布的另一種方式。

讓我們看得深入一些,然後将這些提琴圖作為年齡的函數進行比較。我們首先在數組中建立一個新列,指定每個人的年齡,以十年為機關:

data['age_dec'] = data.age.map(lambda age: 10 * (age // 10))
data.head()           

複制

age gender split final split_sec final_sec split_frac age_dec
33 M 01:05:38 02:08:51 3938.0 7731.0 -0.018756 30
1 32 M 01:06:26 02:09:28 3986.0 7768.0 -0.026262 30
2 31 M 01:06:49 02:10:42 4009.0 7842.0 -0.022443 30
3 38 M 01:06:16 02:13:45 3976.0 8025.0 0.009097 30
4 31 M 01:06:32 02:13:59 3992.0 8039.0 0.006842 30
men = (data.gender == 'M')
women = (data.gender == 'W')

with sns.axes_style(style=None):
    sns.violinplot("age_dec", "split_frac", hue="gender", data=data,
                   split=True, inner="quartile",
                   palette=["lightblue", "lightpink"]);           

複制

資料科學 IPython 筆記本 8.17 使用 Seaborn 的可視化8.17 使用 Seaborn 的可視化

考慮到這一點,我們可以看到男性和女性的分布在哪裡不同:與同齡(或任何年齡的)女性相比,20 到 50 歲男性的分割分布,與較低的分割相比,表現出明顯的過度密集。

同樣令人驚訝的是,這位 80 歲的女性在分割時間方面表現優于每個人。 這可能是因為我們估計來自小數字的分布,因為在該範圍内隻有少數運動員:

(data.age > 80).sum()

# 7           

複制

回到帶有負分割的男性:誰是這些運動員? 這個分割分數是否與快速結束相關? 我們可以很容易地繪制這個圖。 我們将使用

regplot

,它将自動拟合資料的線性回歸:

g = sns.lmplot('final_sec', 'split_frac', col='gender', data=data,
               markers=".", scatter_kws=dict(color='c'))
g.map(plt.axhline, y=0.1, color="k", ls=":");           

複制

資料科學 IPython 筆記本 8.17 使用 Seaborn 的可視化8.17 使用 Seaborn 的可視化

顯然,帶有快速分割的人是精英運動員,他們在約 15,000 秒或約 4 小時内結束。 慢于此的人不太可能具有快速的第二次分割。