天天看點

使用Matplotlib繪制3D圖形測試環境準備線形圖散點圖線框圖曲面圖等高線柱狀圖多邊形制作動圖結束語參考資料與推薦讀物

本文是Matplotlib的第二篇文章,會講解如何通過Matplotlib繪制3D圖形。關于Matplotlib的第一篇文章,請看這裡: Python繪圖庫Matplotlib入門教程

測試環境

由于這是一個Python語言的軟體包,是以需要你的機器上首先安裝好Python語言的環境。關于這一點,請自行在網絡上搜尋擷取方法。

關于如何安裝Matplotlib請參見這裡:

Matplotlib Installing

筆者推薦大家通過

pip

或者

anaconde

的方式進行安裝。

本文中的源碼和測試資料可以在這裡擷取:

Github: matplotlib_tutorial

本文的代碼示例會用到其他一些Python庫。建議讀者先對其有一定的熟悉,

我的部落格

中也有一些相關文章。

本文的代碼在如下環境中測試:

  • Apple OS X 10.13
  • Python 3.6.2
  • matplotlib 2.2.3
  • numpy 1.14.1

準備

繪制3D圖形的時候我們通常都會包含下面這個代碼片段,這裡我們先對其進行說明。

import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

fig = plt.figure()
ax = fig.gca(projection='3d')           

這4行代碼說明如下:

  • 第一行自然不必說,就是導入

    matplotlib.pyplot

  • 第二行

    from mpl_toolkits.mplot3d import Axes3D

    是導入Axes3D類。我們後面在繪制3D圖形的時候,相應的函數都位于這個接口上。
  • fig = plt.figure()

    是擷取到目前figure對象。
  • ax = fig.gca(projection='3d')

    這一行是比較關鍵的。

    fig.gca

    是擷取圖中的目前極軸。如果不存在,或者不是極軸,則将建立相應的軸,然後傳回。此時得到的

    ax

    對象的類型是

    Axes3D

    的子類,這個對象将是繪制3D圖形的入口。

Colormap

繪制圖形的時候,常常會需要對圖形着色。Matplotlib中内置了很多的Colormap來簡化這個工作,具體可以看這裡:

Choosing Colormaps

下面是一些Colormap示例:

使用Matplotlib繪制3D圖形測試環境準備線形圖散點圖線框圖曲面圖等高線柱狀圖多邊形制作動圖結束語參考資料與推薦讀物

通過指定相應的名稱我們就可以直接使用這裡的Colormap了。

線形圖

Axes3D.plot 函數用來繪制線形圖。

首先我們來看最簡單的圖形 - 線形圖。

由于這是三維空間中的線,是以需要若幹個

(x, y, z)

坐标的值。

下面這段代碼生成了一條三維空間中的直線。

# line.py

import matplotlib.pyplot as plt
import numpy as np

from mpl_toolkits.mplot3d import Axes3D

fig = plt.figure()
ax = fig.gca(projection='3d')

x = np.linspace(-10, 10, 1000)
y = np.linspace(-10, 10, 1000)
z = np.add(x, y)

ax.plot(x, y, z)
plt.show()           

從這段代碼可以看出,這條線的x和y軸的範圍都是[-10, 10]。我們共計采樣了1000個點。

需要注意的是,由于線是通過點來描繪的,每一個點都由[x,y,z]三個坐标值來确定,是以這裡

x,y,z

三個數組的元素數量應該是一樣多的。

z軸取值為

np.add(x, y)

。請注意,

np.add

是元素級(element-wise)的運算:它是将x和y兩個數組的元素逐個相加,是以得到的結果仍然是包含了1000個元素的數組。

這段代碼得到的結果如下:

使用Matplotlib繪制3D圖形測試環境準備線形圖散點圖線框圖曲面圖等高線柱狀圖多邊形制作動圖結束語參考資料與推薦讀物

散點圖

Axes3D.scatter 函數用來繪制散點圖。

下面我們再來看一下散點圖。

和線形圖類似,它也是展示若幹個

(x, y, z)

坐标的值。差別在于,這裡僅僅是一些點,沒有通過線連在一起。

下面是一段代碼示例:

# scatter.py

import matplotlib.pyplot as plt
import numpy as np

from mpl_toolkits.mplot3d import Axes3D

fig = plt.figure()
ax = fig.gca(projection='3d')

count = 100
range = 100

xs = np.random.rand(count) * range
ys = np.random.rand(count) * range
zs = np.random.rand(count) * range

