【问题标题】:Combine two NumPy arrays into one structured array for appending to a PyTables table将两个 NumPy 数组组合成一个结构化数组,以附加到 PyTables 表
【发布时间】:2020-05-30 14:46:05
【问题描述】:

我有两个非结构化 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 缓冲区。

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

【问题讨论】:

  • 我不确定这个问题是用 NumPy 问题还是 PyTables 问题来表述最好。我选择了 NumPy 问题,因为它似乎更普遍适用并且需要较少的专业知识来回答。一个人可能能够在不了解 PyTables 的情况下回答 NumPy 问题,但反之则不行。如果人们认为我打错了电话,我愿意编辑问题以改变重点。
  • 创建一个具有正确形状和数据类型的np.zeros 结构化数组,并按名称单独分配字段。
  • 啊!当你说@hpaulj时,这很明显!我有点太快地阅读了有关结构化数组的文档,并意识到根本不可能按字段对它们进行切片,但这很有效。我想我在某个地方读到了一些过时的东西。当然,我们可以使用np.empty 代替np.zeros。如果您想用最少的代码示例将此作为答案发布,我很乐意接受。
  • 最近的版本对多字段访问做了改动;否则创建结构化数组保持不变。

标签: python numpy numpy-ndarray pytables


【解决方案1】:

定义数据类型,并创建一个空/零数组:

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 是结构化数组函数的另一个集合。这些也大多遵循按字段分配的方法。

【讨论】:

  • 很简单,只要你指出我可以像这样按字段索引结构化数组就很明显了。对文档的误读使我走上了一条比必要的复杂得多的道路!我会为这个而自责的。完美的答案,谢谢!
【解决方案2】:

为了获得合适大小的测试打印输出,我的解决方案假设:

  • 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 切片转换为单行。数据 从每一行(从这些列)转换回“4 * 2” 由这个函数塑造。
  • 以上两个组件都是用hstack组装的。

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

在目标版本的代码中:

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

【讨论】:

    【解决方案3】:

    这个答案建立在@hpualj 的答案之上。他的第一个方法将obj 参数创建为结构化数组,而他的第二个方法创建了一个记录数组。 (当您追加时,此数组将是 rows 参数。)当我已经将数据保存在结构化(或记录)数组中时,我喜欢这两种方法来创建或追加到表中。但是,如果您的数据位于单独的数组中(如“避免 X-Y 问题”中所述),则不必这样做。如 table.append() 的 PyTables 文档中所述:

    rows 参数可以是任何可以转换为 符合表结构的结构化数组......这包括 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])
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-12-21
      • 2022-10-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多