[Python] Matplotlib 圖表的繪製和美化技巧
阿新 • • 發佈:2021-02-20
[TOC]
## 在一張畫布中繪製多個圖表
Matplotlib模組在繪製圖表時,預設先建立一張畫布,然後在畫布中顯示繪製的圖表。
如果想要在一張畫布中繪製多個圖表,可以使用subplot()函式將畫布劃分為幾個區域,然後在各個區域中分別繪製不同的圖表。
subplot()函式的引數為3個整型數字:
+ 第1個數字代表將整張畫布劃分為幾行;
+ 第2個數字代表將整張畫布劃分為幾列;
+ 第3個數字代表要在第幾個區域中繪製圖表,區域的編號規則是按照從左到右、從上到下的順序,從1開始編號。
演示程式碼如下:
```python
import matplotlib.pyplot as plt
# 如果值中有中文字元,則必須在繪製圖表前加上這兩行程式碼
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']
plt.rcParams['axes.unicode_minus'] = False
x = ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']
y = [50, 45, 65, 76, 75, 85, 55, 78, 86, 89, 94, 90]
plt.subplot(2, 2, 1)
plt.pie(y, labels = x, labeldistance = 1.1, startangle = 90, counterclock = False)
plt.subplot(2, 2, 2)
# 引數width用於設定柱子的寬度,預設值為0.8。如果設定為1,則各個柱子會緊密相連;如果設定為大於1的數,則各個柱子會相互交疊
plt.bar(x, y, width = 0.5, color = 'r')
plt.subplot(2, 2, 3)
# 引數color用於設定柱子的填充顏色,具體取值見後面的說明
plt.stackplot(x, y, color = 'r')
plt.subplot(2, 2, 4)
plt.plot(x, y, color = 'r', linestyle = 'solid', linewidth = 2, marker = 'o', markersize = 10)
plt.show()
```
輸出結果:
![](https://img2020.cnblogs.com/blog/2200001/202102/2200001-20210220161523307-1212189385.png)
用顏色名的英文單詞或其簡寫定義的8種基礎顏色,具體見:
![](https://img2020.cnblogs.com/blog/2200001/202102/2200001-20210220162749196-934552246.png)
## 加圖表元素
```python
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']
plt.rcParams['axes.unicode_minus'] = False
x = ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']
y = [50, 45, 65, 76, 75, 85, 55, 78, 86, 89, 94, 90]
# 這裡加了標籤
plt.bar(x, y, width=0.6, color='r', label='銷售額(萬元)')
# 這裡加了標題,loc還可以是right和left
plt.title(label='銷售額對比圖', fontdict={'family': 'KaiTi', 'color': 'k', 'size': 30}, loc='center')
# 座標上的標籤
plt.xlabel('月份', fontdict={'family': 'SimSun', 'color': 'k', 'size': 20}, labelpad=20)
plt.ylabel('銷售額', fontdict={'family': 'SimSun', 'color': 'k', 'size': 20}, labelpad=20)
# legend()函式用於新增圖例
plt.legend(loc='upper left', fontsize=15)
# zip() 函式用於將可迭代的物件作為引數,將物件中對應的元素打包成一個個元組,然後返回由這些元組組成的列表。
for a,b in zip(x, y):
# text()函式的功能是在圖表座標系的指定位置新增文字。引數ha是horizontalalignment的簡稱,相對應有va
plt.text(x=a, y=b, s=b, ha='center', va='bottom', fontdict={'family': 'KaiTi', 'color': 'k', 'size': 20})
plt.show()
```
輸出結果
![](https://img2020.cnblogs.com/blog/2200001/202102/2200001-20210220163727288-1207960104.png)
## 氣泡圖
氣泡圖是一種展示三個變數之間關係的圖表,它其實是在散點圖的基礎上升級改造而成的,在原有的x座標和y座標兩個變數的基礎上,引入第三個變數,並用氣泡的大小表示。
```
pip install openpyxl
```
產品銷售統計.xls 內容
| 產品名稱 | 銷售量(件) | 銷售額(元) | 毛利率(%) |
| ---- | ------ | ------ | ------ |
| 牛仔褲 | 125 | 6800 | 30 |
| 連衣裙 | 278 | 5600 | 20 |
| 運動褲 | 366 | 7800 | 35 |
| 短褲 | 452 | 5800 | 10 |
| 短裙 | 365 | 5400 | 50 |
| 揹帶褲 | 258 | 10000 | 22 |
| 半身裙 | 369 | 3600 | 15 |
| 闊腿褲 | 566 | 7800 | 8 |
程式碼如下:
```python
import matplotlib.pyplot as plt
import pandas as pd
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']
plt.rcParams['axes.unicode_minus'] = False
data = pd.read_excel('產品銷售統計.xls')
n = data['產品名稱']
x = data['銷售量(件)']
y = data['銷售額(元)']
z = data['毛利率(%)']
plt.scatter(x, y, s=z * 300, color='r', marker='o')
plt.xlabel('銷售量(件)', fontdict={'family': 'Microsoft YaHei', 'color': 'k', 'size': 20}, labelpad=20)
plt.ylabel('銷售額(元)', fontdict={'family': 'Microsoft YaHei', 'color': 'k', 'size': 20}, labelpad=20)
plt.title('銷售量、銷售額與毛利率關係圖', fontdict={'family': 'Microsoft YaHei', 'color': 'k', 'size': 30}, loc='center')
for a, b, c in zip(x, y, n):
plt.text(x=a, y=b, s=c, ha='center', va='center', fontsize=15, color='w')
plt.xlim(50, 600)
plt.ylim(2900, 11000)
plt.show()
```
輸出結果:
![](https://img2020.cnblogs.com/blog/2200001/202102/2200001-20210220165618772-1044548018.png)
## 組合圖
組合圖是指在一個座標系中繪製多張圖表,其實現方式也很簡單,在使用Matplotlib模組中的函式繪製圖表時設定多組y座標值即可。
銷售業績表.xls
| 月份 | 銷售額(萬元) | 同比增長率 |
| ---- | ------- | ----- |
| 1月 | ¥36.00 | 10% |
| 2月 | ¥25.00 | 8% |
| 3月 | ¥36.12 | 20% |
| 4月 | ¥69.30 | 50% |
| 5月 | ¥26.90 | 15% |
| 6月 | ¥32.00 | 11% |
| 7月 | ¥45.00 | 26% |
| 8月 | ¥56.00 | 13% |
| 9月 | ¥25.60 | 4% |
| 10月 | ¥36.21 | 5% |
| 11月 | ¥25.00 | 7% |
| 12月 | ¥59.00 | 30% |
```python
import pandas as pd
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']
plt.rcParams['axes.unicode_minus'] = False
data = pd.read_excel('銷售業績表.xlsx')
x = data['月份']
y1 = data['銷售額(萬元)']
y2 = data['同比增長率']
plt.bar(x, y1, color = 'c', label = '銷售額(萬元)')
plt.legend(loc = 'upper left', fontsize = 15)
# 使用twinx()函式為圖表新增次座標軸
plt.twinx()
plt.plot(x, y2, color = 'r', linewidth = 3, label = '同比增長率')
plt.legend(loc = 'upper right', fontsize = 15)
plt.show()
```
輸出結果:
![](https://img2020.cnblogs.com/blog/2200001/202102/2200001-20210220170456784-1838638597.png)
## 直方圖
直方圖用於展示資料的分佈情況,使用Matplotlib模組中的hist()函式可以繪製直方圖
[客戶年齡統計表.zip](https://files.cnblogs.com/files/feily/%E5%AE%A2%E6%88%B7%E5%B9%B4%E9%BE%84%E7%BB%9F%E8%AE%A1%E8%A1%A8.zip)
```python
import pandas as pd
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']
plt.rcParams['axes.unicode_minus'] = False
data = pd.read_excel('客戶年齡統計表.xlsx')
x = data['年齡']
plt.hist(x, bins = 9)
plt.xlim(15, 60)
plt.ylim(0, 40)
plt.title('年齡分佈直方圖', fontsize = 20)
plt.xlabel('年齡')
plt.ylabel('人數')
plt.grid(b = True, linestyle = 'dotted', linewidth = 1)
plt.show()
```
輸出結果
![](https://img2020.cnblogs.com/blog/2200001/202102/2200001-20210220172015289-863829486.png)
## 雷達圖
雷達圖可以同時比較和分析多個指標。該圖表可以看成一條或多條閉合的折線,因此,使用繪製折線圖的plot()函式也可以繪製雷達圖。
汽車效能指標分值統計表.xlsx
| 效能評價指標 | A品牌 | B品牌 | C品牌 |
| ------ | ---- | ---- | ---- |
| 動力性 | 1 | 3 | 10 |
| 燃油經濟性 | 2 | 6 | 7 |
| 制動性 | 1 | 10 | 5 |
| 操控穩定性 | 3 | 10 | 2 |
| 行駛平順性 | 2 | 6 | 1 |
| 通過性 | 4 | 7 | 2 |
| 安全性 | 8 | 2 | 1 |
| 環保性 | 9 | 1 | 3 |
| 方便性 | 10 | 3 | 0 |
| 舒適性 | 8 | 2 | 1 |
| 經濟性 | 4 | 1 | 10 |
| 容量性 | 2 | 2 | 8 |
程式碼如下:
```python
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
data = pd.read_excel('汽車效能指標分值統計表.xlsx')
data = data.dropna(axis=1)
data = data.set_index('效能評價指標')
data = data.T
data.index.name = '品牌'
def plot_radar(data, feature):
columns = ['動力性', '燃油經濟性', '制動性', '操控穩定性', '行駛平順性', '通過性', '安全性', '環保性', '方便性', '舒適性', '經濟性', '容量性']
colors = ['r', 'g', 'y']
# 設定雷達圖的角度,用於平分切開一個平面
# linspace(1,10,x) 建立1-10的等差陣列,個數為 x,預設50個;endpoint引數指定是否包含終值,預設值為True,即包含終值。
angles = np.linspace(0.1 * np.pi, 2.1 * np.pi, len(columns), endpoint = False)
# 使雷達圖封閉起來
angles = np.concatenate((angles, [angles[0]]))
# figsize:指定figure的寬和高,單位為英寸;
figure = plt.figure(figsize = (6, 6))
# 設定為極座標格式;subplot(nrows,ncols,sharex,sharey,subplot_kw,**fig_kw)建立單個子圖,下面兩句效果相同
ax = figure.add_subplot(111, polar=True)
# ax = figure.add_subplot(1, 1, 1, projection = 'polar')
for i, c in enumerate(feature):
stats = data.loc[c]
stats = np.concatenate((stats, [stats[0]]))
ax.plot(angles, stats, '-', linewidth = 2, c = colors[i], label = str(c))
ax.fill(angles, stats, color = colors[i], alpha = 0.75)
# bbox_to_anchor這個引數,可以把圖例放在圖外面
# bbox_to_anchor:表示legend的位置,前一個表示左右,後一個表示上下。
# 當使用這個引數時。loc將不再起正常的作用,ncol=3表示圖例三列顯示。
ax.legend(loc = 4, bbox_to_anchor = (1.15, -0.07))
#設定極軸範圍
ax.set_ylim(0,10)
# ax.set_yticklabels([2, 4, 6, 8, 10])
# 新增每個特質的標籤
columns = np.concatenate((columns, [columns[0]]))
ax.set_thetagrids(angles*180/np.pi, columns, fontsize = 12)
#新增標題
plt.title('汽車效能指標雷達圖')
plt.show()
return figure
figure = plot_radar(data, ['A品牌', 'B品牌', 'C品牌'])
# figure = plot_radar(data, ['B品牌'])
```
![](https://img2020.cnblogs.com/blog/2200001/202102/2200001-20210220190834095-1285051144.png)
## 樹狀圖
樹狀圖通過矩形的面積、排列和顏色直觀地展示多個專案的資料比例關係。要繪製該圖表,需結合使用Matplotlib模組與squarify模組。
```python
import squarify as sf
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']
plt.rcParams['axes.unicode_minus'] = False
x = ['上海', '北京', '重慶', '成都', '南京', '青島', '長沙', '武漢', '深圳']
y = [260, 45, 69, 800, 290, 360, 450, 120, 50]
colors = ['lightgreen', 'pink', 'yellow', 'skyblue', 'cyan', 'silver', 'lightcoral', 'orange', 'violet']
percent = ['11%', '2%', '3%', '33%', '12%', '15%', '18%', '5%', '2%']
chart = sf.plot(sizes = y, label = x, color = colors, value = percent, edgecolor = 'white', linewidth = 2)
plt.title(label = '城市銷售額分佈及佔比圖',fontdict = {'family' : 'KaiTi', 'color' : 'k', 'size' : 25})
plt.axis('off')
plt.show()
```
![](https://img2020.cnblogs.com/blog/2200001/202102/2200001-20210220191046317-92980110.png)
## 箱形圖
箱形圖是一種用於展示資料的分佈情況的統計圖,因形狀如箱子而得名。使用Matplotlib模組中的boxplot()函式可以繪製箱形圖。
資料
| 日期 | 成都 | 上海 | 北京 | 重慶 | 南京 |
| ----- | ---- | ---- | ---- | ---- | ---- |
| 1月1日 | 25 | 50 | 52 | 25 | 50 |
| 1月2日 | 12 | 58 | 56 | 26 | 56 |
| 1月3日 | 26 | 60 | 100 | 78 | 58 |
| 1月4日 | 23 | 78 | 125 | 45 | 87 |
| 1月5日 | 18 | 36 | 108 | 46 | 50 |
| 1月6日 | 15 | 69 | 100 | 50 | 60 |
| 1月7日 | 19 | 41 | 85 | 53 | 26 |
| 1月8日 | 20 | 52 | 85 | 61 | 36 |
| 1月9日 | 26 | 53 | 87 | 87 | 69 |
| 1月10日 | 27 | 69 | 86 | 25 | 78 |
| 1月11日 | 28 | 78 | 45 | 16 | 75 |
| 1月12日 | 54 | 80 | 78 | 69 | 80 |
| 1月13日 | 50 | 52 | 73 | 68 | 81 |
| 1月14日 | 51 | 26 | 62 | 45 | 45 |
| 1月15日 | 52 | 28 | 65 | 40 | 65 |
| 1月16日 | 36 | 57 | 90 | 50 | 63 |
| 1月17日 | 38 | 56 | 96 | 60 | 69 |
| 1月18日 | 45 | 89 | 94 | 36 | 64 |
| 1月19日 | 40 | 84 | 25 | 52 | 65 |
| 1月20日 | 41 | 85 | 36 | 54 | 45 |
| 1月21日 | 26 | 80 | 68 | 58 | 52 |
| 1月22日 | 29 | 75 | 78 | 56 | 59 |
| 1月23日 | 36 | 50 | 70 | 52 | 80 |
| 1月24日 | 33 | 25 | 52 | 57 | 29 |
| 1月25日 | 31 | 36 | 51 | 69 | 36 |
| 1月26日 | 15 | 64 | 58 | 54 | 29 |
| 1月27日 | 18 | 56 | 68 | 25 | 90 |
| 1月28日 | 25 | 54 | 78 | 36 | 78 |
| 1月29日 | 14 | 50 | 90 | 78 | 71 |
| 1月30日 | 39 | 44 | 95 | 56 | 75 |
| 1月31日 | 48 | 49 | 84 | 25 | 76 |
```
import pandas as pd
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei']
plt.rcParams['axes.unicode_minus'] = False
data = pd.read_excel('1月銷售統計表.xlsx')
x1 = data['成都']
x2 = data['上海']
x3 = data['北京']
x4 = data['重慶']
x5 = data['南京']
x = [x1, x2, x3, x4, x5]
labels = ['成都', '上海', '北京', '重慶', '南京']
# 引數vert用於設定箱形圖的方向,True表示縱向展示,False表示橫向展示;引數showmeans用於設定是否顯示均值,True表示顯示均值,False表示不顯示均值。
plt.boxplot(x, vert = True, widths = 0.5, labels = labels, showmeans = True )
plt.title('各地區1月銷售額箱形圖', fontsize = 20)
plt.ylabel('銷售額(萬元)')
plt.show()
```
![](https://img2020.cnblogs.com/blog/2200001/202102/2200001-20210220193217166-1848719514.png)
箱形圖中的5條橫線和1個點所代表的含義如下:
+ 下限:指所有資料中的最小值;
+ 下四分位數:又稱“第一四分位數”,指將所有資料從小到大排列後第25%的值;
+ 中位數:又稱“第二四分位數”,指將所有資料從小到大排列後第50%的值;
+ 上四分位數:又稱“第三四分位數”,指將所有資料從小到大排列後第75%的值;
+ 上限:指所有資料中的最大值;
+ 點:指所有資料的平均值。
## 玫瑰圖
玫瑰圖可反映多個維度的資料,它將柱形圖轉化為餅圖,在圓心角相同的情況下,以扇面長度展示指標大小。要繪製玫瑰圖,也要用到繪製柱形圖的bar()函式。
```python
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
# 將風速的分佈設定為4個區間
index = ['0~0.5', '0.6~2.0', '2.1~4.0', '4.1~6.0']
# 設定了16個方向
columns = ['N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW']
# seed()函式用於產生相同的隨機數
np.random.seed(0)
# 建立一個4行16列的DataFrame,其中的資料是30~300範圍內的隨機數,行標籤為第6行程式碼設定的風速分佈區間,列標籤為第7行程式碼設定的方向。
data = pd.DataFrame(np.random.randint(30, 300, (4, 16)), index = index, columns = columns)
N = 16
# 生成16個方向的角度值
theta = np.linspace(0, 2 * np.pi, N, endpoint = False)
# 用於計算扇面的寬度
width = np.pi / N
labels = list(data.columns)
plt.figure(figsize = (6, 6))
ax = plt.subplot(1, 1, 1, projection = 'polar')
for i in data.index:
radius = data.loc[i]
# 使用bar()函式繪製玫瑰圖中的16根柱子,也就是扇面,引數bottom用於設定每根柱子底部的位置,這裡設定為0.0,表示從圓心開始繪製。
ax.bar(theta, radius, width = width, bottom = 0.0, label = i, tick_label = labels)
# 設定0°的方向為“N”,即北方
ax.set_theta_zero_location('N')
# 設定按逆時針方向排列各個柱子
ax.set_theta_direction(-1)
plt.title('各方向風速頻數玫瑰圖', fontsize = 20)
plt.legend(loc = 4, bbox_to_anchor = (1.3, 0.2))
plt.show()
```
![](https://img2020.cnblogs.com/blog/2200001/202102/2200001-20210220193807032-15218021