使用正確的圖表理解數據

在本文中,我們將更多注意力放在展現的數據所表達的含義上,以及如何通過圖表把它有效地表達出來。我們將展示一些新的技術和圖表,當知道想要傳達給用戶什麼信息後,我們對這些圖表的理解會更深刻。有這樣的一個問題:“為什麼要以這種方式展示數據?”這在數據探索階段是最重要的一個問題。如果沒能很好地理解數據就把它以某種形式展示出來,那麼毫無疑問,讀者也將難以正確地理解這些數據。

1.1 理解對數圖

很多情況下,在讀日報及類似的文章時,人們常常發現媒體機構用圖表歪曲了事實。一個常見的例子是用線性標度來創建所謂的恐慌圖。圖表中有一個在很長一段時間(若干年)內持續增長的值,其起始值要比最新的值小好幾個量級。然而在正確的可視化時,這些值可以(並且通常應該)用線形圖或者近似線性的圖表表示,把它們要強調的一些恐慌因素忽略。

1.1.1 準備工作

使用對數標度時,連續值的比例是常量。這在讀對數圖表時是非常重要的。使用線性(算術)標度時,連續值之間的距離是常量。換句話說,對數圖表按數量級順序有一個常量的距離。這在接下來的圖表中可以看到,生成圖表的代碼在後面也會解釋。

根據一般經驗,遇到以下情況應該使用對數標度。

當要展示的數據的值跨越好幾個量級時。

當要展示的數據有朝向大值(一些數據點比其他數據大很多)的傾斜度時。

當要展示變化率(增長率),而不是值的變化時。

不要盲目地遵循這些規則,它們更像是指導,而不是規則,要始終依靠你自己對於手頭的數據和項目,或者客戶對你提出的需求作判斷。

根據數據範圍的不同,我們應該使用不同的對數底。對數的標準底是10,但是如果數據範圍比較小,以2為底數會好一些,因為其會在一個較小的數據範圍下有更多的分辨率。

如果有適合在對數標度上顯示的數據範圍,我們會注意到,以前非常靠近而難以判斷差異的值現在很好地區分開了。這讓我們很容易讀懂原來在線性標度下難以理解的數據。

對於長時間範圍的數據的增長率圖表,我們想看的不是在時間點所測量的絕對值,而是其在時間上的增長。雖然我們仍可以得到絕對值信息,但是這些信息的優先級較低。

再者,如果數據分佈存在一個正偏態,例如工資,取值(工資)的對數能讓數據更合乎模型,因為對數變換能提供一個更加正常的數據分佈。

1.1.2 操作步驟

我們將用一段代碼來證明上面所述的內容。這段代碼用不同的標度(線性和對數)在兩個不同的圖表中顯示了兩個相同的數據集合(一個線性的,一個對數的)。

我們將藉助後面的代碼實現下面的步驟。

(1)生成兩個簡單的數據集合:指數/對數y和線性z。

(2)創建一個包含4個子區的圖形。

(3)創建兩個包含數據集合y的子區:一個為對數標度,一個為線性標度。

(4)創建兩個包含數據集合z的子區:一個為對數標度,一個為線性標度。

代碼如下:

from matplotlib import pyplot as plt

import numpy as np

x = np.linspace(1, 10)

y = [10 ** el for el in x]

z = [2 * el for el in x]

fig = plt.figure(figsize=(10, 8))

ax1 = fig.add_subplot(2, 2, 1)

ax1.plot(x, y, color='blue')

ax1.set_yscale('log')

ax1.set_title(r'Logarithmic plot of $ {10}^{x} $ ')

ax1.set_ylabel(r'$ {y} = {10}^{x} $')

plt.grid(b=True, which='both', axis='both')

ax2 = fig.add_subplot(2, 2, 2)

ax2.plot(x, y, color='red')

ax2.set_yscale('linear')

ax2.set_title(r'Linear plot of $ {10}^{x} $ ')

ax2.set_ylabel(r'$ {y} = {10}^{x} $')

plt.grid(b=True, which='both', axis='both')

ax3 = fig.add_subplot(2, 2, 3)

ax3.plot(x, z, color='green')

ax3.set_yscale('log')

ax3.set_title(r'Logarithmic plot of $ {2}*{x} $ ')

ax3.set_ylabel(r'$ {y} = {2}*{x} $')

plt.grid(b=True, which='both', axis='both')

ax4 = fig.add_subplot(2, 2, 4)

ax4.plot(x, z, color='magenta')

ax4.set_yscale('linear')

ax4.set_title(r'Linear plot of $ {2}*{x} $ ')

ax4.set_ylabel(r'$ {y} = {2}*{x} $')

plt.grid(b=True, which='both', axis='both')

plt.show()

代碼將生成圖1-1所示的圖表。


使用正確的圖表理解數據

圖1-1

1.1.3 工作原理

我們生成一些樣本數據和兩個相關的變量:y和z。變量y被表示為數據x的指數函數,變量z是x的簡單線性函數。這展示了線性圖表和指數圖表的區別。

然後創建4個子區,上面一行子區是關於數據(x,y)的,下面一行子區是關於數據(x,z)的。

從左手邊看,y軸列為對數標度;從右手邊看,y軸列為線性標度。通過set_yscale('log')分別對每一個座標軸進行設置。

我們為每一個子區設置標題和標籤,標籤描述了所繪製的函數。

通過plt.grid(b=True, which='both', axis='both'),我們為每個圖的兩個座標軸和主次刻度打開網格顯示。

