比较numpy结构化数组

时间:2015-12-22 17:21:19

标签: python python-3.x numpy structured-array

快速问题

我希望能够比较两个保证具有相同dtype的numpy结构化数组中的特定dtype字段。我希望这样做的方式允许我们比较的字段每次根据给定的输入调用函数时都是不同的(即我不能轻易地对每个字段的比较进行硬编码)

示例

的长期问题

我试图比较具有相同dtype的两个numpy结构化数组中的特定字段。例如,说我们有

import numpy as np
from io import BytesIO

a = np.genfromtxt(BytesIO('12 23 0|23.2|17.9|0\n12 23 1|13.4|16.9|0'.encode()),dtype=[('id','U7'),('pos',[('x',float),('y',float)]),('flag','U1')],delimiter='|')

b = np.genfromtxt(BytesIO(' |23.0|17.91|0'.encode()),dtype=[('id','U7'),('pos',[('x',float),('y',float)]),('flag','U1')],delimiter='|')

给出了

In[156]: a
Out[154]: 
array([('12 23 0', (23.2, 17.9), '0'), ('12 23 1', (13.4, 16.9), '0')], 
      dtype=[('id', '<U7'), ('pos', [('x', '<f8'), ('y', '<f8')]), ('flag', '<U1')])

In[153]: b
Out[151]: 
array([('', (23.0, 17.91), '0')], 
      dtype=[('id', '<U7'), ('pos', [('x', '<f8'), ('y', '<f8')]), ('flag', '<U1')])

现在假设我要检查并查找a字段大于a['pos']['x']字段的b['pos']['x']中的任何条目,并将这些条目返回到新的numpy数组,类似于这会起作用

newArr = a[a["pos"]["x"]>b["pos"]["x"]]

现在想象一下,我们只想保留axy字段大于b中对应字段的条目。这很简单,因为我们可以再次做到