ax.scatter(xs, ys, zs, s=zs, c=zs)

ax.set_xlabel('X Label')
ax.set_ylabel('Y Label')
ax.set_zlabel('Z Label')

plt.show()           

這段代碼中,我們還設定了三個坐标軸的Label。

另外,所有點的x,y,z軸都是随機的。範圍都在100以内。并且,我們在顯示這些點的時候,根據z值的大小設定了點的顔色和尺寸以示區分。

最終我們得到的圖形如下所示:

使用Matplotlib繪制3D圖形測試環境準備線形圖散點圖線框圖曲面圖等高線柱狀圖多邊形制作動圖結束語參考資料與推薦讀物

線框圖

Axes3D.plot_wireframe 函數用來繪制線框圖。

線框圖要比前面的圖形要複雜一些。

線框圖展示的是一個曲面的架構結構,由于是一個面,是以它在x,y兩個坐标的整個面上都應該有所取值。

前面兩種圖形的x,y軸的值都是一維的數組,而對于線框圖來說,其x,y軸的取值應該是一個二維的矩陣。

例如,我們設定x的範圍是[1, 3]之間,y的範圍是[11, 15]之間。并且,每一個整數坐标取一個點,那麼如下的所有點上都會對應一個z值:

$$

\begin{pmatrix}

[1,11], [2,11], [3,11] \\\

[1,12], [2,12], [3,12] \\\

[1,13], [2,13], [3,13] \\\

[1,14], [2,14], [3,14] \\\

[1,15], [2,15], [3,15]

\end{pmatrix}

而對于描述x,y軸的兩個數組來說,它們各自應該是下面這樣的矩陣:

1, 2, 3 \\\

1, 2, 3

11, 11, 11 \\\

12, 12, 12 \\\

13, 13, 13 \\\

14, 14, 14 \\\

15, 15, 15

這裡的兩個矩陣其實是互相由對方資料的數量而确定尺寸的。

numpy中的

meshgrid

函數剛好可以幫我們完成這個功能,下面是一段代碼示例:

# meshgrid_demo.py

import numpy as np

x = np.arange(1, 4)
y = np.arange(11, 16)
print(x)
print(y)

X, Y = np.meshgrid(x, y)
print(X)
print(Y)           

請仔細觀察一下它的輸出:

[1 2 3]
[11 12 13 14 15]
[[1 2 3]
 [1 2 3]
 [1 2 3]
 [1 2 3]
 [1 2 3]]
[[11 11 11]
 [12 12 12]
 [13 13 13]
 [14 14 14]
 [15 15 15]]           

有了這個基礎之後,我們就可以以此來産生我們需要的線框圖了。

假設我們要展示的函數如下:

z = -x^3 + y^4 , -10 \lt x,y \lt 10

我們可以通過下面這段代碼來生成這個函數的圖形:

# wireframe.py

import matplotlib.pyplot as plt
import numpy as np

from mpl_toolkits.mplot3d import Axes3D

fig = plt.figure()
ax = fig.gca(projection='3d')

x = np.arange(-10, 10, 0.1)
y = np.arange(-10, 10, 0.1)
X, Y = np.meshgrid(x, y)

Z = np.add(-np.power(X, 3), np.power(Y, 4))

surf = ax.plot_wireframe(X, Y, Z)

plt.show()           
請注意,這段代碼中關于

np

的函數都是元素級(element-wise)的運算。

請讀者思考一下,

np.power(X, 3)

X**3

的含義分别是什麼。

這段代碼所得到的圖形如下所示:

使用Matplotlib繪制3D圖形測試環境準備線形圖散點圖線框圖曲面圖等高線柱狀圖多邊形制作動圖結束語參考資料與推薦讀物

曲面圖

Axes3D.plot_surface 函數用來繪制曲面圖。

曲面圖和線框圖類似,它們都是描述三維空間中的曲面的。差別在于:曲面圖中的面是着色的。

下面這段代碼繪制出了下面這個函數的圖形:

z = -x^3 + y^2, -10 \lt x, y \lt 10

# surface.py

import matplotlib.pyplot as plt
import numpy as np

from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D

fig = plt.figure()
ax = fig.gca(projection='3d')

x = np.arange(-10, 10, 0.1)
y = np.arange(-10, 10, 0.1)
X, Y = np.meshgrid(x, y)

Z = np.add(-np.power(X, 3), np.power(Y, 2))

