Python 神兵譜之數據分析-下篇:數據可視化

前言

承接上一篇《Python 神兵譜之數據分析-中篇:數據處理》,今天咱們來說講這下篇:數據可視化。html

不管是數據採集仍是數據處理,都是冰山在水面下的內容,水面下作的再好,也須要水面上的可視化來呈現。而水面上展現的內容多少,須要水面下成倍的積累。前端

那麼,如何快速作好數據可視化呢?python

下篇:數據可視化

數據可視化,顧名思義就是將繁多的數據轉換成利於展現,便於人理解的圖表。而這其中,最爲常見的就是幾種圖:餅圖、折線圖、柱狀圖、雷達圖、箱線圖等等。下面咱們來看看 Python 中衆多的繪圖庫,如何來繪製這些圖表吧。git

Matplotlib

Matplotlib 是 Python 很是著名的繪圖庫,受到了 Matlab 的啓發,使用方式和接口都很是像 Matlab。先來看看用 Matplotlib 繪製的圖,github

比較中規中矩,很是適合科學計算領域。 Matplotlib 通常以以下的方式引用。express

import matplotlib.pyplot as plt
複製代碼

Matplotlib 中有幾個核心概念。數組

  • Figure:面板,全部圖像都是位於 figure 對象中,一個圖像只能有一個 figure 對象。
  • Subplot:子圖,figure 對象下建立一個或多個 subplot 對象(即座標系)用於繪製圖像。
  • Axis:座標軸,即每一個子圖或座標系中的一條座標軸。

繪製柱狀圖的例子。bash

import matplotlib.pyplot as plt


labels = ['G1', 'G2', 'G3', 'G4', 'G5']
men_means = [20, 35, 30, 35, 27]
women_means = [25, 32, 34, 20, 25]
men_std = [2, 3, 4, 1, 2]
women_std = [3, 5, 2, 3, 3]
width = 0.35

fig, ax = plt.subplots()  # 即 Figure,subplot 對象

# 繪製柱狀圖
ax.bar(labels, men_means, width, yerr=men_std, label='Men')
ax.bar(labels, women_means, width, yerr=women_std, bottom=men_means, label='Women')

# 設置標籤、標題、圖例
ax.set_ylabel('Scores')
ax.set_title('Scores by group and gender')
ax.legend()

# 顯示圖表
plt.show()
複製代碼

會顯示以下所示的圖形。服務器

Matplotlib 配置的方式比較直觀,同時也支持 Numpy 數組做爲輸入。框架

fig = plt.figure(2)  # 新開一個窗口
ax1 = fig.add_subplot(1, 2, 1, polar=True)  # 啓動一個極座標子圖
theta = np.arange(0, 2 * np.pi, 0.02)  # 角度數列值
ax1.plot(theta, 2 * np.ones_like(theta), lw=2)  # 畫圖,參數:角度,半徑,lw線寬
ax1.plot(theta, theta / 6, linestyle='--', lw=2)  # 畫圖,參數:角度,半徑,linestyle樣式,lw線寬

# 啓動一個極座標子圖
ax2 = fig.add_subplot(1, 2, 2, polar=True)
ax2.plot(theta, np.cos(5 * theta), linestyle='--', lw=2)
ax2.plot(theta, 2 * np.cos(4 * theta), lw=2)

# 設置網格軸的距離和角度
ax2.set_rgrids(np.arange(0.2, 2, 0.2), angle=45)
ax2.set_thetagrids([0, 45, 90])

plt.show()
複製代碼

Matplotlib 是 Python 繪圖庫中功能強大,可是偏底層的,對於繪圖的控制能力比較強,可是配置項也比較多。

Seaborn

Seaborn 是基於 Matplotlib 開發的繪圖庫,提供了更高級和簡潔的語法。經過 Seaborn 的高級 API,可以快速繪圖圖形,且無需配置就提供了一些好看的樣式。

先上一個折線圖。

import seaborn as sns
sns.set(style="ticks")

# 載入測試數據集
df = sns.load_dataset("anscombe")

# 爲每一個數據集繪製折線圖並展現線性迴歸線
sns.lmplot(x="x", y="y", col="dataset", hue="dataset", data=df,
           col_wrap=2, ci=None, palette="muted", height=4,
           scatter_kws={"s": 50, "alpha": 1})
複製代碼

繪圖只有一行代碼,是否是很是的酷!

Seaborn 有一套統一的繪圖 API,要求原始數據的輸入類型爲 Pandas 的 Dataframe 或 Numpy 數組,API 形式以下:

  • sns.圖名(x='X軸列名', y='Y軸列名', data=原始數據df對象)
  • sns.圖名(x='X軸列名', y='Y軸列名', hue='分組繪圖參數', data=原始數據df對象)
  • sns.圖名(x=np.array, y=np.array[, ...])

也來繪製一個柱狀圖。

import seaborn as sns
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

# 生成數據
x = np.arange(8)
y = np.array([1,5,3,6,2,4,5,6])
df = pd.DataFrame({"x-axis": x, "y-axis": y})
# 繪製柱狀圖
sns.barplot("x-axis", "y-axis", palette="RdBu_r", data=df)
# 調用 Matplotlib 的底層 API
plt.xticks(rotation=90)
plt.show()
複製代碼

Seaborn 提供了一個高層的簡便的 API,能夠和 Matplotlib 結合使用,大大簡化了 Matplotlib 的配置操做。

plotnine/ggplot

plotnine 和 ggplot 的靈感都來源於 R 語言的 ggplot2,和 Matplotlib 的設計思路徹底不一樣,是圖層疊加的思想,即一層層疊加繪製。不過它們仍舊是基於 Matplotlib 開發的。輸入數據須要是 Pandas 的 DataFrame 類型。兩個庫的語法相似,ggplot 最近已經不更新,而 plotnine 是最近比較活躍的。

下面的代碼來自 plotnine,繪製柱狀圖。

import pandas as pd
import numpy as np

from plotnine import *
from plotnine.data import *

ggplot(mpg) + geom_bar(aes(x='class'))
複製代碼

經過 + 把一個個圖層疊加起來,通常會有數據層、幾何圖形層和美化層組成。

而後加一些色彩。

ggplot(mpg) + geom_bar(aes(x='class', fill='drv'))
複製代碼

再進行一些變換。

(
    ggplot(mpg)
    + geom_bar(aes(x='class', fill='drv'))
    + coord_flip()
    + theme_classic()
)
複製代碼

plotnine / ggplot 的語法很是簡潔,很是適合於喜歡或習慣 R 語言繪圖的人。

Bokeh

Bokeh 是 Python 中一個很是強大的交互式圖表庫,即經過 JS 代碼生成能夠交互的網頁端圖表,很是適合嵌入到前端應用中。Bokeh 繪製的圖表很是好看,而且有不錯的交互體驗。

左側的選擇框可實時影響中間的數據渲染,圖表右側還有交互式的控制欄。同時數據點也能響應鼠標事件,例如鼠標移動上去後顯示具體數值。

上述圖表的代碼以下:

import pandas as pd

from bokeh.layouts import column, row
from bokeh.models import Select
from bokeh.palettes import Spectral5
from bokeh.plotting import curdoc, figure
from bokeh.sampledata.autompg import autompg_clean as df

df = df.copy()

SIZES = list(range(6, 22, 3))
COLORS = Spectral5
N_SIZES = len(SIZES)
N_COLORS = len(COLORS)

# 數據清理
df.cyl = df.cyl.astype(str)
df.yr = df.yr.astype(str)
del df['name']

columns = sorted(df.columns)
discrete = [x for x in columns if df[x].dtype == object]
continuous = [x for x in columns if x not in discrete]

def create_figure():
    xs = df[x.value].values
    ys = df[y.value].values
    x_title = x.value.title()
    y_title = y.value.title()

    kw = dict()
    if x.value in discrete:
        kw['x_range'] = sorted(set(xs))
    if y.value in discrete:
        kw['y_range'] = sorted(set(ys))
    kw['title'] = "%s vs %s" % (x_title, y_title)

    p = figure(plot_height=600, plot_width=800, tools='pan,box_zoom,hover,reset', **kw)
    p.xaxis.axis_label = x_title
    p.yaxis.axis_label = y_title

    if x.value in discrete:
        p.xaxis.major_label_orientation = pd.np.pi / 4

    sz = 9
    if size.value != 'None':
        if len(set(df[size.value])) > N_SIZES:
            groups = pd.qcut(df[size.value].values, N_SIZES, duplicates='drop')
        else:
            groups = pd.Categorical(df[size.value])
        sz = [SIZES[xx] for xx in groups.codes]

    c = "#31AADE"
    if color.value != 'None':
        if len(set(df[color.value])) > N_COLORS:
            groups = pd.qcut(df[color.value].values, N_COLORS, duplicates='drop')
        else:
            groups = pd.Categorical(df[color.value])
        c = [COLORS[xx] for xx in groups.codes]

    p.circle(x=xs, y=ys, color=c, size=sz, line_color="white", alpha=0.6, hover_color='white', hover_alpha=0.5)

    return p


def update(attr, old, new):
    layout.children[1] = create_figure()


x = Select(title='X-Axis', value='mpg', options=columns)
x.on_change('value', update)

y = Select(title='Y-Axis', value='hp', options=columns)
y.on_change('value', update)

size = Select(title='Size', value='None', options=['None'] + continuous)
size.on_change('value', update)

color = Select(title='Color', value='None', options=['None'] + continuous)
color.on_change('value', update)

controls = column(x, y, color, size, width=200)
layout = row(controls, create_figure())

