Numpy: reduceat角箱(Trac#236)

创建于 2012-10-19  ·  49评论  ·  资料来源: numpy/numpy

_trac用户martin_wiechert于2006-08-07发出的原始工单http://projects.scipy.org/numpy/ticket/236 ,已分配给未知对象。

.reduceat无法正确处理重复的索引。 重复索引时,应返回操作的中性元素。 在下面的示例中,期望的是[0,10],而不是[1,10]。

In [1]:import numpy

In [2]:numpy.version.version
Out[2]:'1.0b1'

In [3]:a = numpy.arange (5)

In [4]:numpy.add.reduceat (a, (1,1))
Out[4]:array([ 1, 10])
01 - Enhancement 23 - Wish List numpy.core

最有用的评论

reduceat的主要动机是为了最大速度避免超过reduce的循环。 因此,我不太确定reduce上的for循环包装器是否对Numpy有用。 这将违反reduceat主要目的。

此外,作为reduce循环的快速矢量化替代, reduceat存在和API的逻辑是干净且有用的。 我不会弃用它,而是会修复它。

关于reduceat速度,让我们考虑一个简单的示例,但类似于我在自己的代码中使用reduceat一些实际案例:

n = 10000
arr = np.random.random(n)
inds = np.random.randint(0, n, n//10)
inds.sort()

%timeit out = np.add.reduceat(arr, inds)
10000 loops, best of 3: 42.1 µs per loop

%timeit out = piecewise_reduce(np.add, arr, inds)
100 loops, best of 3: 6.03 ms per loop

这是超过100倍的时间差,说明保持reduceat效率的重要性。

总之,与引入新功能相比,我将优先解决reduceat问题。

在某些情况下虽然很有用的start_indicesend_indices通常是多余的,我认为这是可能的补充,但不能解决当前的reduceat不一致行为。

所有49条评论

_ @ teoliphant发表于2006-08-08_

不幸的是,对于这种特殊情况,也许NumPy的reduceat方法遵循Numeric的reduceat方法的行为。

在索引相等的情况下,没有工具可以返回操作的“标识”元素。 如果条带返回空序列,则定义的行为是返回第一个索引给出的元素。 因此,在这种情况下reducat的记录和实际行为是为了构造

[a [1],add.reduce(a [1:])]

这是一项功能请求。

_trac用户martin_wiechert写于2006-08-08_

另请参阅门票#835

里程碑由@alberts于2007-05-12更改为1.1

里程碑由@cournape于2009-03-02更改为Unscheduled

我认为这与#835紧密相关:如果索引之一是len(a) ,则reduceat无法在该索引处输出元素,如果出现索引len(a) ,则需要此元素或在索引末尾重复。

一些解决方案:

  • reduceat的选项不在输出中设置任何值,其中end - start == 0
  • 一个将输出设置为给定固定值的选项,其中end - start == 0
  • 一个where参数,例如ufunc() ,它掩盖了应该完全计算出的输出。

在这个问题上还有其他想法吗? 我对将输出设置为结束-开始== 0的标识值(如果存在)的选项感兴趣。

我坚决支持更改此长期公开问​​题中建议的reduceat行为。 它看起来像一个明显的错误或明显的设计错误,阻碍了这种出色的Numpy结构的实用性。

reduceat在所有索引上的表现应一致。 即,对于每个索引i, ufunc.reduceat(a, indices)应该返回ufunc.reduce(a[indices[i]:indices[i+1]])

对于indices[i] == indices[i+1]情况也应该如此。 我看不出任何明智的原因,在这种情况下,reduceat应该返回a[indices[i]]而不是ufunc.reduce(a[indices[i]:indices[i+1]])

另请参阅此处熊猫创造者Wes McKinney的类似评论。

哇,这确实是可怕的和破裂的。

我们需要在邮件列表上进行一些讨论,但至少我会这样
完全赞成在下一个版本中将其发布为FutureWarning
并在稍后发布时修复该行为。 我们需要有人带走
导致开始讨论并编写补丁。 也许就是你吗?

感谢您的支持。 如果有帮助,我可以开始讨论,但不幸的是,不能修补C代码。

对于没有身份的ufunc(例如np.maximum),您打算做什么?

对于此类功能,空归约应该是一个错误,因为它已经是
当您使用.reduce()而不是.reduceat()时。

实际上,该行为应由与ufunc.reduce(a[indices[i]:indices[i+1]])的一致性来驱动,这是每个用户期望的。 因此,这不需要新的设计决策。 在我看来,这确实像是一个长期存在的错误修复程序。 除非有人能证明当前的不一致行为是合理的。

@njsmith我无法注册Numpy列表。 我在这里https://mail.scipy.org/mailman/listinfo/numpy-discussion发送了地址,但是没有收到任何“要求确认的电子邮件”。 不确定是否需要特殊要求才能订阅...

@divenex :您是否检查了垃圾邮件文件夹? (我总是忘记这样做...)否则,我不确定会出什么问题。 除了“拥有电子邮件地址”之外,订阅绝对没有任何特殊要求。 如果您仍然无法正常运行,请说出来,我们将尝试跟踪相关的sysadmin ...我们肯定想知道它是否已损坏。

ufunc.reduce(a[indices[i]:indices[i+1]])一致的reduceat版本会非常非常好。 它将更加有用! 选择行为的参数或新函数( reduce_intervalsreduce_segments ?...?)都将避免破坏向后不兼容。

我可能很想一起弃用np.ufunc.reduceat -能够指定一组开始和结束索引似乎更有用,以避免出现indices[i] > indices[i+1] 。 此外,名称at表示与at相似性比通常存在的相似性大得多

我建议的替代品是np.piecewise_reduce np.reducebins ,可能是纯python,它基本上可以做到:

def reducebins(func, arr, start=None, stop=None, axis=-1, out=None):
    """
    Compute (in the 1d case) `out[i] = func.reduce(arr[start[i]:stop[i]])`

    If only `start` is specified, this computes the same reduce at `reduceat` did:

        `out[i]  = func.reduce(arr[start[i]:start[i+1]])`
        `out[-1] = func.reduce(arr[start[-1]:])`

    If only `stop` is specified, this computes:

        `out[0] = func.reduce(arr[:stop[0]])`
        `out[i] = func.reduce(arr[stop[i-1]:stop[i]])`

    """
    # convert to 1d arrays
    if start is not None:
        start = np.array(start, copy=False, ndmin=1, dtype=np.intp)
        assert start.ndim == 1
    if stop is not None:
        stop = np.array(stop, copy=False, ndmin=1, dtype=np.intp)
        assert stop.ndim == 1

    # default arguments that do useful things
    if start is None and stop is None:
        raise ValueError('At least one of start and stop must be specified')
    elif stop is None:
        # start only means reduce from one index to the next, and the last to the end
        stop = np.empty_like(start)
        stop[:-1] = start[1:]
        stop[-1] = arr.shape[axis]
    elif start is None:
        # stop only means reduce from the start to the first index, and one index to the next
        start = np.empty_like(stop)
        start[1:] = stop[:-1]
        start[0] = 0
    else:
        # TODO: possibly confusing?
        start, stop = np.broadcast_arrays(start, stop)

    # allocate output - not clear how to do this safely for subclasses
    if not out:
        sh = list(arr.shape)
        sh[axis] = len(stop)
        sh = tuple(sh)
        out = np.empty(shape=sh)

    # below assumes axis=0 for brevity here
    for i, (si, ei) in enumerate(zip(start, stop)):
        func.reduce(arr[si:ei,...], out=out[i, ...], axis=axis)
    return out

具有以下优良特性:

  • np.add.reduce(arr)np.piecewise_reduce(np.add, arr, 0, len(arr))
  • np.add.reduceat(arr, inds)np.piecewise_reduce(np.add, arr, inds)
  • np.add.accumulate(arr)np.piecewise_reduce(np.add, arr, 0, np.arange(len(arr)))

现在,这是否要通过__array_ufunc__机制? func.reduce应该已经涵盖了大多数需要处理的内容-唯一的问题是np.empty行,这是np.concatenate分享的问题。

从API角度看,这对我来说是一个不错的解决方案。 甚至只能够指定两组索引为reduceat就足够了。 从实现角度看? 好吧,如果能够提供好处,那么更改当前的PyUFunc_Reduceat以支持拥有两组inds并不是很困难。 如果我们真的看到了有效支持类似累积的用例的优势,那么做到这一点也不难。

Marten在〜1的类似讨论中提出了与此类似的内容
一年前,但他还提到了添加“ step”选项的可能性:

http://numpy-discussion.10968.n7.nabble.com/Behavior-of-reduceat-td42667.html

我喜欢您的建议的事物(如果有人在计数,则为+1):

  • 创建一个新功能,而不是试图挽救现有功能
    一。
  • 使开始索引和结束索引参数具体,而不是
    从多维数组中神奇地找出它们。
  • None索引的默认值非常整洁。

对于此新功能,我认为需要深思的事情很重要:

  • 我们应该将“步骤”作为一种选择吗? (我会说)
  • 广播索引数组是否有意义,还是必须将它们广播
    是一维的?
  • 这应该是np函数还是ufunc方法? (我想我更喜欢
    作为一种方法)

从自行车脱落部门,我更喜欢:

  • 给它起一个更令人难忘的名字,但我没有建议。
  • 使用“开始”和“停止”(如果我们决定这样做,则使用“步骤”)
    与np.arange和Python的slice保持一致。
  • 从kwarg名称中删除_indices。

海梅

2017年4月13日星期四,下午1:47,Eric Wieser [email protected]
写道:

我可能很想一起弃用np.ufunc.reduceat-它
能够指定一组开始和结束索引似乎更为有用,
避免索引[i]>索引[i + 1]的情况。 另外,名字
与atat相比,存在更大的相似性

我提议的替代品是np.piecewise_reduce,基本上
确实:

def piecewise_reduce(func,arr,start_indices = None,end_indices = None,axis = -1,out = None):
如果start_indices为None且end_indices为None:
start_indices = np.array([0],dtype = np.intp)
end_indices = np.array(arr.shape [axis],dtype = np.intp)
elif end_indices为None:
end_indices = np.empty_like(start_indices)
end_indices [:-1] = start_indices [1:]
end_indices [-1] = arr.shape [axis]
elif start_indices为None:
start_indices = np.empty_like(end_indices)
start_indices [1:] = end_indices
end_indices [0] = 0
其他:
断言len(start_indices)== len(end_indices)

if not out:
    sh = list(arr.shape)
    sh[axis] = len(end_indices)
    out = np.empty(shape=sh)

# below assumes axis=0 for brevity here
for i, (si, ei) in enumerate(zip(start_indices, end_indices)):
    func.reduce(arr[si:ei,...], out=alloc[i, ...], axis=axis)
return out

具有以下优良特性:

  • np.ufunc.reduce与np.piecewise_reduce(func,arr,0,
    len(arr))
  • np.ufunc.accumulate与`np.piecewise_reduce(func,arr,
    np.zeros(len(arr)),np.arange(len(arr)))

现在,这是否要通过__array_ufunc__机制? 大多数
func.reduce已经涵盖了需要处理的内容-
唯一的问题是np.empty行,这是np.concatenate的问题
分享。

-
您收到此消息是因为您已订阅此线程。
直接回复此电子邮件,在GitHub上查看
https://github.com/numpy/numpy/issues/834#issuecomment-293867746或静音
线程
https://github.com/notifications/unsubscribe-auth/ADMGdtjSCodONyu6gCpwofdBaJMCIKa-ks5rvgtrgaJpZM4ANcqc

-
(__ /)
(哦)
(> <)Este es Conejo。 Copia a Conejo en tu firma yayúdaleen sus planes
dedominaciónmundial。

使用“开始”和“停止”

完成了

我们是否应该将“步骤”作为一种选择

似乎是一个非常狭窄的用例

广播索引数组是否有意义,还是必须为一维

更新。 > 1d显然很糟糕,但是我认为在类似累积的情况下,我们应该允许0d和广播。

这应该是np函数还是ufunc方法? (我想我更喜欢
作为一种方法)

每个ufunc方法都需要__array_ufunc__处理。

reduceat的主要动机是为了最大速度避免超过reduce的循环。 因此,我不太确定reduce上的for循环包装器是否对Numpy有用。 这将违反reduceat主要目的。

此外,作为reduce循环的快速矢量化替代, reduceat存在和API的逻辑是干净且有用的。 我不会弃用它,而是会修复它。

关于reduceat速度,让我们考虑一个简单的示例,但类似于我在自己的代码中使用reduceat一些实际案例:

n = 10000
arr = np.random.random(n)
inds = np.random.randint(0, n, n//10)
inds.sort()

%timeit out = np.add.reduceat(arr, inds)
10000 loops, best of 3: 42.1 µs per loop

%timeit out = piecewise_reduce(np.add, arr, inds)
100 loops, best of 3: 6.03 ms per loop

这是超过100倍的时间差,说明保持reduceat效率的重要性。

总之,与引入新功能相比,我将优先解决reduceat问题。

在某些情况下虽然很有用的start_indicesend_indices通常是多余的,我认为这是可能的补充,但不能解决当前的reduceat不一致行为。

我不认为允许开始和停止索引来自不同的数组
如果在C中实施,将会对效率产生很大的影响。

2017年4月13日23:40,divenex [email protected]写道:

reduceat的主要动机是避免为
最大速度。 所以我不能完全确定for循环的包装
减少将是Numpy的非常有用的补充。 这会违背
减少主要目的。

此外,作为快速向量化的约简逻辑和API的逻辑
替换为reduce的循环是干净且有用的。 我不会
弃用它,而是修复它。

关于减速速度,让我们考虑一个简单的示例,但与
我在自己的代码中使用了reduceat的一些实际案例:

n = 10000
arr = np.random.random(n)
inds = np.random.randint(0,n,n // 10)
inds.sort()
%timeit out = np.add.reduceat(arr,inds)10000个循环,每个循环最好3:42.1 µs
%timeit out = piecewise_reduce(np.add,arr,inds)100个循环,每个循环最好3:6.03毫秒

这是超过100倍的时差,说明了重要性
保持还原效率。

总而言之,与引入新功能相比,我将优先解决reduceat问题。
职能。

拥有start_indices和end_indices在某些情况下仍然很有用
通常是多余的,我会认为这是可能的补充,但不是解决方法
对于当前的reducat不一致的行为。

-
您收到此邮件是因为您发表了评论。
直接回复此电子邮件,在GitHub上查看
https://github.com/numpy/numpy/issues/834#issuecomment-293898215或静音
线程
https://github.com/notifications/unsubscribe-auth/AAEz6xPex0fo2y_MqVHbNP5YNkJ0CBJrks5rviW-gaJpZM4ANcqc

这是超过100倍的时间差,说明了保持减速效率的重要性。

谢谢-我想我低估了与reduce呼叫的第一阶段相关的开销(这只发生在reduceat )。

不是反对自由函数的论点,而是反对反对在纯python中实现它的论点

但不能解决当前减少的不一致行为。

问题是,更改已经存在很长时间的代码的行为很棘手。


另一个可能的扩展:当indices[i] > indices[j] ,计算反函数:

    for i, (si, ei) in enumerate(zip(start, stop)):
        if si >= ei:
            func.reduce(arr[si:ei,...], out=out[i, ...], axis=axis)
        else:
            func.reduce(arr[ei:si,...], out=out[i, ...], axis=axis)
            func.inverse(func.identity, out[i, ...], out=out[i, ...])

其中np.add.inverse = np.subtractnp.multiply.inverse = np.true_divide 。 这导致了一个不错的属性

func.reduce(func.reduceat(x, inds_from_0)) == func.reduce(x))

例如

a = [1, 2, 3, 4]
inds = [0, 3, 1]
result = np.add.reduceat(a, inds) # [6, -5, 9] == [(1 + 2 + 3), -(3 + 2), (2 + 3 + 4)]

问题是,更改已经存在很长时间的代码的行为很棘手。

这部分是为什么我建议在电子邮件线程中给二维索引数组赋予特殊含义的原因,其中二维为2或3:然后将其有效地解释为一叠切片。 但是我意识到这也有些混乱,当然也可以使用reduce_by_sliceslicereducereduceslice方法。

ps我确实认为对许多ufunc都有效的任何方法都应该是一种方法,以便可以将其通过__array_ufunc__传递并被覆盖。

实际上,我认为是另一种更好的建议:与其挽救reduceat ,不如添加一个slice参数(或startstopstep )到ufunc.reduce !? 正如@ eric-wieser指出的那样,任何此类实现方式都意味着我们可以完全弃用reduceat ,因为

add.reduce(array, slice=slice(indices[:-1], indices[1:])

(现在我们可以自由地使行为与空片的预期匹配)

在这里,如果切片是0维,则将广播切片,如果使用轴的元组,甚至可能考虑传入切片的元组。

编辑:使上面的slice(indices[:-1], indices[1:])允许扩展到一个切片元组( slice可以容纳任意数据,因此可以正常工作)。

我仍然会找到对reduceat的修复,以使其成为最合理的设计解决方案reduce的正确100%向量化版本。 或者,为避免破坏代码(但请参见下文),可以创建一个名为reducebins的等效方法,该方法只是reduceat的更正版本。 实际上,我同意@ eric-wieser的观点,即reduceat的命名传达了与at函数的更多联系。

我确实知道不必破坏代码。 但是我必须说,我很难想象有很多代码取决于旧的行为,因为它根本就没有逻辑上的意义,我将其称为一个长期存在的错误。 我希望使用reduceat只是确保不重复indices ,以避免reduceat的无意义结果,或者像我使用out[:-1] *= np.diff(indices) > 0一样固定输出

我对@mhvk slice解决方案并不完全确信,因为它为slice构造引入了非标准用法。 此外,这与reduce的当前设计思想不一致,即“通过沿一个轴应用ufunc将a的尺寸减小一倍。” _

对于startend索引,我也都没有看到引人注目的用户案例。 实际上,我看到了当前reduceat方法的漂亮设计逻辑,在概念上类似于np.histogram ,其中bins ,其中_“定义bin边,” _替换为indices ,它也代表垃圾箱边缘,但是在索引空间而不是值中。 reduceat将函数应用到每对容器边中包含的元素。 直方图是一种非常流行的构造,但不需要,并且在Numpy中也不包括传递两个左右边缘向量的选项。 对于相同的原因,我怀疑reduceat或它的替代品都强烈需要这两个边缘。

reduceat的主要动机是为了最大速度而避免循环减小。 因此,我不能完全确定for循环reduce的包装是否对Numpy非常有用。 这将违背reduceat的主要目的。

我在这里同意@divenexreduceat要求对索引进行排序和重叠这一事实是一个合理的约束条件,可确保通过对数据的单次传递就可以以缓存有效的方式计算循环。 如果要重叠箱,几乎可以肯定有更好的方法来计算所需的操作(例如,滚动窗口聚合)​​。

我也同意,最干净的解决方案是使用固定的API定义新方法,例如reducebins (并弃用reduceat ),而不是尝试将其压缩为reduce已经有所作为。

嗨,大家好,

我想勉强讨论这是一个错误的讨论。 这是文档字符串中记录的行为

For i in ``range(len(indices))``, `reduceat` computes
``ufunc.reduce(a[indices[i]:indices[i+1]])``, which becomes the i-th
generalized "row" parallel to `axis` in the final result (i.e., in a
2-D array, for example, if `axis = 0`, it becomes the i-th row, but if
`axis = 1`, it becomes the i-th column).  There are three exceptions to this:

* when ``i = len(indices) - 1`` (so for the last index),
  ``indices[i+1] = a.shape[axis]``.
* if ``indices[i] >= indices[i + 1]``, the i-th generalized "row" is
  simply ``a[indices[i]]``.
* if ``indices[i] >= len(a)`` or ``indices[i] < 0``, an error is raised.

因此,我反对任何更改reduceat行为的尝试。

快速的github搜索显示了该功能的许多使用。 这里的每个人都确定他们都只使用严格增加的索引吗?

关于功能的行为,我认为没有单独的启动/停止数组,功能将受到严重阻碍。 在许多情况下,人们可能希望在没有规则排列的重叠窗口中测量值(因此滚动窗口将无法工作)。 例如,通过某些独立方法确定感兴趣区域。 @divenex表明,与Python迭代相比,性能差异可能很大。

在许多情况下,人们可能希望在没有规则排列的重叠窗口中测量值(因此滚动窗口将无法工作)。

是的,但是您不希望使用朴素的循环,例如reduceat实现的循环。 您希望实现自己的滚动窗口计算,以某种方式存储中间结果,以便可以在数据上进行一次线性传递来完成。 但是现在我们谈论的是一种比reduceat复杂得多的算法。

@shoyer我可以设想只有某些ROI重叠的情况。 在这种情况下,编写自定义算法将是过大的。 别忘了我们的主要用户群是科学家,他们通常时间紧缺,需要“足够好”的解决方案,而不是绝对的最佳方案。 与np.reduceat的复杂性相关的常量常数较低,这意味着很难或不可能用纯Python代码获得更好的解决方案-通常是用户愿意编写的唯一代码。

@jni当然,将其分为任意开始和停止位置的组可能很有用。 但是对我来说,这感觉像是范围的显着增加,并且它更适合于另一种方法,而不是代替reduceat (即使我们从不删除它,我们当然也想弃用)。

分为任意起点和终点的组可能很有用。 但这对我来说范围似乎很大

这对我来说似乎微不足道。 现在,我们有基本执行ind1 = indices[i], ind2 = indices[i + 1] 。 将其更改为使用两个不同的数组而不是同一数组应该很少。

而且,通过连续范围时的单次通过行为应该几乎与现在一样快—唯一的开销是对nditer的另一个论点

这对我来说似乎微不足道。

究竟。 而且,它是用户具有reduceat (通过使用所有其他索引),但是会因不支持重叠的新功能而丢失。

此外,两索引形式可以模仿旧的(怪异的)行为:

def reduceat(func, arr, inds):
    deprecation_warning()
    start = inds
    stops = zeros(inds.shape)
    stops[:-1] = start[1:]
    stops[-1] = len(arr)
    np.add(stops, 1, where=ends == starts, out=stops)  # reintroduce the "bug" that we would have to keep
    return reducebins(func, arr, starts, stops)

意味着我们不需要维护两个非常相似的实现

对于新的reducebins ,我并不强烈反对startsstops索引,尽管我仍然看不到一个明显的例子,说明它们都需要。 感觉就像通过添加开始和结束bins edges来概括np.histogram ...

最终,只要不影响主要用法,并且仍然可以使用单个索引数组调用reducebins(arr, indices)且不会造成速度损失,这很好。

当然,在许多情况下,需要对不重叠的箱进行操作,但是在这种情况下,我通常希望箱不会仅由成对的边来定义。 这种情况下可用的功能是Scipy的ndimage.labeled_comprehension以及相关功能,例如ndimage.sum等。

但这似乎与reducebins的范围大不相同。

那么,这将是一个自然的使用情况startsstopsreducebins

那么,在reducebins中启动和停止的自然用法是什么?

可以通过其他方式实现,但是长度k的移动平均值将为reducebins(np,add, arr, arange(n-k), k + arange(n-k)) 。 我怀疑,忽略分配索引的成本,性能可以与as_strided方法相媲美。

独特的是, reducebins允许移动平均值的变化持续时间,而as_strided是不可能的

另一个用例-消除以单参数形式包含结尾还是开始。

例如:

a = np.arange(10)
reducebins(np.add, start=[2, 4, 6]) == [2 + 3, 4 + 5, 6 + 7 + 8 + 9]  # what `reduceat` does
reducebins(np.add, stop=[2, 4, 6])  == [0 + 1, 2 + 3, 4 + 5]          # also useful

另一个用例-消除以单参数形式包含结尾还是开始。

我不太明白这一点。 您可以在此处输入输入张量吗? 另外: start / stop的默认值是什么?

无论如何,我并不坚决反对单独的论点,但是它并不是一个替代品。 我希望能够说“不要使用reduceat,而使用reducebins”,但是当界面看起来不同时,这会(稍微)困难一些。

实际上,我只是意识到,即使开始/停止选项也无法涵盖空切片的用例,这在过去对我很有用:当我的属性/标签对应于CSR稀疏矩阵中的行时,并且我使用indptr的值进行减少。 使用reduceat ,我可以忽略空行。 任何替换将需要额外的簿记。 因此,无论您提出什么替代方案,请留下reduceat左右。

In [2]: A = np.random.random((4000, 4000))
In [3]: B = sparse.csr_matrix((A > 0.8) * A)
In [9]: %timeit np.add.reduceat(B.data, B.indptr[:-1]) * (np.diff(B.indptr) > 1)
1000 loops, best of 3: 1.81 ms per loop
In [12]: %timeit B.sum(axis=1).A
100 loops, best of 3: 1.95 ms per loop
In [16]: %timeit np.maximum.reduceat(B.data, B.indptr[:-1]) * (np.diff(B.indptr) > 0)
1000 loops, best of 3: 1.8 ms per loop
In [20]: %timeit B.max(axis=1).A
100 loops, best of 3: 2.12 ms per loop

顺便说一句,空序列难题可以用与Python相同的方法来解决:通过提供一个初始值。 这可以是标量或与indices形状相同的数组。

是的,我同意,首要重点是解决空白问题
案件。 在start = end的情况下,我们可以使用一种方法来设置输出
元素的标识,或者不使用
指定输出数组。 当前的问题是它被覆盖
有不相关的数据

对于他的最后评论,我完全满意

让我们简单地定义out=ufunc.reducebins(a, inds)out[i]=ufunc.reduce(a[inds[i]:inds[i+1]])所有i但最后,并弃用reduceat

startsends索引的当前用例似乎更自然,并且可能通过诸如as_strided或卷积之类的替代功能更有效地实现。

@shoyer

我不太明白这一点。 您可以在此处输入输入张量吗? 另外:启动/停止的默认值是什么?

用输入更新。 在默认值开始的注释中,请参阅reduce_bins的实现。 我也在那里添加了文档字符串。 该实现功能齐全,但速度较慢(由于是python)。

但是当界面看起来不同时,这会(略)困难。

当仅传递一个start参数时,接口是相同的(忽略了我们首先要解决的标识大小写)。 这三行含义相同:

np.add.reduce_at(arr, inds)
reduce_bins(np.add, arr, inds)
reduce_bins(np.add, arr, start=inds)

(我不太在乎方法/函数的区别,我无法在python中将新的ufunc方法定义为原型!)


@jni

实际上,我只是意识到,即使开始/停止选项也无法涵盖空片的用例,这在过去对我很有用

您错了,但确实如此-与ufunc.reduceat完全相同。 也可以简单地通过传递start[i] == end[i]

空序列难题可以通过提供一个初始值来解决。

是的,我们已经介绍了这一点,并且ufunc.reduce已经通过填充ufunc.identity做到了这一点。 这不难添加到现有的ufunc.reduecat ,特别是如果合并了#8952。 但是正如您自己说的那样,当前行为是_documented_,所以我们可能不应该更改它。


@divenex

让我们简单地将除最后一个i之外的所有i都定义为out [i] = ufunc.reduce(a [inds [i]:inds [i + 1]])out = ufunc.reducebins(a,inds)

所以len(out) == len(inds) - 1 ? 这与reduceat的当前行为不同,因此@shoyer关于切换的论点在这里更强


全部:我已经看过较早的评论,并删除了引用的电子邮件回复,因为它们使此讨论难以阅读

@ eric-wieser好点。 在上面的句子中,我的意思是,对于最后一个索引, reducebins的行为将与当前的reduceat 。 但是,在那种情况下,我不确定该值应该是多少,因为最后一个值形式上没有意义。

忽略兼容性问题, reducebins (在一维中)的输出应具有大小inds.size-1 ,这与np.diff(a)具有大小a.size-1np.histogram(a, bins)大小bins.size-1 。 但是,这与以reduceat代替即用型的愿望背道而驰。

我认为没有令人信服的论点认为a.size-1是正确的答案-包括索引0和/或索引n似乎也很合理。 在某些情况下,它们似乎都很方便,但是我认为减少更换非常重要。

还有一个参数要求stop / start隐藏在这里-它允许您构建diff类似的行为,如果需要的话,只需很少的成本,同时仍然保持reduceat行为:

a = np.arange(10)
inds = [2, 4, 6]
reduce_bins(a, start=inds[:-1], stop=inds[1:])  #  [2 + 3, 4 + 5]

# or less efficiently:
reduce_at(a, inds)[:-1}
reduce_bins(a, start=inds)[:-1]
reduce_bins(a, stop=inds)[1:]

@ eric-wieser我可以接受必需的startstop参数,但是我不喜欢将其中一个作为可选参数。 并不明显,仅提供start意味着out[i] = func.reduce(arr[start[i]:start[i+1]])而不是out[i] = func.reduce(arr[start[i]:]) ,这就是我猜到的。

我首选的reducebins API类似于reduceat但没有在docstring中指出令人困惑的“ exceptions”。 即,只是:

对于i in range(len(indices)) ,reduceat计算ufunc.reduce(a[indices[i]:indices[i+1]]) ,它成为最终结果中平行于轴的第i个广义“行”(例如,在二维数组中,如果axis = 0,它将成为第i行,但如果axis = 1,它将成为第i列)。

我可以对第三个“例外”进行任一种选择,后者需要非负索引( 0 <= indices[i] <= a.shape[axis] ),我认为这更多是一个健全性检查,而不是例外。 但是可能也可以-我可以看到负索引可能对某人有用,并且不难进行数学运算来标准化这些索引。

不自动在末尾添加索引确实意味着结果的长度应为len(a)-1 ,如np.histogram

@jni您能否举一个例子,说明您实际上想根据稀疏矩阵中的数组计算出什么? 最好是带有一个非随机数且自包含的具体示例(不取决于scipy.sparse)。

不仅仅提供开始就意味着out [i] = func.reduce(arr [start [i]:start [i + 1]])而不是out [i] = func.reduce(arr [start [i] :]),这就是我猜到的。

我要读的是“每个垃圾箱都从这些位置开始”,这意味着所有垃圾箱都是连续的,除非另有明确说明。 也许我应该尝试起草更完整的文档字符串。 我认为我可以看到一个很强的禁止禁止传递任何参数的论点,因此我将其从我的proposal函数中删除。

需要非负索引(0 <= index [i] <a.shape [axis])

请注意,这里也存在一个错误(#835)-上限应包含在内,因为它们是切片。

请注意,这里还有一个错误-上限应包含在内,因为它们是切片。

固定,谢谢。

不在reduceat函数本身中,您还没有;)

原来:\doc\neps\groupby_additions.rst包含一个针对reduceby函数的(IMO下)建议。

此页面是否有帮助?
0 / 5 - 0 等级