在Numpy结构化数组中合并记录

3

我有一个按第一列排序的Numpy结构化数组:

x = array([(2, 3), (2, 8), (4, 1)], dtype=[('recod', '<u8'), ('count', '<u4')])

我需要合并记录(将第二列的值相加),条件是

x[n][0] == x[n + 1][0]

在这种情况下,期望的输出应为:
x = array([(2, 11), (4, 1)], dtype=[('recod', '<u8'), ('count', '<u4')])

什么是达成这个目标的最佳方式?

请编辑此问题以反映您在评论中提供的结构化数组:array([(2, 3), (2, 8), (4, 1)], dtype=[('recod', '<u8'), ('count', '<u4')])。您现有的问题看起来更像是一个二维数组。 - hpaulj
4个回答

3
你可以使用 np.unique 函数获取第一列中每个元素对应的 ID 数组,然后使用 np.bincount 函数针对这些 ID 对第二列中的元素进行累加。
In [140]: A
Out[140]: 
array([[25,  1],
       [37,  3],
       [37,  2],
       [47,  1],
       [59,  2]])

In [141]: unqA,idx = np.unique(A[:,0],return_inverse=True)

In [142]: np.column_stack((unqA,np.bincount(idx,A[:,1])))
Out[142]: 
array([[ 25.,   1.],
       [ 37.,   5.],
       [ 47.,   1.],
       [ 59.,   2.]])

使用np.diffnp.cumsum的组合可以避免使用np.unique。这种方法可能更好,因为np.unique在内部进行排序,而输入数据已经排序,所以不需要排序。实现代码如下:

In [201]: A
Out[201]: 
array([[25,  1],
       [37,  3],
       [37,  2],
       [47,  1],
       [59,  2]])

In [202]: unq1 = np.append(True,np.diff(A[:,0])!=0)

In [203]: np.column_stack((A[:,0][unq1],np.bincount(unq1.cumsum()-1,A[:,1])))
Out[203]: 
array([[ 25.,   1.],
       [ 37.,   5.],
       [ 47.,   1.],
       [ 59.,   2.]])

我遇到了以下错误: Traceback (most recent call last): File "/home/krlk89/abc.py", line 8, in <module> unq1 = np.append(True,np.diff(x[:,0])!=0) IndexError: too many indices - krlk89
非常感谢您的帮助,虽然我得到了正确的数字,但好像我失去了数组结构?这个结构对我很重要,我需要将数组以8字节无符号整数和4字节无符号整数对的形式写入二进制文件。 - krlk89
你需要构建一个新的结构化数组,使用 unqA 和计数来填充这两个字段。它可以从原始数组中复制 dtype - hpaulj
@hpaulj,您能否提供更详细的指南来说明如何做到这一点。谢谢! - krlk89
1
我基于这个答案添加了一个针对结构化数组进行了适配的答案。 - hpaulj
显示剩余3条评论

2

pandas可以轻松完成这种“分组”操作,与此相关的技术是it技术。

In [285]: import pandas as pd

In [286]: x = [(25, 1), (37, 3), (37, 2), (47, 1), (59, 2)]

In [287]: df = pd.DataFrame(x)

In [288]: df
Out[288]: 
    0  1
0  25  1
1  37  3
2  37  2
3  47  1
4  59  2

In [289]: df.groupby(0).sum()
Out[289]: 
    1
0    
25  1
37  5
47  1
59  2

如果您只需要执行此操作,则可能不需要依赖pandas,但一旦开始,您可能会发现库中有其他有用的部分。


谢谢您的帮助!我尝试了这个,但是收到了一个错误信息:pastebin.com/mA6fDT3u - krlk89
我看到你改变了数组的格式。在这种情况下,请使用df.groupby('recod').sum() - Warren Weckesser
谢谢!现在它能用了,但我怎么才能恢复我的初始数组结构呢? - krlk89

2

Dicakar的答案以结构化数组形式呈现:

In [500]: x=np.array([(25, 1), (37, 3), (37, 2), (47, 1), (59, 2)], dtype=[('recod', '<u8'), ('count', '<u4')])

查找唯一值并计算重复值:

In [501]: unqA, idx=np.unique(x['recod'], return_inverse=True)    
In [502]: cnt = np.bincount(idx, x['count'])

创建一个新的结构化数组,并填充字段:
In [503]: x1 = np.empty(unqA.shape, dtype=x.dtype)
In [504]: x1['recod'] = unqA
In [505]: x1['count'] = cnt

In [506]: x1
Out[506]: 
array([(25, 1), (37, 5), (47, 1), (59, 2)], 
      dtype=[('recod', '<u8'), ('count', '<u4')])

有一个 recarray 函数,它可以从数组列表构建一个数组:

In [507]: np.rec.fromarrays([unqA,cnt],dtype=x.dtype)
Out[507]: 
rec.array([(25, 1), (37, 5), (47, 1), (59, 2)], 
      dtype=[('recod', '<u8'), ('count', '<u4')])

在内部,它做的事情是相同的-构建正确大小和dtype的空数组,然后循环遍历dtype字段。recarray只是一个专门的数组子类包装器中的结构化数组。

有两种方法可以填充结构化数组(特别是具有多样化dtype)-使用元组列表(如您使用的x),以及逐个字段填充。


非常感谢您的帮助! - krlk89
感谢您在这方面帮助OP!我对结构化数组的东西并不是很熟悉。 - Divakar

1
你可以使用np.reduceat。你只需要填充x [:,0]变化的位置,这相当于np.diff(x [:,0])的非零索引加上初始索引0后移一位:
>>> i = np.r_[0, 1 + np.nonzero(np.diff(x[:,0]))[0]]
>>> a, b = x[i, 0], np.add.reduceat(x[:, 1], i)
>>> np.vstack((a, b)).T
array([[25,  1],
       [37,  5],
       [47,  1],
       [59,  2]])

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接