繁体   English   中英

在Python中有效地获得直方图箱的索引

[英]Efficiently get indices of histogram bins in Python

简短的问题

我有一个大的10000x10000元素图像,我把它分成几百个不同的扇区/箱。 然后,我需要对每个bin中包含的值执行一些迭代计算。

如何使用bin值提取每个bin的索引以有效地执行计算?

我正在寻找的是一个解决方案,它避免了每次从我的大型数组中选择ind == j的瓶颈。 有没有办法一次性直接获得属于每个bin的元素的索引?

详细说明

1.直截了当的解决方案

实现我需要的一种方法是使用如下代码(参见例如THIS相关答案),其中我数字化我的值然后有一个j循环选择数字化索引等于j如下所示

import numpy as np

# This function func() is just a place mark for a much more complicated function.
# I am aware that my problem could be easily speed up in the specific case of
# of the sum() function, but I am looking for a general solution to the problem.
def func(x):
    y = np.sum(x)
    return y

vals = np.random.random(1e8)
nbins = 100
bins = np.linspace(0, 1, nbins+1)
ind = np.digitize(vals, bins)

result = [func(vals[ind == j]) for j in range(1, nbins)]

我正在寻找的是一个解决方案,它避免了每次从我的大型数组中选择ind == j的瓶颈。 有没有办法一次性直接获得属于每个bin的元素的索引?

2.使用binned_statistics

对于用户定义函数的一般情况,上述方法与scipy.stats.binned_statistic中实现的方法相同。 直接使用Scipy,可以通过以下方式获得相同的输出

import numpy as np
from scipy.stats import binned_statistics

vals = np.random.random(1e8)
results = binned_statistic(vals, vals, statistic=func, bins=100, range=[0, 1])[0]

3.使用labeled_comprehension

另一个Scipy替代方法是使用scipy.ndimage.measurements.labeled_comprehension 使用该函数,上面的例子就变成了

import numpy as np
from scipy.ndimage import labeled_comprehension

vals = np.random.random(1e8)
nbins = 100
bins = np.linspace(0, 1, nbins+1)
ind = np.digitize(vals, bins)

result = labeled_comprehension(vals, ind, np.arange(1, nbins), func, float, 0)

不幸的是,这种形式效率低下,特别是它没有速度优势超过我原来的例子。

4.与IDL语言比较

为了进一步说明,我正在寻找的是与IDL语言HEREHISTOGRAM函数中的REVERSE_INDICES关键字等效的功能。 这个非常有用的功能可以在Python中有效复制吗?

具体来说,使用IDL语言可以将上面的示例写成

vals = randomu(s, 1e8)
nbins = 100
bins = [0:1:1./nbins]
h = histogram(vals, MIN=bins[0], MAX=bins[-2], NBINS=nbins, REVERSE_INDICES=r)
result = dblarr(nbins)

for j=0, nbins-1 do begin
    jbins = r[r[j]:r[j+1]-1]  ; Selects indices of bin j
    result[j] = func(vals[jbins])
endfor

上面的IDL实现比Numpy快了大约10倍,因为不必为每个bin选择bin的索引。 并且有利于IDL实施的速度差异随着箱的数量而增加。

我发现特定的稀疏矩阵构造函数可以非常有效地实现所需的结果。 它有点模糊,但我们可以为此目的滥用它。 下面的函数可以与scipy.stats.binned_statistic几乎相同的方式使用,但速度可以快几个数量级

import numpy as np
from scipy.sparse import csr_matrix

def binned_statistic(x, values, func, nbins, range):
    '''The usage is nearly the same as scipy.stats.binned_statistic''' 

    N = len(values)
    r0, r1 = range

    digitized = (float(nbins)/(r1 - r0)*(x - r0)).astype(int)
    S = csr_matrix((values, [digitized, np.arange(N)]), shape=(nbins, N))

    return [func(group) for group in np.split(S.data, S.indptr[1:-1])]

我避免使用np.digitize因为它没有使用所有bin都相等宽度因此很慢的事实,但我使用的方法可能无法完美处理所有边缘情况。

我假设无法更改在带有digitize的示例中完成的分箱。 这是一种方法,您可以一次性进行排序。

vals = np.random.random(1e4)
nbins = 100
bins = np.linspace(0, 1, nbins+1)
ind = np.digitize(vals, bins)

new_order = argsort(ind)
ind = ind[new_order]
ordered_vals = vals[new_order]
# slower way of calculating first_hit (first version of this post)
# _,first_hit = unique(ind,return_index=True)
# faster way:
first_hit = searchsorted(ind,arange(1,nbins-1))
first_hit.sort()

#example of using the data:
for j in range(nbins-1):
    #I am using a plotting function for your f, to show that they cluster
    plot(ordered_vals[first_hit[j]:first_hit[j+1]],'o')

该图显示了垃圾箱实际上是预期的集群: 在此输入图像描述

您可以先通过排序数组将计算时间减半,然后使用np.searchsorted

vals = np.random.random(1e8)
vals.sort()

nbins = 100
bins = np.linspace(0, 1, nbins+1)
ind = np.digitize(vals, bins)

results = [func(vals[np.searchsorted(ind,j,side='left'):
                     np.searchsorted(ind,j,side='right')])
           for j in range(1,nbins)]

使用1e8作为我的测试用例,我将从34秒计算到大约17秒。

一个有效的解决方案是使用numpy_indexed包(免责声明:我是它的作者):

import numpy_indexed as npi
npi.group_by(ind).split(vals)

Pandas有一个非常快速的分组代码(我认为它是用C语言编写的),所以如果你不介意加载库,你可以这样做:

import pandas as pd

pdata=pd.DataFrame({'vals':vals,'ind':ind})
resultsp = pdata.groupby('ind').sum().values

或更一般地说:

pdata=pd.DataFrame({'vals':vals,'ind':ind})
resultsp = pdata.groupby('ind').agg(func).values

虽然后者对标准聚合函数较慢(如sum,mean等)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM