我想左外连接两个重新排列。第一个是具有唯一键的实体列表。第二个是值列表,每个实体可以有 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
Neither
r1
norr2
should have any duplicates alongkey
: the presence of duplicates will make the output quite unreliable. Note that duplicates are not looked for by the algorithm.
所以,看来这个要求确实限制了这个函数的用处。看来我描述的左外连接类型是一个非常常见的操作,有人知道如何使用 numpy 实现它吗?
最佳答案
如果x
的point_index
值按数字顺序排列,您可以通过简单的索引将它们与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)
使用 y
中的匹配字段填充 z
:
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')])
append_fields
大致执行我之前编写的操作 - 使用新的 dtype 创建一个 output
,然后填充基础数据和新数据中的值。它使用 recursive_fill_fields 来复制数据,对于简单的数据类型,它会执行相同的按名称复制。
nrec.recursive_fill_fields(y,z)
关于python numpy left join rearray 具有重复的键值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31774578/