如何水平堆叠 numpy 记录数组?
[这篇文章的早期版本标题不准确,原标题是“如何向numpy记录数组添加一列?” 早期标题中提到的问题已经部分得到了解答,但这个答案并不完全符合早期版本的内容。我已经重新修改了标题,并大幅编辑了帖子,以使区别更加清晰。我还解释了为什么之前提到的答案没有满足我的需求。]
假设我有两个 numpy
数组 x
和 y
,每个数组都有 r 个“记录”(也叫“结构化”)数组。设 x
的形状是 (r, cx),而 y
的形状是 (r, cy)。我们还假设 x.dtype.names
和 y.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')])
我想要“水平”连接 x
和 y
,生成一个新的记录数组 z
,其形状为 (r, cx + cy)。这个操作不应该修改 x
或 y
。
一般来说,z = np.hstack((x, y))
是不行的,因为 x
和 y
的数据类型(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),从上面定义的 x
和 y
生成一个新的记录数组 z
,等同于 x
和 y
的水平连接,并且不修改 x
或 y
?
我认为这只需要一两行代码。当然,我希望代码不需要通过遍历 x
和 y
来逐条构建 z
。另外,代码可以假设 x
和 y
有相同数量的记录,并且 x.dtype.names
和 y.dtype.names
之间没有重叠。除此之外,我希望代码对 x
和 y
一无所知。理想情况下,它也应该对要连接的数组数量没有限制。换句话说,省略错误检查,我希望的代码可以是一个函数 hstack_rec
的主体,这样新数组 z
就是 hstack_rec((x, y))
的结果。
1...虽然我得承认,在我与 numpy.lib.recfunctions.append_fields
的完美失败记录之后,我对这个函数如何使用产生了一些好奇,无论它与这个帖子的问题是否相关。
2 个回答
这是一个很晚的回答,但也许对其他人会有帮助。我在问了同样的问题,并且条件大致相同时,使用了这个解决方案。
这个方法并不会生成一个新的numpy数组,而是通过使用 zip
和 itertools.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)
我从来不使用递归数组,所以可能会有人想出更好的办法,但也许 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')])