将两个NumPy数组合并为一个结构化数组,以附加到PyTables表

时间:2020-05-30 14:46:05

标签: python numpy numpy-ndarray pytables

我有两个非结构化的NumPy数组ab,分别具有形状(N,)(N, 256, 2)和dtype np.float。我希望将它们组合成具有形状(N,)和dtype [('field1', np.float), ('field2', np.float, (256, 2))]的单个结构化数组。

令人惊讶地缺少有关此问题的文档。我找到了np.lib.recfunctions.merge_arrays之类的方法,但无法找到执行此操作所需的功能的精确组合。


为了避免使用the XY problem,我将陈述更广泛的目标。

我有一个布局为{"field1": tables.FloatCol(), "field2": tables.FloatCol(shape = (256, 2))}的PyTables表。这两个NumPy数组表示要添加到这些字段中的每个字段的N个新行。 N很大,所以我希望通过一个有效的table.append(rows)调用来完成此操作,而不是通过table.row['field'] = ...循环的缓慢过程。

The table.append documentation

rows参数可以是任何可以转换为与表结构兼容的结构化数组的对象(否则会引发ValueError)。这包括NumPy结构化的数组,元组或数组记录的列表以及字符串或Python缓冲区。

将数组转换为适当的结构化数组似乎是我在这里应该做的。我正在寻找速度,并且我预计其他选择会更慢。

3 个答案:

答案 0 :(得分:1)

定义dtype,并创建一个空/零数组:

In [163]: dt = np.dtype([('field1', np.float), ('field2', np.float, (4, 2))])            
In [164]: arr = np.zeros(3, dt)     # float display is prettier                                                          
In [165]: arr                                                                            
Out[165]: 
array([(0., [[0., 0.], [0., 0.], [0., 0.], [0., 0.]]),
       (0., [[0., 0.], [0., 0.], [0., 0.], [0., 0.]]),
       (0., [[0., 0.], [0., 0.], [0., 0.], [0., 0.]])],
      dtype=[('field1', '<f8'), ('field2', '<f8', (4, 2))])

逐字段分配值:

In [166]: arr['field1'] = np.arange(3)                                                   
In [167]: arr['field2'].shape                                                            
Out[167]: (3, 4, 2)
In [168]: arr['field2'] = np.arange(24).reshape(3,4,2)                                   
In [169]: arr                                                                            
Out[169]: 
array([(0., [[ 0.,  1.], [ 2.,  3.], [ 4.,  5.], [ 6.,  7.]]),
       (1., [[ 8.,  9.], [10., 11.], [12., 13.], [14., 15.]]),
       (2., [[16., 17.], [18., 19.], [20., 21.], [22., 23.]])],
      dtype=[('field1', '<f8'), ('field2', '<f8', (4, 2))])

np.rec确实具有类似的功能:

In [174]: np.rec.fromarrays([np.arange(3.), np.arange(24).reshape(3,4,2)], dtype=dt)     
Out[174]: 
rec.array([(0., [[ 0.,  1.], [ 2.,  3.], [ 4.,  5.], [ 6.,  7.]]),
           (1., [[ 8.,  9.], [10., 11.], [12., 13.], [14., 15.]]),
           (2., [[16., 17.], [18., 19.], [20., 21.], [22., 23.]])],
          dtype=[('field1', '<f8'), ('field2', '<f8', (4, 2))])

相同,除了字段也可以作为属性访问。在幕后,它执行相同的按领域分配。

numpy.lib.recfunctions是结构化数组函数的另一个集合。这些大多都遵循按字段分配方法。

答案 1 :(得分:0)

为了获得合适尺寸的测试打印输出,我的解决方案假定:

  • N = 5
  • 第二个维度-仅 4 (而不是您的 256 )。

要生成结果,请按照下列步骤操作:

  1. import numpy.lib.recfunctions as rfn开始(很快将需要)。

  2. 创建源数组:

    a = np.array([10, 20, 30, 40, 50])
    b = np.arange(1, 41).reshape(5, 4, 2)
    
  3. 创建结果:

    result = rfn.unstructured_to_structured(
        np.hstack((a[:,np.newaxis], b.reshape(-1,8))),
        np.dtype([('field1', 'f4'), ('field2', 'f4', (4,2))]))
    