curdoc().add_root(layout)
curdoc().title = "Crossfilter"
複製代碼

因爲 Bokeh 生成的是 Html 網頁,沒法直接看到圖片,可使用 Bokeh 服務器運行查看,固然也能夠整合到本身的前端服務中。

bokeh serve --show crossfilter
複製代碼

Pygal

Pygal 是一個 SVG 繪圖庫,SVG 是一種矢量圖,也可嵌入前端網頁中作交互式展現。不過和 Bokeh 不一樣的是,SVG 只是圖表交互,並不包含 JS 代碼來動態修改數據和樣式。

Pygal 繪製的圖表也是能夠經過鼠標交互的,也但是隱藏或顯示某個系列數據。

繪製柱狀圖。

bar_chart = pygal.Bar()
# 添加數據
bar_chart.add('Fibonacci', [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55])
# 生成 SVG
bar_chart.render_to_file('bar_chart.svg')
複製代碼

只能展現靜態的截圖。

多個系列的數據。

import pygal

# 配置圖表
line_chart = pygal.Bar()
line_chart.title = 'Browser usage evolution (in %)'
line_chart.x_labels = map(str, range(2002, 2013))
# 添加數據
line_chart.add('Firefox', [None, None, 0, 16.6,   25,   31, 36.4, 45.5, 46.3, 42.8, 37.1])
line_chart.add('Chrome',  [None, None, None, None, None, None,    0,  3.9, 10.8, 23.8, 35.3])
line_chart.add('IE',      [85.8, 84.6, 84.7, 74.5,   66, 58.6, 54.7, 44.8, 36.2, 26.6, 20.1])
line_chart.add('Others',  [14.2, 15.4, 15.3,  8.9,    9, 10.4,  8.9,  5.8,  6.7,  6.8,  7.5])
line_chart.render()
複製代碼

Pygal 語法簡單,且能生成交互式 SVG 圖表,適合有前端簡單交互需求的場景使用。

Plotly

Plotly 是針對科學計算和機器學習可視化開發的圖表庫,同時它們還有 Dash 框架,用於快速開發機器學習或數據科學的應用。Plotly 繪製的圖表很是美觀,並且也是前端交互式的圖表。Plotly 是 Plotly 公司的產品,不過它仍舊是開源且無償使用的,無需聯網或註冊帳戶,不過它們也提供企業版本。

仍是繪製柱狀圖。

import plotly.express as px
data_canada = px.data.gapminder().query("country == 'Canada'")
fig = px.bar(data_canada, x='year', y='pop')
fig.show()
複製代碼

圖表也是能夠交互的,且右上角有菜單欄。

簡單的代碼美化效果。

import plotly.express as px
data = px.data.gapminder()

data_canada = data[data.country == 'Canada']
fig = px.bar(data_canada, x='year', y='pop',
             hover_data=['lifeExp', 'gdpPercap'], color='lifeExp',
             labels={'pop':'population of Canada'}, height=400)
fig.show()
複製代碼

Plotly 很是適合構建前端交互式圖表,同時可使用 Dash 框架快速構建數據分析應用,使用上也很是簡單明瞭。

Altair

Altair 是基於 VegaVega-Lite 語法的聲明式繪圖語言,可使用簡單的語法繪製交互式的圖表,效果仍是比較好的。

照例來繪製柱狀圖。

import altair as alt
import pandas as pd

source = pd.DataFrame({
    'a': ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I'],
    'b': [28, 55, 43, 91, 81, 53, 19, 87, 52]
})

alt.Chart(source).mark_bar().encode(
    x='a',
    y='b'
)
複製代碼

圖表也是交互式的。

import altair as alt
from vega_datasets import data

source = data.wheat()

bar = alt.Chart(source).mark_bar().encode(
    x='year:O',
    y='wheat:Q'
)

# 添加均值線
rule = alt.Chart(source).mark_rule(color='red').encode(
    y='mean(wheat):Q'
)

(bar + rule).properties(width=600)
複製代碼

Altair 的使用也很是簡單,且能夠繪製強大的交互式圖表,不過須要學習 Vega 或 Vega-lite 繪圖語法。

後記

Python 的繪圖庫很是之多,還有許多庫這裏尚未羅列,並不表明它們很差用。對於庫的選擇和使用,須要咱們基於不一樣的使用場景。例如,生成純圖片文件的 Matlibplot,Seaborn,plotline,ggplot,生成交互式圖表的 Bokeh、Plotly、Altair,生成 SVG 的 Pygal。 一些庫的 API 偏底層,編寫代碼量較大,可是靈活性高,例如 Matplotlib。一些庫的 API 偏高層,封裝更多,代碼量少,可是有很多侷限性,例如 Seaborn。

Python 神兵譜到此就完結了,之後我仍是會繼續分享有用的 Python 庫或工具,甚至作一些深刻實踐,歡迎點贊關注評論。

參考

相關文章
相關標籤/搜索