python numpy left join重新组合重复键值

时间:2015-08-02 17:28:47

标签: python python-2.7 numpy

我想留下外连接两个重组。第一个是具有唯一键的实体列表。第二个是值列表,每个实体可以有0个或更多值。我的环境要求我使用Python 2.7并且我无法使用Pandas。

here之前已经提出了这个问题,但没有一个好的答案。

    import numpy as np
    import numpy.lib.recfunctions
    from pprint import pprint

    dtypes = [('point_index',int),('name','S50')] 
    recs = [(0,'Bob'),
            (1,'Bob'),
            (2,'Sue'),
            (3,'Sue'),
            (4,'Jim')]
    x = np.rec.fromrecords(recs,dtype=dtypes)

    dtypes = [('point_index',int),('type','S500'),('value',float)] 
    recs = [(0,'a',0.1),
            (0,'b',0.2),
            (1,'a',0.3),
            (2,'b',0.4),
            (2,'b',0.5),
            (4,'a',0.6),
            (4,'a',0.7),
            (4,'a',0.8)]
    y = np.rec.fromrecords(recs,dtype=dtypes)

    j = np.lib.recfunctions.join_by('point_index',x,y,jointype='leftouter',usemask=False,asrecarray=True)

    pprint(j.tolist())

我想要

# [(0,'Bob','a',0.1),
#  (0,'Bob','b',0.2),
#  (1,'Bob','a',0.3),
#  (2,'Sue','b',0.4),
#  (2,'Sue','b',0.5),
#  (4,'Jim','a',0.6),
#  (4,'Jim','a',0.7),
#  (4,'Jim','a',0.8)]

但是我得到了

[(0, 'Bob', 'a', 0.1),
 (0, 'Bob', 'b', 0.2),
 (1, 'Sue', 'a', 0.3),
 (2, 'Jim', 'b', 0.4),
 (2, 'N/A', 'b', 0.5),
 (3, 'Sue', 'N/A', 1e+20),
 (4, 'N/A', 'a', 0.6),
 (4, 'N/A', 'a', 0.7),
(4, 'N/A', 'a', 0.8)]

我知道原因,这来自docs

  

r1r2都不应该在key上有任何重复:   重复将使输出非常不可靠。请注意重复   没有被算法查找。

所以,似乎这个要求确实限制了这个功能的用处。看起来我描述的左外连接的类型是一个非常常见的操作,有人知道如何使用numpy实现它吗?

1 个答案:

答案 0 :(得分:3)

如果point_index的{​​{1}}值是按数字顺序排列的,则可以通过简单索引将它们与x匹配。

一种方法是使用添加的y字段构建一个新数组z。在这里,我使用结构化数组(names也可以,但我不需要它的额外功能):

rec

使用In [419]: dtypes1 = [('point_index',int),('name','S50')] In [420]: dtypes Out[420]: [('point_index', int), ('type', 'S500'), ('value', float)] In [421]: dtypes2=dtypes1 + dtypes[1:] In [422]: z=np.zeros(y.shape[0],dtype=dtypes2) 的匹配字段填充z

y

由于字段数通常远小于行数,因此这种副本并不昂贵。

通过简单索引选择名称:

In [423]: for n in y.dtype.names:
    z[n] = y[n]

有更多通用的匹配方式In [424]: z['name']=x['name'][y['point_index']] In [425]: z Out[425]: array([(0, b'Bob', b'a', 0.1), (0, b'Bob', b'b', 0.2), (1, b'Bob', b'a', 0.3), (2, b'Sue', b'b', 0.4), (2, b'Sue', b'b', 0.5), (4, b'Jim', b'a', 0.6), (4, b'Jim', b'a', 0.7), (4, b'Jim', b'a', 0.8)], dtype=[('point_index', '<i4'), ('name', 'S50'), ('type', 'S500'), ('value', '<f8')]) x['point_index']。只需将它们视为需要匹配的两个数字数组(可能具有唯一和排序)。甚至使用列表理解并找到。

或在链接的答案中使用y['point_index']方法:

append_fields

In [441]: import numpy.lib.recfunctions as nrec In [442]: names=x['name'][y['point_index']] In [443]: nrec.append_fields(y, 'name', names, asrecarray=False, usemask=False) Out[443]: array([(0, b'a', 0.1, b'Bob'), (0, b'b', 0.2, b'Bob'), (1, b'a', 0.3, b'Bob'), (2, b'b', 0.4, b'Sue'), (2, b'b', 0.5, b'Sue'), (4, b'a', 0.6, b'Jim'), (4, b'a', 0.7, b'Jim'), (4, b'a', 0.8, b'Jim')], dtype=[('point_index', '<i4'), ('type', 'S500'), ('value', '<f8'), ('name', 'S50')]) 大致完成了我之前写的内容 - 使用新的dtype创建append_fields,然后填写基础和新数据中的值。它使用output来复制数据,对于简单的dtype,它会按名称进行相同的复制。

recursive_fill_fields