生成的数组包含:

array([(10., [[ 1.,  2.], [ 3.,  4.], [ 5.,  6.], [ 7.,  8.]]),
       (20., [[ 9., 10.], [11., 12.], [13., 14.], [15., 16.]]),
       (30., [[17., 18.], [19., 20.], [21., 22.], [23., 24.]]),
       (40., [[25., 26.], [27., 28.], [29., 30.], [31., 32.]]),
       (50., [[33., 34.], [35., 36.], [37., 38.], [39., 40.]])],
      dtype=[('field1', '<f4'), ('field2', '<f4', (4, 2))])

请注意,创建了 unstructured_to_structured 的源数组 通过以下方式:

  • 0 -来自 a (转换为列),
  • 剩余的列-从 b 中重塑为所有元素 各个4 * 2切片中的s转换为单行。数据 从每一行(从这些列)转换为“ 4 * 2” 通过此功能进行塑形。
  • 以上两个组件都是用 hstack 组装的。

在上述实验中,我假设 f4 的类型,也许您应该更改 到 f8 (您的决定)。

在代码的目标版本中:

    field2 的第一维中的
  • 4 4 更改为 256
  • b.reshape 中的 8 更改为 512 (= 2 * 256)。

答案 2 :(得分:0)

此答案以@hpualj的答案为基础。他的第一个方法将obj参数创建为结构化数组,第二个方法创建一个记录数组。 (追加时,该数组将是rows参数。)当我已经将数据存储在结构化(或记录)数组中时,我喜欢这两种方法来创建或追加到表。但是,如果数据位于单独的数组中,则不必执行此操作(如“ 避免XY问题'中所述”)。如table.append()的PyTables文档中所述:< / p>

rows参数可以是任何可以转换为a的对象 与表结构兼容的结构化数组。...包括 NumPy结构化数组, 元组列表或数组记录...

换句话说,您可以在列表中追加引用数组的列表,只要它们与示例中用description=dt创建的表结构匹配即可。 (我认为您在创建时仅限于结构化数组。)这可能会简化您的代码。

我写了一个基于@hpaulj代码的示例。它使用不同的方法创建2个相同的HDF5文件。

  • 对于第一个文件(_1.h5),我使用结构化数组方法创建表。然后,我使用table.append([list of arrays])
  • 将3行数据添加到表中
  • 对于第二个文件(_2.h5),我创建了引用 使用description=dt的结构化数组dtype,但不要使用obj=arr添加数据。然后,我使用table.append([list of arrays])将数据的前3行添加到表中,并重复添加另外3行。

以下示例:

import numpy as np
import tables as tb

dt = np.dtype([('field1', np.float), ('field2', np.float, (4, 2))])            
arr = np.zeros(3, dt)     # float display is prettier                                                          
arr['field1'] = np.arange(3)                                                                                                           
arr['field2'] = np.arange(24).reshape(3,4,2)                                   

with tb.File('SO_62104084_1.h5','w') as h5f1:
    test_tb = h5f1.create_table('/','test',obj=arr)
    arr1 = np.arange(13.,16.,1.)                                                                                                           
    arr2 = np.arange(124.,148.,1.).reshape(3,4,2)          
# add rows of data referencing list of arrays: 
    test_tb.append([arr1,arr2])

with tb.File('SO_62104084_2.h5','w') as h5f2:
    test_tb=h5f2.create_table('/','test', description=dt)
    # add data rows 0-2:  
    arr1 = np.arange(3)                                                                                                           
    arr2 = np.arange(24).reshape(3,4,2)                                   
    test_tb.append([arr1,arr2])
# add data rows 3-5:   
    arr1 = np.arange(13.,16.,1.)                                                                                                           
    arr2 = np.arange(124.,148.,1.).reshape(3,4,2)          
    test_tb.append([arr1,arr2])