根据另一个数组的值选择numpy数组中的元素
假设我有以下两个数组:
a = array([(1, 'L', 74.423088306605), (5, 'H', 128.05441039929008),
(2, 'L', 68.0581377353869), (0, 'H', 88.15726964130869),
(4, 'L', 97.4501582588212), (3, 'H', 92.98550136344437),
(7, 'L', 87.75945631669309), (6, 'L', 90.43196739694255),
(8, 'H', 111.13662092749307), (15, 'H', 91.44444608631304),
(10, 'L', 85.43615908319185), (11, 'L', 78.11685661303494),
(13, 'H', 108.2841293816308), (17, 'L', 74.43917911042259),
(14, 'H', 64.41057325770373), (9, 'L', 27.407214746467943),
(16, 'H', 81.50506434964355), (12, 'H', 97.79700070323196),
(19, 'L', 51.139258140713025), (18, 'H', 118.34835768605957)],
dtype=[('id', '<i4'), ('name', 'S1'), ('value', '<f8')])
b = array([ 0, 3, 5, 8, 12, 13, 14, 15, 16, 18], dtype=int32)
我想从 a
中选择那些在 b
中给出的 id
的元素。也就是说,b
不是一个索引数组,而是包含观察值的 id
。我该如何在 numpy 中做到这一点呢?
谢谢你的帮助。
3 个回答
0
sorted = numpy.sort(a)
sorted[b]
array([(0, 'H', 88.15726964130869), (3, 'H', 92.98550136344437),
(5, 'H', 128.05441039929008), (8, 'H', 111.13662092749307),
(12, 'H', 97.79700070323196), (13, 'H', 108.2841293816308),
(14, 'H', 64.41057325770373), (15, 'H', 91.44444608631304),
(16, 'H', 81.50506434964355), (18, 'H', 118.34835768605957)],
dtype=[('id', '<i4'), ('name', '|S1'), ('value', '<f8')])
只要数组中的行数和id的数量一样就可以。
5
下面这个方法在处理你的示例数组时,速度比Francesco的方法快好几倍:
In [7]: a[np.argmax(a['id'][None, :] == b[:, None], axis=1)]
Out[7]:
array([(0, 'H', 88.15726964130869), (3, 'H', 92.98550136344437),
(5, 'H', 128.05441039929008), (8, 'H', 111.13662092749307),
(12, 'H', 97.79700070323196), (13, 'H', 108.2841293816308),
(14, 'H', 64.41057325770373), (15, 'H', 91.44444608631304),
(16, 'H', 81.50506434964355), (18, 'H', 118.34835768605957)],
dtype=[('id', '<i4'), ('name', '|S1'), ('value', '<f8')])
In [8]: %timeit a[np.argmax(a['id'][None, :] == b[:, None], axis=1)]
100000 loops, best of 3: 11.6 us per loop
In [9]: %timeit indices = [i for i,id in enumerate(a['id']) if id in b]; a[indices]
10000 loops, best of 3: 66.9 us per loop
要理解它是怎么工作的,可以看看这个:
In [10]: a['id'][None, :] == b[:, None]
Out[10]:
array([[False, False, False, True, False, False, False, False, False,
False, False, False, False, False, False, False, False, False,
False, False],
... # several rows removed
[False, False, False, False, False, False, False, False, False,
False, False, False, False, False, False, False, False, False,
False, True]], dtype=bool)
这是一个数组,行数和b中的元素数量一样,列数和a中的元素数量一样。np.argmax会找到每一行中第一个True的位置,这个位置就是b中对应元素在a['id']中第一次出现的索引。
如上所示,对于小数组,这种方法在性能上优于Python的其他方法。但是如果a或b变得太大,中间生成的布尔数组的大小可能会影响性能。此外,np.argmax需要搜索整行,它不会提前退出循环,这在a很长的情况下不是个好事。我在回答这个问题时做了一些时间测试,使用了类似的方法,对于中等大小的数组,这仍然是个不错的选择。
Francesco的方法确实不那么复杂,更容易理解,对于你示例大小的数组,性能差异几乎可以忽略不计,我必须承认。但是用这个方法做出来的感觉就不太一样了,像是这样...
6
你应该能通过这个得到你想要的东西。
indeces = [i for i,id in enumerate(a['id']) if id in b]
suba = a[indeces]
print(suba)
>>>array([(5, 'H', 128.05441039929008), (0, 'H', 88.15726964130869),
(3, 'H', 92.98550136344437), (8, 'H', 111.13662092749307),
(15, 'H', 91.44444608631304), (13, 'H', 108.2841293816308),
(14, 'H', 64.41057325770373), (16, 'H', 81.50506434964355),
(12, 'H', 97.79700070323196), (18, 'H', 118.34835768605957)],
dtype=[('id', '<i4'), ('name', '|S1'), ('value', '<f8')])