surf = ax.plot_surface(X, Y, Z, cmap=cm.gist_rainbow)
fig.colorbar(surf, shrink=0.5, aspect=5)

plt.show()           

這段代碼整體應該都不難了解,隻有兩個地方需要說明一下:

  1. 這裡通過

    cmap=cm.gist_rainbow

    指定了曲面的顔色。更多的Colormap請到查閱這裡:
  2. 通過

    fig.colorbar(surf, shrink=0.5, aspect=5)

    添加了一個色彩條。

    shrink

    指定了色彩條與圖形高度的比例,

    aspect

    指定了色彩條本身的長寬比。

這段代碼得到的圖形如下所示:

使用Matplotlib繪制3D圖形測試環境準備線形圖散點圖線框圖曲面圖等高線柱狀圖多邊形制作動圖結束語參考資料與推薦讀物

等高線

Axes3D.contour 函數用來繪制等高線。

等高線顧名思義,就是描述高度相等的線。等高線通常伴随主體圖形一起出現,輔助我們觀察主體圖形的一些特性。

有了前面的基礎,繪制等高線也就很容易了。

下面這段代碼繪制出了以下這個函數的線框圖以及等高線:

Z = -x^4 + Y^4, -10 \lt x, y \lt 10

# contour.py

import matplotlib.pyplot as plt
import numpy as np

from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D

fig = plt.figure()
ax = fig.gca(projection='3d')

x = np.arange(-10, 10, 0.1)
y = np.arange(-10, 10, 0.1)
X, Y = np.meshgrid(x, y)

Z = np.add(-np.power(X, 4), np.power(Y, 4))

ax.set_xlabel("X")
ax.set_ylabel("Y")
ax.plot_wireframe(X, Y, Z, alpha=0.1)
ax.contour(X, Y, Z, cmap=cm.Accent, linewidths=2)

plt.show()           

為了便于觀察等高線,我們将主體圖形的線框圖透明度設為0.1,然後将等高線的粗度設定為2。

上面這段代碼得到的圖形如下所示:

使用Matplotlib繪制3D圖形測試環境準備線形圖散點圖線框圖曲面圖等高線柱狀圖多邊形制作動圖結束語參考資料與推薦讀物
這個圖形比較複雜,單從一個角度不太容易看清楚其完整結構,文末我們會講解怎麼制作一副動态圖來展示圖形的全貌。

柱狀圖

Axes3D.bar 函數用來繪制柱狀圖。

柱狀圖也是很常用的圖。

下面這段代碼展示了這樣一種場景:在一副圖中,對比一個城市四年期間每個月的降水量。

在這幅圖中,每一年的12個月是一組柱狀圖。四年的資料進行了前後的對比展示。

代碼如下:

# bar.py

import matplotlib.pyplot as plt
import numpy as np

from matplotlib.collections import PolyCollection
from mpl_toolkits.mplot3d import Axes3D

fig = plt.figure()
ax = fig.gca(projection='3d')

np.random.seed(59)
month = np.arange(1, 12)
years = [2016, 2017, 2018, 2019]

def get_color(value_array):
    color = []
    for v in value_array:
        if (v < 50):
            color.append('y')
        elif (v < 100):
            color.append('g')
        elif (v < 150):
            color.append('b')
        elif (v < 200):
            color.append('c')
        elif (v < 250):
            color.append('m')
        else:
            color.append('r')
    return color

for year, c in zip(years, ['b','c','r','m']):
    value = np.random.rand(len(month)) * 300
    ax.bar(month, value, year, zdir='y', color=get_color(value), alpha=0.7)
    for i in np.arange(0, 12):
        ax.bar

ax.set_xlabel('Month')
ax.set_xticks(np.arange(1, 13))
ax.set_ylabel('Year')
ax.set_yticks(np.arange(2016, 2020))
ax.set_zlabel('Precipitation')

plt.show()           

在這段代碼中,我們通過随機數生成了每個月的降水量。并且根據降水量的程度設定了條柱的顔色以示區分。

我們最終得到的圖形如下所示:

使用Matplotlib繪制3D圖形測試環境準備線形圖散點圖線框圖曲面圖等高線柱狀圖多邊形制作動圖結束語參考資料與推薦讀物

多邊形

Axes3D.add_collection3d 函數用來向圖形中添加3D集合對象。

對于某些資料(例如降水量)來說,我們也可能希望通過多邊形來了解其每個點的走勢。