我們觀察到,在線性圖表中線性函數是直線,在對數圖表中對數函數也是直線。

1.2 理解頻譜圖

頻譜圖是隨時間變化的頻譜表現,它顯示了信號的頻譜強度隨時間的變化。

頻譜圖是把聲音或者其他信號的頻譜以可視化的方式呈現出來。它被用在很多科學領域中,從聲音指紋如聲音識別,到雷達工程學和地震學。

通常,頻譜圖的佈局如下:x軸表示時間,y軸表示頻率,第三個維度是頻率—時間對的幅值,通過顏色表示。因為這是三維的數據,因此我們也可以創建3D圖表來表示,其中強度表示為z軸上的高度。3D圖表的問題是人們不太容易理解以及進行比較,而且它比2D圖表佔用更多的空間。

1.2.1 準備工作

對於嚴謹的信號處理,我們將會研究更低級別的細節,進而能從中發現模式並自動識別一定的特徵。但是對於本節數據可視化的內容,我們將藉助一些著名的Python庫來讀取音頻文件,對它進行採樣,然後繪製出頻譜圖。

為了能讀取WAV文件並把聲音可視化出來,需要做一些準備工作。我們需要安裝libsndfile1系統庫來讀/寫音頻文件。這可以通過你喜歡的包管理工具完成。對於Ubuntu,使用以下命令:

$ sudo apt-get install libsndfilel-dev.

安裝dev包非常重要,它包含了頭文件,從而通過pip可以創建scikits.audiolab模塊。

我們也可以安裝libasound和ALSA(Advanced Linux Sound Architecture,高級Linux聲音體系)頭來避免編譯時警告。這是可選的,因為我們不打算使用ALSA庫提供的特性。對於Ubuntu Linux,執行以下命令:

$ sudo apt-get install libasound2-dev

我們用pip安裝用來讀取WAV文件的scikits.audiolab:

$ pip install scikits.audiolab

1.2.2 操作步驟

本節將使用預錄製的聲音文件test.wav,該文件可以在本書的文件代碼庫中找到,但你也可以自己生成一個樣本文件。

在這個例子中,我們順序地執行下面的步驟。

(1)讀取包含一個已經錄製的聲音樣本的WAV文件。

(2)通過NFFT設置用於傅里葉變換的窗口長度。

(3)在採樣時,使用noverlap設置重疊的數據點。

import os

from math import floor, log

from scikits.audiolab import Sndfile

import numpy as np

from matplotlib import pyplot as plt

# Load the sound file in Sndfile instance

soundfile = Sndfile("test.wav")

# define start/stop seconds and compute start/stop frames

start_sec = 0

stop_sec = 5

start_frame = start_sec * soundfile.samplerate

stop_frame = stop_sec * soundfile.samplerate

# go to the start frame of the sound object

soundfile.seek(start_frame)

# read number of frames from start to stop

delta_frames = stop_frame - start_frame

sample = soundfile.read_frames(delta_frames)

map = 'CMRmap'

fig = plt.figure(figsize=(10, 6), )

ax = fig.add_subplot(111)

# define number of data points for FT

NFFT = 128

# define number of data points to overlap for each block

noverlap = 65

pxx, freq, t, cax = ax.specgram(sample, Fs=soundfile.samplerate,

NFFT=NFFT, noverlap=noverlap,

cmap=plt.get_cmap(map))

plt.colorbar(cax)

plt.xlabel("Times [sec]")

plt.ylabel("Frequency [Hz]")

plt.show()

1.2.3 工作原理

首先需要加載一個聲音文件,這通過調用scikits.audiolab.SndFile方法並傳入一個文件名來完成。該方法將實例化一個聲音對象,通過該對象我們可以查詢數據並調用其中的方法。

為了讀取頻譜圖所需要的數據,需要從聲音對象中讀取數據幀。這通過read_frames()完成,該方法接收開始幀和結束幀的參數。把採樣率和想要可視化的時間點(start, end)相乘便可以計算出幀數量。

1.2.4 補充說明

如果找不到音頻文件(wave),可以生成一個。生成方法很簡單,具體如下。

import numpy

def _get_mask(t, t1, t2, lvl_pos, lvl_neg):

if t1 >= t2:

raise ValueError("t1 must be less than t2")

return numpy.where(numpy.logical_and(t > t1, t < t2), lvl_pos,

lvl_neg)

def generate_signal(t):

sin1 = numpy.sin(2 * numpy.pi * 100 * t)

sin2 = 2 * numpy.sin(2 * numpy.pi * 200 * t)

# add interval of high pitched signal

sin2 = sin2 * _get_mask(t,2,5,1.0,0.0)

noise = 0.02 * numpy.random.randn(len(t))

final_signal = sin1 + sin2 + noise

return final_signal

if __name__ == '__main__':

step = 0.001

sampling_freq=1000

t = numpy.arange(0.0, 20.0, step)

y = generate_signal(t)

# we can visualize this now

# in time

ax1 = plt.subplot(211)

plt.plot(t, y)

# and in frequency

plt.subplot(212)

plt.specgram(y, NFFT=1024, noverlap=900,

Fs=sampling_freq, cmap=plt.cm.gist_heat)

plt.show()

這將生成圖1-2所示的信號,其中頂部的圖形是生成的信號。這裡,x軸表示時間,y軸表示信號的幅值。底部的圖形是相同的信號在頻率域中的呈現。這裡,x軸如頂部圖一樣表示時間(通過選擇採樣率來匹配時間),y軸表示信號的頻率。


使用正確的圖表理解數據

圖1-2


分享到:


相關文章: