【MATLAB】contourf 基礎用法與對數作圖

  • 848
  • 0
  • 2019-10-16

在 MATLAB 中有著堆積如山的繪圖指令。
通常想用時就找資料,用完就會忘記,然後重複這個過程。

經過第 N 次查資料之後,夏恩終於受夠了這個重複的過程啦!
這次就來記下 contourf 用法。

作業系統:Microsoft Windows 10 專業版
軟體版本:
    MATLAB      Version 9.6      (R2019a)

繪製資料:data.mat
資料說明:為 31 x 31 的方陣,其值介於 0~1 之間。

使用前,請先匯入資料:

load('data.mat');

這是夏恩之前在跑 SVM 參數最佳化得到誤差值表格,
但這不是本次要討論的範圍,就當作是地上撿到的資料就好。

一. 基礎用法

函數 contourf 與 contour 基本上一樣,就是繪製等高線圖。
差異在於 contourf 會填滿同類區間,畫張圖來看看就知道。

subplot(121)
contour(data)
title('contour function') % 小標-1

subplot(122)
contourf(data)
title('contourf function') % 小標-2

% 大標題
h = sgtitle('contour vs contourf');
h.FontSize = 16;
h.FontWeight = 'bold';

其他可調整繪圖參數主要有以下幾個:

1. 等高線數量

調整第二個參數,即可設定等高線數量。

subplot(121)
contourf(data, 1)
title('1 contour line')

subplot(122)
contourf(data, 5)
title('5 contour lines')

2. 等高線粗細 & 型態

使用函數時,直接代入相應參數即可更改線條型態。

subplot(221)
contourf(data, 3, 'LineWidth', 2)
title('LineWidth = 2')

subplot(222)
contourf(data, 3, 'LineStyle', '--')
title('LineStyle = "--"')

% 不要繪製等高線也可以
subplot(224)
contourf(data, 3, 'LineStyle', 'none')
title('LineStyle = "none"')

3. 配色盤(colormap)

使用者可以手動調整等高線圖的配色盤,使用 colormap 函數:

% colormap('map') or colormap map

contourf(data, 10)
colormap gray

想要使用哪種配色,任君挑選。

配色盤圖片擷取自 MATLAB 說明文件

在繪製等高線的時候,您也可以自行設定配色盤的顏色數量。
舉例來說,若您只畫 1 條等高線會產生 2 個區域,就設定 colormap( jet( N+1 ) )。
意思是使用 jet 色階,產生 N+1 種顏色,其中 N 為等高線數量。

需要特別注意的是「等高線數量」與「配色盤的顏色」是分開作用於圖面上!

若等高線數量與配色盤的顏色不匹配的話,
使用 colorbar 的時候會出現不存在於圖面的顏色。

不過這其實不是什麼大不了的問題,畫起來只是會讓您有點困惑,但不會影響其正確性。
是否要修改這一塊的參數就見仁見智吧。

% contour levels
levels = 4;

contourf(data, levels)
colormap(jet(levels+1))
colorbar
title('color = levels+1')

contourf(data, levels)
colormap(jet(levels+10))
colorbar
title('color = levels+10')

備註:本圖是分開繪製,再另外拼貼。
           在 subplot 中使用 colormap,會將所有子圖全部套用相同的調色盤。

4. 設定資料邊界

有時候我們只關心某個區間的資料,這時可用 caxis 來調整圖像。
caxis 的用法是:caxis( [ 下界 , 上界 ] ),若在此界線之外,則全部配給同一顏色。
例如,下圖範例中,低於 0.3 全給藍色,高於 0.7 全給黃色。

contourf(data, 10)

% 低於 0.3 只給一種顏色
% 高於 0.7 亦只給一種顏色
caxis([0.3 0.7])

5. 修改座標軸範圍

在本例中,資料大小是 31 x 31 的陣列。
畫成圖後,系統會自動給定 1 至 31 的座標軸。

那萬一我想修改座標軸的話呢?

這時候,請使用 xticks 搭配 xticklabels 來修改。
例如,夏恩想把座標軸改成 -14 至 +14,間隔為 2,改法如下:

% contour levels
levels = 10;

contourf(data, levels, 'LineStyle', 'none')
grid on

