環境
開發工具
VSCode
庫的版本
numpy==1.26.4
matplotlib==3.10.1
ipympl==0.9.7
教材
本書為《Python數據可視化》一書的配套內容,本章為第7章 繪制3D圖表和統計地圖
本章首先介紹了使用mplot3d工具包繪制3D圖表,然后介紹了使用animation模塊制作動畫,最后介紹了使用basemap工具包繪制統計地圖。通過對本章的學習,希望讀者能夠掌握這些工具包和動畫模塊的基本用法。
參考
第7章-繪制3D圖表和統計地圖
7.1 使用mplot3d繪制3D圖表
7.1.1 mplot3d概述
matplotlib不僅專注于二維圖表的繪制,也具有繪制3D圖表、統計地圖的功能,并將這些功能分別封裝到工具包mpl_toolkits.mplot3d、mpl_toolkits.basemap中,另外可以結合animation模塊給圖表添加動畫效果。
mplot3d是matplotlib中專門繪制3D圖表的工具包,它提供了一個重要的類Axes3D,該類繼承自Axes類,使用Axes3D類可以構建一個三維坐標系的繪圖區域。
通過以下兩種方式可以創建Axes3D類的對象。
第一種:Axes3D()方法。
第二種:add_subplot()方法。
Axes3D()方法
Axes3D()是構造方法,它直接用于構建一個Axes3D類的對象。
Axes3D(fig, rect=None, *args, azim=-60, elev=30, zscale=None,
sharez=None, proj_type='persp', **kwargs)
fig:表示繪圖區域所屬的
畫布
。
rect:表示繪圖區域的位置
,它的值是一個元組(left, bottom, width, height)
,元組中的前兩個元素表示繪圖區域左側、底部到畫布左側、底部的距離與畫布寬度、高度的比例,后兩個元素表示繪圖區域的寬度和高度與畫布寬度、高度的比例。
azim :表示方位角
,默認值是-60度。
elev:表示仰角
,默認值是30度。
proj_type:表示投影類型
,該參數支持兩種取值’persp’(透視圖)和 ‘ortho’(正交視圖),默認值為’persp’。
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = Axes3D(fig)
plt.show()
add_subplot()方法
在調用add_subplot()方法添加繪圖區域時為該方法傳入projection=‘3d’,即指定坐標系的類型為三維坐標系,并返回一個Axes3D類的對象。
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
print(type(ax))
plt.show()
輸出如下:
官方
推薦使用add_subplot()方法
創建Axes3D類的對象。
Axes3D類中提供了一些用于設置標題和坐標軸的方法
,關于這些方法及說明具體如下表所示。
7.1.2 繪制常見的3D圖表
常見的3D圖表包括3D線框圖、3D曲面圖、3D柱形圖、3D散點圖等。 Axes3D類中提供了一些
繪制3D圖表的方法
,關于這些方法及其說明如下表所示。
繪制3D線框圖 plot_wireframe()方法
Axes3D類的對象使用plot_wireframe()方法繪制3D線框圖。
plot_wireframe(self, X, Y, Z, *args, **kwargs)
X,Y,Z:表示x、y、z軸的數據。
rcount,ccount:表示每個軸方向上使用的最大樣本量,默認為50。若輸入的樣本量更大,則會采用降采樣的方式減少樣本的數量;若輸入的樣本量為0,則不會對相應軸方向的數據進行采樣。
rstride,cstride:表示采樣的密度。若僅使用參數rstride或cstride中任意一個,則另一個參數默認為1。
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import axes3d
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
# 獲取測試數據
X, Y, Z = axes3d.get_test_data(0.05)
# 繪制 3D線框圖
ax.plot_wireframe(X, Y, Z, rstride=10, cstride=10)
plt.show()
輸出如下:
繪制3D曲面圖-plot_surface()方法
Axes3D類的對象使用plot_surface()方法繪制3D曲面圖。
plot_surface(self, X, Y, Z, *args, norm=None, vmin=None, vmax=None,
lightsource=None, **kwargs)
X,Y,Z:表示x、y、z軸的數據。
rcount,ccount:表示每個坐標軸方向上使用的最大樣本量,默認為50。
rstride,cstride:表示采樣的密度。
color:表示曲面的顏色。
cmap:表示曲面的顏色映射表。
shade:表示是否對曲面進行著色。注意,若使用cmap參數,則不可以使用shade參數。
linewidth :表示線寬。
antialiased:表示是否抗鋸齒。
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from matplotlib import cm
import numpy as np
x1 = np.arange(-5, 5, 0.25)
y1 = np.arange(-5, 5, 0.25)
print(x1.shape,"\n",x1)
print(y1.shape,"\n",y1)
print("---------")
x1, y1 = np.meshgrid(x1, y1)
print(x1.shape,"\n",x1)
print(y1.shape,"\n",y1)
print("---------")
r1 = np.sqrt(x1** 2 + y1 ** 2)
z1 = np.sin(r1)
print(r1.shape,"\n",r1)
print(z1.shape,"\n",z1)
print("---------")
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
# 繪制曲面圖
ax.plot_surface(x1, y1, z1, cmap=cm.coolwarm, linewidth=0, antialiased=False)
# 設置 z 軸刻度的范圍、位置、格式
ax.set_zlim(-1.01, 1.01)
plt.show()
輸出如下:
7.1.3 實例1:三維空間的星星
本實例要求根據一組測試數據,繪制包含若干個五角星的3D散點圖。
# 01_stars_in_3d
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
plt.rcParams["font.sans-serif"] = ["SimHei"]
plt.rcParams["axes.unicode_minus"] = False
# 生成測試數據
x = np.random.randint(0, 40, 5)
y = np.random.randint(0, 40, 5)
z = np.random.randint(0, 40, 5)
# 創建三維坐標系的繪圖區域, 并在該區域中繪制3D散點圖
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
for xx, yy, zz in zip(x, y, z):print(xx, " ",yy, " ",zz)color = 'y'if 10 < zz < 20:color = '#C71585'elif zz >= 20:color = '#008B8B'ax.scatter(xx, yy, zz+3, c=color, marker='*', s=160, linewidth=1, edgecolor='black')ax.text(xx, yy, zz, f'({xx}, {yy}, {zz})', fontsize=10, ha='center', va='bottom')
ax.set_xlabel('x軸')
ax.set_ylabel('y軸')
ax.set_zlabel('z軸')
ax.set_title('3D散點圖', fontproperties='simhei', fontsize=14)
plt.tight_layout()
plt.show()
輸出如下:
38 11 22
0 9 10
20 3 29
15 31 16
29 2 23
7.2 使用animation制作動圖
7.2.1 animation概述
matplotlib在1.1版本的標準庫中加入了動畫模塊——animation,使用該模塊的Animation類可以實現一些基本的動畫效果。Animation類是一個動畫基類。
FuncAnimation類
FuncAnimation是基于函數的動畫類,它通過重復地調用同一函數來制作動畫。
FuncAnimation(fig, func, frames=None, init_func=None, fargs=None,
save_count=None, *, cache_frame_data=True, **kwargs)
fig:表示動畫所在的畫布。
func:表示每幀動畫調用的函數,該函數會被matplotlib庫自動調用,并在被調用時將frames中迭代的下一個值傳遞給它的第一個參數。
frames:表示動畫的長度(一次動畫包含的幀數),該參數的值是一個可迭代對象。
init_func:表示用于開始繪制幀的函數,它會在第一幀動畫之前調用一次。若未設置該參數,則程序將使用frames 中第一項的繪圖結果。
fargs:表示傳遞給func函數的其它參數。
interval:表示更新動畫的頻率,以毫秒為單位,默認為200。
blit:表示是否更新所有的點,默認為False。
# 以qt5為圖形界面后端
# %matplotlib 可以使用的后端有:
# inline:將圖形嵌入到Jupyter Notebook中
# notebook:將圖形嵌入到Jupyter Notebook中
# qt5:在單獨的窗口中顯示圖形
# wx:在單獨的窗口中顯示圖形
# tk:在單獨的窗口中顯示圖形
# osx:在單獨的窗口中顯示圖形
# pdf:將圖形保存為PDF文件
# svg:將圖形保存為SVG文件
# ps:將圖形保存為PS文件
# agg:將圖形保存為AGG文件
# svgz:將圖形保存為SVGZ文件# %matplotlib widget
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation # 導入動畫類
x = np.arange(0, 2 *np.pi, 0.01)
fig, ax = plt.subplots()
line, = ax.plot(x, np.sin(x))
# 定義每幀動畫調用的函數
def animate(i):line.set_ydata(np.sin(x + i / 10.0))if(i%10==0):# ax.text(1+i%2, 0, i)print(i)passreturn line
# 定義初始化幀的函數
def init():line.set_ydata(np.sin(x))return line
ani = FuncAnimation(fig=fig, func=animate, frames=100, init_func=init, interval=20, blit=False)
plt.show()
ArtistAnimation類
ArtistAnimation是基于一組Artist對象的動畫類,它通過每幀繪制一個或一組Artist對象制作動畫。
ArtistAnimation(fig, artists, interval, repeat_delay, repeat,
blit, *args, **kwargs)
fig:表示動畫所在的畫布。
artists:表示一組Artist 對象的列表。
interval:表示更新動畫的頻率,以毫秒為單位,默認為200。
repeat_delay:表示再次播放動畫之前延遲的時長。
repeat:表示是否重復播放動畫。
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import ArtistAnimation
x = np.arange(0, 2 * np.pi, 0.01)
fig, ax = plt.subplots()
arr = []
for i in range(5):line = ax.plot(x, np.sin(x + i))arr.append(line)# 根據arr存儲的一組圖形創建動畫
ani = ArtistAnimation(fig=fig, artists=arr, repeat=True)
plt.show()
大家希望將動畫存儲為視頻文件,可以先安裝ffmpeg或mencoder,之后使用Animation類的save()方法將每一幀動畫存儲為視頻文件。
7.2.2 實例2:三維空間閃爍的星星
本實例要求在7.1.3繪制的3D散點圖中添加動畫,實現五角星由紅色到白色最的閃爍效果 。
# 02_twinkling_stars_in_3d
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.animation import FuncAnimation
plt.rcParams["font.sans-serif"] = ["SimHei"]
plt.rcParams["axes.unicode_minus"] = False
# 生成測試數據
xx = np.array([13, 5, 25, 13, 9, 19, 3, 39, 13, 27])
yy = np.array([4, 38, 16, 26, 7, 19, 28, 10, 17, 18])
zz = np.array([7, 19, 6, 12, 25, 19, 23, 25, 10, 15])
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
# 繪制初始的3D散點圖
star = ax.scatter(xx, yy, zz, c='#C71585', marker='*', s=160, linewidth=1, edgecolor='black')
# 每幀動畫調用的函數
def animate(i):if i % 2:color = '#C71585'else:color = 'white'next_star = ax.scatter(xx, yy, zz, c=color, marker='*', s = 160, linewidth=1, edgecolor='black')return next_star
def init():return star
ani = FuncAnimation(fig=fig, func=animate, frames=None, init_func =init, interval=1000, blit=False)
ax.set_xlabel('x軸')
ax.set_ylabel('y軸')
ax.set_zlabel('z軸')
ax.set_title('3D散點圖', fontproperties='simhei', fontsize=14)
plt.tight_layout()
plt.show()
7.3 使用basemap繪制統計地圖
7.3.1 basemap概述
統計地圖是以地圖為底本,用各種幾何圖形、實物形象或不同線紋、顏色等表明指標的大小及其分布狀況的圖形,它是統計圖形與地圖的結合,既可以突出說明某些現象在地域上的分布,也可以對某些現象進行不同地區間的比較,還可以表明現象所處的地理位置及與其他自然條件的關系等。
basemap是matplotlib中用于繪制地圖背景的工具包,它一般情況下不會參與繪圖操作,而是將給定的地理坐標轉換到地圖投影上,之后將數據交給matplotlib進行繪圖。
按照basemap
pip install basemap
到入basemap
from mpl_toolkits.basemap import Basemap
basemap工具包中提供了一個重要的類Basemap, 表示基礎地圖背景,創建Basemap類的對象時可以指定地圖投影的類型和要處理的地球區域等一些內容。
Basemap(llcrnrlon=None, llcrnrlat=None, urcrnrlon=None, urcrnrlat=None, llcrnrx=None, …, ax=None)
lon_0,lat_0:表示所需地圖投影區域中心的經度或緯度。
llcrnrlon,llcrnrlat:表示地圖投影區域左下角的經度或緯度。
urcrnrlon,urcrnrlat:表示地圖投影區域右上角的經度或緯度。
width,height:表示所需地圖投影區域的寬度和高度。
rsphere:表示投影中使用的球體的半徑,默認值為6370997米。
resolution:表示包括海岸線、湖泊等的分辨率,可以取值為’c’(粗略,默認值)、’l’(低)、’i’(中級)、’h’(高)、’f’(完整)或None。
area_thresh:表示不會繪制海岸線或湖泊的閾值。
anchor:表示地圖置于繪圖區域的方式,默認為C,表示地圖居中。
projection:表示地圖投影的類型,默認值為cyl(等距圓柱投影)。
projection參數的常用取值及說明如下所示。
確定地圖背景的投影區域之后,我們還需要對待處理的區域進行完善,比如在該區域中繪制河岸線、河流和地區或國家邊界等。Basemap類中提供了一些繪制地圖背景的方法。
擁有地圖背景之后便可以使用matplotlib在地圖上根據數據繪制圖形。為方便用戶操作,Basemap類中提供了一些在地圖上繪制數據的方法(這些方法其實簡單地轉發到Axes實例方法,且進行了一些額外的處理和參數檢查) 。
7.3.2 實例3:美國部分城鎮人口分布
本實例要求根據下表的數據(存儲于2014_us_cities.csv文件中),獲取前500條數據,將獲取的lat和lon兩列的地理坐標轉換到地圖投影中,將pop列的數據繪制成氣泡并顯示到地圖上。
# 03_twinkling_stars_in_3d
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
import os
os.chdir(os.path.dirname(os.path.abspath(__file__)))
print(os.getcwd())
plt.rcParams["font.sans-serif"] = ["SimHei"]
plt.rcParams["axes.unicode_minus"] = False
# 創建 Basemap 對象
map = Basemap(projection='stere', lat_0=90, lon_0=-105, llcrnrlat=23.41, urcrnrlat=45.44, llcrnrlon=-118.67, urcrnrlon=-64.52, rsphere=6371200., resolution='l', area_thresh=10000)
map.drawmapboundary() # 繪制地圖投影周圍邊界
map.drawstates() # 繪制州界
map.drawcoastlines() # 繪制海岸線
map.drawcountries() # 繪制國家邊界
# 繪制緯線
parallels = np.arange(0., 90, 10.)
map.drawparallels(parallels, labels=[1, 0, 0, 0], fontsize=10)
# 繪制經線
meridians = np.arange(-110., -60., 10.)
map.drawmeridians(meridians, labels=[0, 0, 0, 1], fontsize=10)posi = pd.read_csv("./素材/2014_us_cities.csv")
# 從3228組城市數據中選擇500 組數據
lat = np.array(posi["lat"][0:500]) # 獲取緯度值
lon = np.array(posi["lon"][0:500]) # 獲取經度值
pop = np.array(posi["pop"][0:500], dtype=float) # 獲取人口數
# 氣泡圖的氣泡大小
size = (pop / np.max(pop)) * 1000
x, y = map(lon, lat)
map.scatter(x, y, s=size)
plt.title('2014年美國部分城鎮的人口分布情況')
plt.show()
輸出如下: