Python/Numpy:否定或补充一个切片

3

我有一些函数,是一个大型分析软件的一部分,需要一个布尔掩码来将数组项分为两组。这些函数是这样的:

def process(data, a_mask):
    b_mask = -a_mask
    res_a = func_a(data[a_mask])
    res_b = func_b(data[b_mask])
    return res_a, res_b

现在,我需要使用这些函数(不做修改)与一个只包含类“a”项的大数组一起使用,但我想节省RAM并且不传递具有所有True的布尔掩码。例如,我可以传递像slice(None,None)这样的切片。
问题是如果a_mask是一个切片,那么行b_mask = -a_mask将失败。理想情况下,-a_mask应该给出0项选择。
我考虑创建一个“修改后”的切片对象,该对象将__neg __()方法实现为null切片(例如slice(0,0))。我不知道这是否可行。
其他解决方案,既不修改process()函数,又避免分配全True布尔数组的方法也将被接受。
3个回答

2
很遗憾,我们无法为slice添加__neg__()方法,因为它不能被子类化。然而,tuple可以被子类化,我们可以使用它来保存一个单独的slice对象。
这引导我想到了一个非常、非常恶劣的hack,但它应该能为你工作:
class NegTuple(tuple):
    def __neg__(self):
        return slice(0)

我们可以创建一个包含单个切片对象的NegTuple:
nt = NegTuple((slice(None),))

这可以用作索引,否定它会产生一个空切片,导致一个长度为0的数组被索引:
a = np.arange(5)
print a[nt]
# [0 1 2 3 4]
print a[-nt]
# []

你必须非常绝望才会采用这样的做法。修改 process 像这样完全不可行吗?
def process(data, a_mask=None):
    if a_mask is None:
        a_mask = slice(None)  # every element
        b_mask = slice(0)     # no elements
    else:
        b_mask = -a_mask
    res_a = func_a(data[a_mask])
    res_b = func_b(data[b_mask])
    return res_a, res_b

这更为明确,不应对您当前的使用情况产生任何影响。

OP正在寻找一种不修改“process”的解决方案。 - Peter Gibson
@PeterGibson 我违背了自己的判断,更新了我的答案并提供了这样的解决方案。 - ali_m
那真是太可怕了,但是你能提供一个可行的解决方案真的很好 :) - Peter Gibson
我同意这不太美观,但是process函数实际上相当复杂,分析的大部分依赖于它的正确性。我宁愿为这个特定的用例做一个hack,也不想冒险去干扰其他(已经经过充分测试)的用例。谢谢。 - user2304916
随你便,但如果是我,我会更关注黑客行为可能出现的问题。你的process函数内部有多么复杂并不重要 - 最终,所有的改变只会涉及一个关键字参数和5或6行新代码来处理条件判断。 - ali_m

0

你的解决方案与退化稀疏布尔数组非常相似,尽管我不知道是否有相同的实现。我的第一反应是不喜欢,但如果你真的不能修改process,那么这可能是最好的方法。


什么是退化稀疏布尔数组,它如何避免创建全为True的布尔数组? - user2304916
@user2304916 稀疏矩阵可以压缩某些值(通常为0)。布尔稀疏矩阵可以轻松地编写以压缩占据大多数的任何值(True或False)。你的方法是退化的,因为它只适用于全零或全一的矩阵。 - U2EF1
是的,一个可以压缩True或False的布尔稀疏矩阵可以完成任务。在scipy/numpy中拥有这样的工具会很好。 - user2304916

0

如果您关心内存使用情况,那么高级索引可能是一个不好的主意。来自文档

高级索引总是返回数据的副本(与基本切片形成对比,后者返回视图)。

目前,process函数具有:

  • 大小为 ndata
  • 大小为 na_mask(假设使用高级索引)

并创建:

  • 大小为 nb_mask
  • 大小为 mdata[a_mask]
  • 大小为 n-mdata[b_mask]

这实际上是4个大小为n的数组。

看起来基本切片似乎是您最好的选择,但是 Python 似乎不允许子类化 slice

TypeError: Error when calling the metaclass bases
    type 'slice' is not an acceptable base type

查看 @ali_m 的答案,其中包含切片的解决方案。

或者,您可以直接绕过 process 并获取您的结果

result = func_a(data), func_b([])

真的,但有时花式索引是唯一的选择(如果您的目标元素不规则间隔等)。 - ali_m
很遗憾,我无法在这里使用基本切片,因为两个组的元素是无序的。不过感谢您对使用布尔掩码的内存使用情况的洞察。 - user2304916
@user2304916 是的,对于一般用途,我可以看出布尔掩码是必要的。我特别指的是在你的问题中切片是合适的情况。 - Peter Gibson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接