newArr = a[np.array([np.array([a['pos']['x']>b['pos']['x']),a['pos']['y']>b['pos']['y'])).all(axis=0)]

返回一个空数组,这是正确的答案。

现在,想象一下,对于这些数组我们有一个非常复杂的dtype(比如有34个字段 - 请参阅here以获取我正在使用的dtype的示例)并且我们希望能够比较它们中的任何一个但可能不是全部(类似于前面的例子,但是总体上有更多的dtype字段,我们想要比较更多的字段。此外,如果我们想要比较的字段可以在不同的运行中发生变化(所以我们不能像上面那样严格编码。这就是我试图找到解决方案的问题。

我当前(未完成)尝试解决方案

使用蒙面数组

我首先想到解决这个问题的方法是使用蒙面数组来选择我们想要比较的数据类型字段。这样的事情(假设我们可以使所有比较相同):

mask = np.ones(z.shape,dtype=[('id',bool),('pos',[('x',bool),('y',bool)]),('flag',bool)])
# unmask the x and y fields so we can compare them 
mask['pos']['x']=0
mask['pos']['y']=0

maskedA = np.ma.masked_array(a, mask=mask)
# We need to do this or the masked array gets angry (at least in python 3)
b.shape = (1,)

maskedB = np.ma.masked_array(b, mask=mask)

现在我想做一些像

这样的事情
test = (maskedA>maskedB).any(axis=1)

但这不起作用,因为你可以像这样比较结构化数组 -

TypeError: unorderable types: MaskedArray() > MaskedArray()

我也试过压缩蒙面数组

test = (maskedA.compressed()>maskedB.compressed()).any(axis=1)

会导致不同的错误

TypeError: ufunc 'logical_not' not supported for the input types, and the inputs could not be safely coerced to any supported types according to the casting rule ''safe''

现在,我意识到上述错误可能是因为我不完全理解结构化和屏蔽数组的工作原理,但这也是我提出这个问题的部分原因。有没有办法使用蒙面数组做这样的事情?

我刚想到的解决方案可能会起作用,整体上可能更好......

所以我写这篇文章时我想到的另一个选择就是在我解析用户的输入以形成数组b时进行比较。它实际上只是在解析器中为每个条件添加几行来进行比较,并将结果添加到一个numpy布尔数组中,然后我可以使用它来从a中提取正确的条目。现在我想起来这可能是要走的路。

我漫长而漫无边际问题的结论。

尽管我认为我找到了解决这个问题的方法,但我仍然会发布这个问题至少一点点,看看是否(a)任何人对如何进行逻辑比较结构化/屏蔽都有任何想法numpy数组,因为我认为知道它是一件有用的事情,(b)看看是否有人有更好的想法。请注意,您可以通过逐行复制&#34;以及示例&#34;中的长片问题,非常容易地形成MWE。部分,我没有看到任何理由通过这样做占用更多空间。

2 个答案:

答案 0 :(得分:3)

要将比较应用于列序列,必须使用Python循环。循环可以以列表推导的形式出现,例如:

In [87]: np.all([a['pos'][key] > b['pos'][key] for key in a['pos'].dtype.names], axis=0)
Out[87]: array([False, False], dtype=bool)

这会为a['pos'][key] > b['pos'][key]中的每个字段计算a['pos'],然后沿着0轴使用np.all缩小数组。

如果您希望将比较应用于某些字段列表,您当然可以 将a['pos'].dtype.names替换为该列表。

答案 1 :(得分:2)

我已经回答了很多结构化数组问题,以及一些蒙面数组问题,但从未探索过它们的组合。 Masking已经成为numpy很长一段时间的一部分。结构化数组更新。目前还不清楚开发商是否会特别努力开发。我必须查看/usr/lib/python3/dist-packages/numpy/ma/core.py中的代码。

但很明显,跨领域的功能是有限的。

您可以“查看”字段的子集:

In [116]: a['pos'][['y','x']]
Out[116]: 
array([(17.9, 23.2), (16.9, 13.4)], 
      dtype=[('y', '<f8'), ('x', '<f8')])

但您无法一次设置多个字段:

In [117]: a['pos'][['y','x']]=0
...
IndexError: unsupported iterator index

并没有实现与这些列视图的比较(可能还有其他操作)。

In [123]: a['pos'][['y','x']]>b['pos'][['y','x']]
...
TypeError: unorderable types: numpy.ndarray() > numpy.ndarray()

unutbu已经提出了迭代方法:

In [127]: [a['pos'][name]>b['pos'][name] for name in ['x','y']]
Out[127]: [array([ True, False], dtype=bool), array([False, False], dtype=bool)]

在处理结构化数组时,迭代dtype的名称是很常见的。复制数组的recarray函数,按字段复制执行此类字段(如果需要,可以递归执行)。 genfromtxt在将您的平面输入列表转换为与dtype匹配的嵌套元组时,可能会执行某种名称迭代。

将深层嵌套级别转换为数组可能会有所帮助。例如,我可以将('x','y')转换为(2,)数组:

In [141]: a1=np.array([('12 23 0', (23.2, 17.9), '0'), ('12 23 1', (13.4, 16.9), '0')], 
      dtype=[('id', '<U7'), ('pos', '<f8',(2,)), ('flag', '<U1')])
In [142]: b1=np.array([('', (23.0, 17.91), '0')], dtype=a1.dtype)
In [143]: a1['pos']>b1['pos']
Out[143]: 
array([[ True, False],
       [False, False]], dtype=bool)
In [145]: a1['pos']
Out[145]: 
array([[ 23.2,  17.9],
       [ 13.4,  16.9]])

我可以使用acopyview将其转换为数字数组,与原始reshape进行相同的比较。 copy将所需的数据元素放在一个连续的缓冲区中,view更改dtype(不更改数据缓冲区)。

In [150]: a['pos'].copy().view(float)
Out[150]: array([ 23.2,  17.9,  13.4,  16.9])

In [153]: a['pos'].copy().view(float).reshape(-1,2)>b['pos'].copy().view(float)
Out[153]: 
array([[ True, False],
       [False, False]], dtype=bool)