使用正确的图表理解数据

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/epubit17/article/details/82779786

在本文中,我们将更多注意力放在展现的数据所表达的含义上,以及如何通过图表把它有效地表达出来。我们将展示一些新的技术和图表,当知道想要传达给用户什么信息后,我们对这些图表的理解会更深刻。有这样的一个问题:“为什么要以这种方式展示数据?”这在数据探索阶段是最重要的一个问题。如果没能很好地理解数据就把它以某种形式展示出来,那么毫无疑问,读者也将难以正确地理解这些数据。

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

soundfile = Sndfile(“test.wav”)

start_sec = 0

stop_sec = 5

start_frame = start_sec * soundfile.samplerate

stop_frame = stop_sec * soundfile.samplerate

soundfile.seek(start_frame)

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)

NFFT = 128

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)

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)

ax1 = plt.subplot(211)

plt.plot(t, y)

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

猜你喜欢

转载自blog.csdn.net/epubit17/article/details/82779786