如何水平堆叠 numpy 记录数组?

5 投票
2 回答
2281 浏览
提问于 2025-04-17 16:17

[这篇文章的早期版本标题不准确,原标题是“如何向numpy记录数组添加一列?” 早期标题中提到的问题已经部分得到了解答,但这个答案并不完全符合早期版本的内容。我已经重新修改了标题,并大幅编辑了帖子,以使区别更加清晰。我还解释了为什么之前提到的答案没有满足我的需求。]


假设我有两个 numpy 数组 xy,每个数组都有 r 个“记录”(也叫“结构化”)数组。设 x 的形状是 (r, cx),而 y 的形状是 (r, cy)。我们还假设 x.dtype.namesy.dtype.names 之间没有重叠。

例如,当 r = 2,cx = 2,和 cy = 1 时:

import numpy as np
x = np.array(zip((1, 2), (3., 4.)), dtype=[('i', 'i4'), ('f', 'f4')])
y = np.array(zip(('a', 'b')), dtype=[('s', 'a10')])

我想要“水平”连接 xy,生成一个新的记录数组 z,其形状为 (r, cx + cy)。这个操作不应该修改 xy

一般来说,z = np.hstack((x, y)) 是不行的,因为 xy 的数据类型(dtype)不一定匹配。例如,继续上面的例子:

z = np.hstack((x, y))
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-7-def477e6c8bf> in <module>()
----> 1 z = np.hstack((x, y))
TypeError: invalid type promotion


现在,有一个函数 numpy.lib.recfunctions.append_fields,看起来可能接近我想要的功能,但我一直无法从中得到任何结果:我尝试的所有方法要么出错,要么产生的结果与我想要的不同。

能不能请大家明确地给我展示一下代码(使用 n.l.r.append_fields 或其他方法1),从上面定义的 xy 生成一个新的记录数组 z,等同于 xy 的水平连接,并且不修改 xy

我认为这只需要一两行代码。当然,我希望代码不需要通过遍历 xy 来逐条构建 z。另外,代码可以假设 xy 有相同数量的记录,并且 x.dtype.namesy.dtype.names 之间没有重叠。除此之外,我希望代码对 xy 一无所知。理想情况下,它也应该对要连接的数组数量没有限制。换句话说,省略错误检查,我希望的代码可以是一个函数 hstack_rec 的主体,这样新数组 z 就是 hstack_rec((x, y)) 的结果。


1...虽然我得承认,在我与 numpy.lib.recfunctions.append_fields 的完美失败记录之后,我对这个函数如何使用产生了一些好奇,无论它与这个帖子的问题是否相关。

2 个回答

0

这是一个很晚的回答,但也许对其他人会有帮助。我在问了同样的问题,并且条件大致相同时,使用了这个解决方案。

这个方法并不会生成一个新的numpy数组,而是通过使用 zipitertools.chain 来提高速度。在我的情况下,我需要按顺序访问每一行的每一个值。下面是一个模拟这种使用情况的基准测试:

import numpy
from numpy.lib.recfunctions import merge_arrays
from itertools import chain

a = numpy.empty(3, [("col1", int), ("col2", float)])
b = numpy.empty(3, [("col3", int), ("col4", "U1")])

结果:

%timeit [i for i in (row for row in merge_arrays([a,b], flatten=True))]
52.9 µs ± 2 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

%timeit [i for i in (row for row in (chain(i,k) for i,k in zip(a,b)))]
3.47 µs ± 52 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
5

我从来不使用递归数组,所以可能会有人想出更好的办法,但也许 merge_arrays 可以试试?

>>> import numpy.lib.recfunctions as nlr
>>> x = np.array(zip((1, 2), (3., 4.)), dtype=[('i', 'i4'), ('f', 'f4')])
>>> y = np.array(zip(('a', 'b')), dtype=[('s', 'a10')])
>>> x
array([(1, 3.0), (2, 4.0)], 
      dtype=[('i', '<i4'), ('f', '<f4')])
>>> y
array([('a',), ('b',)], 
      dtype=[('s', '|S10')])
>>> z = nlr.merge_arrays([x, y], flatten=True)
>>> z
array([(1, 3.0, 'a'), (2, 4.0, 'b')], 
      dtype=[('i', '<i4'), ('f', '<f4'), ('s', '|S10')])

撰写回答