如何将你的数据导入Acoular

483 阅读5分钟

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来使用,有两个选择。

  1. 我们读取该数据并将其转换为遵循所解释的规范的HDF5文件。这在这篇博文中有演示。
  2. 我们扩展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。它也可以用来指导如何转换这里没有明确提到的任何其他格式。