4

快速的问题

我希望能够比较两个保证具有相同 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')])

现在假设我想检查并查找aa['pos']['x']字段大于该b['pos']['x']字段的任何条目并将这些条目返回到一个新的 numpy 数组,这样的事情会起作用

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

现在想象一下,我们只想保留和字段a都大于. 这很简单,因为我们可以再次这样做xyb

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

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

然而,现在假设我们有一个非常复杂的 dtype 用于这些数组(比如有 34 个字段 - 请参阅此处以获取我正在使用的 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)看看是否有人比我想出的想法更好。请注意,您可以通过逐行复制“示例的长问题”部分中的片段来非常轻松地形成 MWE,我认为这样做没有任何理由占用更多空间。

4

2 回答 2

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'],然后使用np.all沿 0 轴减少数组。

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

于 2015-12-22T20:20:25.147 回答
2

我已经回答了很多结构化数组问题和一些掩码数组问题,但从未探索过它们的组合。长期以来,掩蔽一直是其中的一部分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在处理结构化数组时, 迭代 a 的名称是很常见的。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]])

我可以a通过将其转换为数字数组来与原始数组进行相同的比较 - 使用copy,viewreshape. 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)
于 2015-12-23T04:44:14.757 回答