Acoular是一个Python库,它可以处理来自传声器阵列的声学测量的多通道数据(多达几百个通道),这些数据被存储在HDF5文件中。这篇博文解释了如何将其他格式的数据转换成这种文件格式。作为其他文件格式的例子,我们将使用.csv(逗号分隔文本文件)和.mat(Matlab文件)。
为了演示如何导入和转换数据,我们首先需要得到一些数据。在我们的案例中,我们使用来自Zenodo的数据,在那里,一个有三个来源的场景的64个通道的记录可以用多种不同的格式获得。我们使用Python的urllib进行下载。取决于你的网速,这可能需要一些时间。
import urllib.request
url = 'https://zenodo.org/record/5809069/files/'
for filename in ('three_sources.h5','three_sources.csv',
'three_sourcesv7.mat','three_sourcesv73.mat'):
urllib.request.urlretrieve(url+filename, filename)
现在我们有四个不同格式的相同数据。Acoular的HDF5、.csv、Matlab版本<=7和Matlab版本>=7.3。
HDF5格式是一种开放的所有用途的数字数据容器文件格式。HDF5文件中的数据对象是以树状结构存储的,与文件系统中的文件和文件夹相类似。让我们打开文件,探索这个结构,在这个案例中,这个结构非常简单。
我们使用pytables库来访问该文件。这正是Acoular所使用的库。另外,Acoular也可以用h5py工作。
你也可以使用一个带有GUI的HDF5文件查看器(比如HDFView)。
import tables
h5file = tables.open_file('three_sources.h5', mode = 'r') # read only mode
h5file.root
/ (RootGroup) ''
children := ['time_data' (EArray)]
在它的根部只有一个对象(一个 "子"),它是一个EArray(可扩展数组)。让我们检查一下它的属性。
h5file.root.time_data
/time_data (EArray(51200, 64)) ''
atom := Float32Atom(shape=(), dflt=0.0)
maindim := 0
flavor := 'numpy'
byteorder := 'little'
chunkshape := (256, 64)
我们看到这个数组的大小是51200(样本)乘64(通道)。这些值被存储为32位浮点数。虽然比通常的64位要少,但在这种情况下,32位的精度是绰绰有余的,而且可以节省文件空间。数据本身可以像numpy数组一样被访问。作为一个例子,我们读取通道47的前10个样本。
h5file.root.time_data[:10,47]
array([ 1.5875906 , -0.7917087 , 3.1555338 , 1.0036362 , -3.1655273 ,
-6.466202 , -0.19289835, 1.7383114 , 6.901536 , 2.723017 ],
dtype=float32)
除了数据本身,该对象还存储了一些元数据("属性")。
h5file.root.time_data.attrs
/time_data._v_attrs (AttributeSet), 5 attributes:
[CLASS := 'EARRAY',
EXTDIM := 0,
TITLE := '',
VERSION := '1.1',
sample_freq := 51200.0]
这里有一个自定义属性,即sample_freq 。它指定了采样频率。在我们的例子中是51200.0 Hz。
如果我们现在有其他格式的数据,想用Acoular来使用,有两个选择。
- 我们读取该数据并将其转换为遵循所解释的规范的HDF5文件。这在这篇博文中有演示。
- 我们扩展Acoular以直接读取其他文件格式。这意味着要对
TimeSamples类进行子类化,需要对Acoular的代码和工作机制有一定的了解。
现在将用.csv格式的数据来演示第一个方案。尽管这种人类可读的文本格式效率特别低,但它被广泛使用。该文件包含相同数量的浮点数字,每行以逗号分隔。一些.csv文件也有一个或多个标题行,解释文件中包含的数据。在我们的例子中,没有头行。有多种方法可以将这种文件读入Python。我们将使用Numpy来做这件事。请注意,导入这个(相对较小的)80MByte文件需要一些时间。
import numpy as np
datacsv = np.genfromtxt('three_sources.csv', delimiter=',', dtype='float32')
datacsv
array([[-0.43654928, -4.696499 , -2.9038546 , ..., -0.39481497,
-3.7462494 , -3.2238567 ],
[ 2.2970407 , -1.9746966 , -4.089035 , ..., -3.8922982 ,
-4.8707275 , -3.613382 ],
[-2.261127 , 1.6419717 , 3.4066103 , ..., -0.732125 ,
0.22087638, -1.6310387 ],
...,
[-1.530854 , -1.2453959 , 1.566295 , ..., -3.9039657 ,
-0.00989423, -6.0220094 ],
[ 0.47992265, 3.8888328 , -0.15509878, ..., -1.2525555 ,
-2.5308452 , -3.22349 ],
[-1.0162828 , 1.230733 , -2.4700263 , ..., -5.659823 ,
-5.2780933 , -0.36301124]], dtype=float32)
现在数据已经存储在datacsv 。下一步是创建一个新的HFD5文件,将数据存储到该文件中,并添加采样频率的属性。
h5filecsv = tables.open_file('three_sources_from_csv.h5', mode='w',
title='three_sources')
earraycsv = h5filecsv.create_earray('/', 'time_data', obj=datacsv)
display(earraycsv)
h5filecsv.root.time_data.set_attr('sample_freq',51200.0)
h5filecsv.close()
/time_data (EArray(51200, 64)) ''
atom := Float32Atom(shape=(), dflt=0.0)
maindim := 0
flavor := 'numpy'
byteorder := 'little'
chunkshape := (256, 64)
就像之前的原始HDF5文件一样,我们现在有了新的HDF5文件中的数据,可以作为Acoular的数据源。这种方法有一个可能的缺陷:数据在被存储到HDF5文件之前已经完全读入计算机内存。如果数据真的很庞大,比如说几百个通道和几分钟的记录,它可能无法装入内存。在这种情况下,就需要一种更复杂的方法,即连续读取和存储大块的数据。因为我们使用的是EArray,这是有可能的,但我们必须修改代码。
如前所述,还有其他选择来读取.csv数据。这里值得一提的是Pandas,它可以读取很多不同的数据格式。
由于某些原因,用Matlab使用的格式来存储数据是相当流行的。然而,重要的是要知道,尽管有相同的扩展名(.mat),却有不同的格式。如果我们有Matlab v7之前使用的任何一种格式,那么我们可以用Scipy来导入这些格式。
from scipy.io import loadmat
ans = loadmat('three_sourcesv7.mat')['ans']
datamat7 = np.array(ans, dtype='float32')
h5filemat7 = tables.open_file('three_sources_from_mat7.h5', mode='w',
title='three_sources')
earraymat7 = h5filemat7.create_earray('/', 'time_data', obj=datamat7)
display(earraymat7)
h5filemat7.root.time_data.set_attr('sample_freq',51200.0)
h5filemat7.close()
/time_data (EArray(51200, 64)) ''
atom := Float32Atom(shape=(), dflt=0.0)
maindim := 0
flavor := 'numpy'
byteorder := 'little'
chunkshape := (256, 64)
从7.3版本开始,.mat文件的格式基本上就是一个HDF5文件本身!它只是使用了另一个文件名后缀。它只是使用了另一个文件名扩展名。当然,其内部结构与Acoular使用的不同。然而,我们可以用pytables打开它并读取其中的数据。
matfile73 = tables.open_file('three_sourcesv73.mat', mode = 'r')
# be aware of Matlab transposing the array here
datamat73 = np.array(matfile73.root.ans[:,:], dtype='float32').T
h5filemat73 = tables.open_file('three_sources_from_mat73.h5', mode='w',
title='three_sources')
earraymat73 = h5filemat73.create_earray('/', 'time_data', obj=datamat73)
display(earraymat73)
h5filemat73.root.time_data.set_attr('sample_freq',51200.0)
h5filemat73.close()
/time_data (EArray(51200, 64)) ''
atom := Float32Atom(shape=(), dflt=0.0)
maindim := 0
flavor := 'numpy'
byteorder := 'little'
chunkshape := (256, 64)
这篇博文演示了如何从外国格式导入数据到Acoular。它也可以用来指导如何转换这里没有明确提到的任何其他格式。