% x 座標軸修改
xticks(2:2:30)
xticklabels(gca, -14:2:14)

% y 座標軸修改
yticks(2:2:30)
yticklabels(gca, -14:2:14)

二、進階應用

在本範例所提供的資料中,數值的分布兩極,因此直接作圖的效果並不好。
這種情況下,將資料進行對數轉換,通常可以看到更多的細節。

畫一張對數轉換前後的比較圖,就可以一目了然。

% Natural logarithm
ln_data = log(data);

subplot(121)
normplot(data(:))
title('original')

subplot(122)
normplot(ln_data(:))
title('log')

左圖是原始資料,可以看到大部分的資料都落在 0~0.1 與 0.8~1 之間。
經過對數轉換,原本 0~0.1 之間的資料就可以看出比較明顯的差異。

接著來看看轉換前後的比較圖,轉換後的資料可以看到更多細節:

到這邊,相信讀者應該也發現了另外一個問題:
對數轉換只會強化靠近 0 的部分,那靠近 1 的資料呢?

若您對靠近 1 的資料也感興趣的話,那夏恩會建議使用 sigmoid 的反轉換。
話不多說,先來畫張 sigmoid 函數圖吧:

% plot sigmoid function

% sigmoid 數學式為 y = 1/(1+e^(-x))
x = -10:0.01:10;
y = 1./(1+exp(-x));
plot(x, y, 'b ','LineWidth', 2)

% 當 X 等於零,其對應值為 0.5
hold on
plot([-10 10], [0.5 0.5], 'k--','LineWidth', 1)
hold off

% 限制圖表範圍,看起來較美觀
grid on
axis([-10 10 -0.1 1.1])

% 手動給定標籤
yticks([0 0.2 0.4 0.5 0.6 0.8 1])

% 使用 latex 語法,撰寫公式文字
text(-9.5, 0.9, 'S(t) =', 'interpreter','latex' , 'FontSize', 14);
text(-7, 0.9, '$\frac{1}{1+e^{-x}}$', 'interpreter','latex', 'FontSize', 24);

% 給一個好聽的標題名稱
title('Sigmoid function')

反轉換的話,數學式改為:x = ln( y/(1-y) )。
即當數值愈靠近 0 或 1,就會被映射成愈大的值,
如此一來,我們就能更清楚地看到兩端值的微小變化。

不過本範例還是繼續使用單純的自然對數作說明,
因為夏恩主要是想說明繪圖語法,不是要討論映射公式。

好的,我們接著讓話題回到對數作圖。

1. 多圖比較

當我們想要比較很多份資料的時候,一定會要求每張等高線圖的色階要相同。
若每份圖表都使用不同的 colorbar,除了造成讀者的困擾之外,也失去繪圖的意義。

以本次的資料來看,其值介於 0~1 之間,即對數轉換後的值域為 -Inf~0。
使用負無限大實在太虛無飄渺了,這裡夏恩取資料的最小值作為邊界。

% 設定資料變動範圍
min_boundary = floor(log(min(data(:))));
caxis( [ min_boundary , 0 ] )

其中 min_boundary 的計算結果為 -6,也可以直接手動指定 caxis( [ -6 , 0 ] )。

若我們想把原始資料,以及對數轉換之後的資料放在一起比較的話,
那變動範圍就要設定在 -6~1之間。(原資料為0~1,新資料為-6~0)

% Natural logarithm
ln_data = log(data);
% contour levels
levels = 10;

% ======== 圖1 ======== %
subplot(121)
contourf(data, levels, 'LineStyle', 'none')
title('original')
grid on

% 座標修改
xticks(2:2:30)
xticklabels(gca, -14:2:14)
yticks(2:2:30)
yticklabels(gca, -14:2:14)

% 邊界設定
caxis( [ -6 , 1 ] )
colorbar

% ======== 圖2 ======== %
subplot(122)
contourf(ln_data, levels, 'LineStyle', 'none')

title('log')
grid on

% 座標修改
xticks(2:2:30)
xticklabels(gca, -14:2:14)
yticks(2:2:30)
yticklabels(gca, -14:2:14)

% 邊界設定
caxis( [ -6 , 1 ] )
colorbar

如上圖,當我們使用 caxis 來限定資料的變動範圍之後,
就能成功地把同一個色階表套用在兩張不同的圖面上。