下面這段代碼通過多邊形的形式展示了和上面柱狀圖一樣的資料。

# poly.py

import matplotlib.pyplot as plt
import numpy as np

from matplotlib.collections import PolyCollection
from mpl_toolkits.mplot3d import Axes3D

fig = plt.figure()
ax = fig.gca(projection='3d')

np.random.seed(59)
month = np.arange(0, 13)
years = [2016, 2017, 2018, 2019]

precipitation = []
for year in years:
    value = np.random.rand(len(month)) * 300
    value[0], value[-1] = 0, 0
    precipitation.append(list(zip(month, value)))

poly = PolyCollection(precipitation, facecolors=['b','c','r','m'])
poly.set_alpha(0.7)

ax.add_collection3d(poly, zs=years, zdir='y')
ax.set_xlabel('Month')
ax.set_xlim3d(0, 12)
ax.set_ylabel('Year')
ax.set_ylim3d(2015, 2020)
ax.set_zlabel('Precipitation')
ax.set_zlim3d(0, 300)

plt.show()           

函數除了支援

PolyCollection

,還支援

LineCollection

PatchCollection

。這一點,讀者可以自行研究一下。

上面這段代碼得到的圖形如下:

使用Matplotlib繪制3D圖形測試環境準備線形圖散點圖線框圖曲面圖等高線柱狀圖多邊形制作動圖結束語參考資料與推薦讀物

制作動圖

很多時候,我們可能需要制作一張動畫圖來展示圖形的全貌,下面我們就來看一下如何做到。

生成不同角度的圖形

為了制作動圖,我們需要先有制作動圖的圖檔素材。

下面我們就以前面等高線那個函數生成的複雜圖形為例,來看看如何生成一個關于這個圖形不同角度的動圖。

相關代碼如下:

# surface_files.py

import matplotlib.pyplot as plt
import numpy as np

from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D

fig = plt.figure(figsize=(10, 8))
ax = fig.gca(projection='3d')

x = np.arange(-10, 10, 0.1)
y = np.arange(-10, 10, 0.1)
X, Y = np.meshgrid(x, y)

Z = np.add(-np.power(X, 4), np.power(Y, 4))

ax.set_xlabel("X")
ax.set_ylabel("Y")
ax.plot_surface(X, Y, Z, cmap=cm.hsv)

for angle in range(95, 180, 3):
    ax.set_zlabel("Angle: " + str(angle))
    ax.view_init(30, angle)
    filename = "./" + str(angle) + ".png"
    plt.savefig(filename)
    print("Save " + filename + " finish")           

這段代碼其實并不複雜,與前面的差別主要就是在于代碼最後的

for

循環。

在這個

for

循環中,我們選取了從95到180這個範圍的角度,每隔3做一次采樣,每次采樣做如下的事情:

  • set_zlabel

    設定了目前旋轉角度的Label
  • ax.view_init(30, angle)

    設定圖形的視角
  • 根據目前角度生成一個單獨的檔案名稱
  • plt.savefig(filename)

    儲存檔案
  • 列印日志

這段代碼執行完成之後,我們就會得到一系列的png檔案。下面我們就通過這些png檔案來生成動圖。

使用ImageMagick

這裡通過一個免費的跨平台工具

ImageMagick

來制作動圖。該工具支援 Linux,Windows,Mac OS X,iOS和Android等各個平台。

首先,我們到這裡進行下載下傳:

Download ImageMagick

請根據你的平台選擇下載下傳哪個版本。

由于我是Mac使用者,是以直接通過下面的指令就可以安裝ImageMagick。

brew install ImageMagick           

安裝好之後,指令行就會有

convert

工具。通過這個工具就可以生成動圖了。

相關指令如下:

convert -delay 50 *.png animated.gif           

當然,你可以研究一下這個指令的其他參數和功能。這裡就不贅述了。

我們最終得到的動圖看起來像下面這個樣子:

使用Matplotlib繪制3D圖形測試環境準備線形圖散點圖線框圖曲面圖等高線柱狀圖多邊形制作動圖結束語參考資料與推薦讀物

結束語

能夠繪制3D圖形将是一項非常有用的技能。因為在今後的機器學習過程中,我們常常會将資料以圖形的形式展示出來,以便我們觀察和了解。

由于篇幅所限,本文隻介紹了一些最基本的用法,但實際上Matplotlib所支援的功能遠不止這些,是以建議讀者朋友們以此為基礎繼續進行更多的探索。

參考資料與推薦讀物