如果這邊沒有使用 caxis 的話,畫起來的圖形如下:
備註:把上述程式的 caxis( [ -6 , 1 ] ) 註解掉,即可畫出下圖。

讀者們可以仔細看一下圖內的數值,每張圖的 colorbar 所對應的值都不一樣。
即左圖的黃色區塊代表著 0.8 以上;右圖的黃色區塊代表 -1~0 的區間,這樣是無法比較的。

2. 一般的 colorbar 調值

當使用者直接調用 colorbar 的時候,其旁邊顯示的數值,預設為自動計算資料分布。
若您覺得不滿意,當然可以手動調整。

先以原始資料為例,我們把值指定顯示 0~1 之間,間隔為 0.1 和 0.2,使用 'YTick' 屬性。

% contour levels
levels = 10;

subplot(121)
contourf(data, levels, 'LineStyle', 'none')

% 指定 colorbar 數值
c = 0:0.1:1;
colorbar('YTick', c)
title('colorbar 間隔=0.1')

subplot(122)
contourf(data, levels, 'LineStyle', 'none')

% 指定 colorbar 數值
c = 0:0.2:1;
colorbar('YTick', c)
title('colorbar 間隔=0.2')

再來,我們可以修改每個 colorbar 標籤上的數值。
例如原本是 0.1, 0.2,..., 1.0,改成10, 20, ..., 100。

這邊要使用 'YTickLabel' 屬性:

% contour levels
levels = 10;

contourf(data, levels, 'LineStyle', 'none')

c = 0:0.1:1;
c_label = 0:10:100;

colorbar('YTick', c, 'YTickLabel', c_label)
title('colorbar 修改 YTickLabel')

備註:本例中,其 colorbar 的值為 10~80,而非從 0~100。
           原因是沒有使用 caxis 限定,因此會透過資料數值自動計算上下界。

3. 對數轉換後的 colorbar 調值

原始資料經過對數轉換後,數值已經和原本截然不同。
直接繪製的意義不大,我們需要把對數值轉換回來,畫到相應的位置。

原始資料中,我們感興趣的數值大概是 0.01, 0.02, ..., 0.1, 0.2, ..., 1.0。
這裡同樣使用 colorbar 中的 'YTick' 屬性與 'YTickLabel' 來修改:

% Natural logarithm
ln_data = log(data);
% contour levels
levels = 10;

contourf(ln_data, levels, 'LineStyle', 'none')

% 邊界設定
min_boundary = floor(log(min(data(:))));
caxis( [ min_boundary , 0 ] )

% colorbar 數值設定
c = log( [0.01, 0.02:0.02:0.1, 0.2:0.2:1] );
c_label = [0.01, 0.02:0.02:0.1, 0.2:0.2:1];

colorbar('YTick', c, 'YTickLabel', c_label)
title('對數轉換時 colorbar 修改')

如上圖,經過調整後的標示法,能夠讓使用者很清楚地看到原本的數值對應的情況。

小結

函數 contourf 的使用說明至此告一段落,其他更瑣碎的技巧也不太常用到。
若有特殊需求,到時候再來找資料即可。

最後附上完整的繪圖程式,有需要的讀者都可自行取用。

% Plot contour figure
% 
% 2019.09.04 Shayne

load('data.mat');

% Natural logarithm
ln_data = log(data);

% contour levels
levels = 20;

contourf(ln_data, levels, 'LineStyle', 'none')
axis equal
grid on

% 座標修改
xticks(2:2:30)
xticklabels(gca, -14:2:14)
yticks(2:2:30)
yticklabels(gca, -14:2:14)

% 邊界設定
min_boundary = floor(log(min(data(:))));
caxis( [ min_boundary , 0 ] )

% colorbar 數值設定
c = log( [0.01, 0.02:0.02:0.1, 0.2:0.2:1] );
c_label = [0.01, 0.02:0.02:0.1, 0.2:0.2:1];
colorbar('YTick', c, 'YTickLabel', c_label)

% 標題 & 座標軸
title('contourf 繪圖範例', 'FontSize', 16)
xlabel('C (=2^n)')
ylabel('epsilon (=2^m)')

參考資料

1. MATLAB contourf function