Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/348.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 查找每个唯一箱子的最大位置(binargmax) 安装程序_Python_Numpy - Fatal编程技术网

Python 查找每个唯一箱子的最大位置(binargmax) 安装程序

Python 查找每个唯一箱子的最大位置(binargmax) 安装程序,python,numpy,Python,Numpy,如果我有 bins = np.array([0, 0, 1, 1, 2, 2, 2, 0, 1, 2]) vals = np.array([8, 7, 3, 4, 1, 2, 6, 5, 0, 9]) k = 3 我需要在bin中唯一bin的最大值位置 # Bin == 0 # ↓ ↓ ↓ # [0 0 1 1 2 2 2 0 1 2] # [8 7 3 4 1 2 6 5 0 9] # ↑ ↑ ↑ # ⇧ # [0 1 2 3 4 5 6 7 8

如果我有

bins = np.array([0, 0, 1, 1, 2, 2, 2, 0, 1, 2])
vals = np.array([8, 7, 3, 4, 1, 2, 6, 5, 0, 9])
k = 3
我需要在
bin
中唯一bin的最大值位置

# Bin == 0
#  ↓ ↓           ↓
# [0 0 1 1 2 2 2 0 1 2]
# [8 7 3 4 1 2 6 5 0 9]
#  ↑ ↑           ↑
#  ⇧
# [0 1 2 3 4 5 6 7 8 9]
# Maximum is 8 and happens at position 0

(vals * (bins == 0)).argmax()

0



这些函数很粗糙,甚至不能概括为负值

问题: 如何使用Numpy以最有效的方式获取所有这些值

我试过的。

这是一个有趣的小问题要解决。我的方法是根据
bin
中的值,将索引放入
vals
。使用
where
获取索引为
True
的点,并结合VAL中这些点上的
argmax
给出结果值

def binargmaxA(bins, vals):
    res = []
    for v in unique(bins):
        idx = (bins==v)
        r = where(idx)[0][argmax(vals[idx])]
        res.append(r)
    return array(res)
通过使用
range(k)
获取可能的bin值,可以删除对
unique
的调用。这加快了速度,但随着k的增大,仍然会导致性能不佳

def binargmaxA2(bins, vals, k):
    res = []
    for v in range(k):
        idx = (bins==v)
        r = where(idx)[0][argmax(vals[idx])]
        res.append(r)
    return array(res)
最后一次尝试,比较每一个值会大大降低速度。此版本计算值的排序数组,而不是对每个唯一值进行比较。实际上,它会计算排序后的索引,并且只在需要时获取排序后的值,因为这样可以避免将VAL一次性加载到内存中。性能仍然随存储箱的数量而变化,但比以前慢得多

def binargmaxB(bins, vals):
    idx = argsort(bins)   # Find sorted indices
    split = r_[0, where(diff(bins[idx]))[0]+1, len(bins)]  # Compute where values start in sorted array
    newmax = [argmax(vals[idx[i1:i2]]) for i1, i2 in zip(split, split[1:])]  # Find max for each value in sorted array
    return idx[newmax +split[:-1]] # Convert to indices in unsorted array
基准 以下是一些基准测试和其他答案

3000个元素 数据集稍大(
bins=randint(0,30,3000);vals=randn(3000)
;k=30;)

  • 171usbinargmax\u比例\u按Divakar排序2
  • 209us这个答案,版本B
  • 281usbinargmax\u比例\u按Divakar排序
  • 329us用户广播版本545424
  • 399us这个答案,版本A
  • 416us由sacul使用lexsort回答
  • 899us皮尔斯平方参考代码
三万元 还有一个更大的数据集(
bins=randint(0,30,30000);vals=randn(30000)
;k=30)。令人惊讶的是,这并没有改变解决方案之间的相对性能

  • 1.27ms此答案,版本B
  • 2.01msbinargmax\u刻度\u按刻度排序2
  • 2.38ms用户广播版本545424
  • 2.68ms此答案,版本A
  • 5.71ms由sacul使用lexsort回答
  • 9.12ms皮尔斯平方参考代码
Edit我没有随着可能的bin值数量的增加而改变
k
,现在我已经修复了基准更加均匀的问题

1000箱值 增加唯一bin值的数量也可能会影响性能。Divakar和sacul的解决方案大多不受影响,而其他解决方案则有相当大的影响。
bins=randint(0,1000,30000);VAL=兰特(30000);k=1000

  • 1.99msbinargmax\u刻度\u按刻度排序2
  • 3.48ms此答案,版本B
  • 6.15ms由sacul使用lexsort回答
  • 10.6ms皮尔斯平方参考代码
  • 27.2ms此答案,版本A
  • 129ms用户广播版本545424
编辑包括问题中参考代码的基准测试,它的竞争力令人惊讶,尤其是与更多的BIN相比。

索引的
numpy\u
库: 我知道这在技术上不是
numpy
,但是
numpy\u索引的
库有一个向量化的
group\u by
函数,非常适合于此,只是想作为我经常使用的一个替代方案来分享:

>>> import numpy_indexed as npi
>>> npi.group_by(bins).argmax(vals)
(array([0, 1, 2]), array([0, 3, 9], dtype=int64))

使用简单的
pandas
groupby
idxmax

使用
sparse.csr\u矩阵
此选项在非常大的输入上非常快速

sparse.csr_matrix(
    (vals, bins, np.arange(vals.shape[0]+1)), (vals.shape[0], k)
).argmax(0)

# matrix([[0, 3, 9]])

演出 功能

设置

结果

结果的
k
(这是广播受到严重冲击的地方):

从图中可以明显看出,当组数较少时,广播是一个很好的技巧,但是广播的时间复杂度/内存在较高的
k
值下增长过快,从而使其具有很高的性能。

这方面如何:

>>> import numpy as np
>>> bins = np.array([0, 0, 1, 1, 2, 2, 2, 0, 1, 2])
>>> vals = np.array([8, 7, 3, 4, 1, 2, 6, 5, 0, 9])
>>> k = 3
>>> np.argmax(vals*(bins == np.arange(k)[:,np.newaxis]),axis=-1)
array([0, 3, 9])

这里有一种方法是对每组数据进行偏移,这样我们就可以一次性对整个数据使用
argsort
-

def binargmax_scale_sort(bins, vals):
    w = np.bincount(bins)
    valid_mask = w!=0
    last_idx = w[valid_mask].cumsum()-1
    scaled_vals = bins*(vals.max()+1) + vals
    #unique_bins = np.flatnonzero(valid_mask) # if needed
    return len(bins) -1 -np.argsort(scaled_vals[::-1], kind='mergesort')[last_idx]

如果你想要可读性,这可能不是最好的解决方案,但我认为它是可行的

def binargsort(bins,vals):
    s = np.lexsort((vals,bins))
    s2 = np.sort(bins)
    msk = np.roll(s2,-1) != s2
    # or use this for msk, but not noticeably better for performance:
    # msk = np.append(np.diff(np.sort(bins)),1).astype(bool)
    return s[msk]

array([0, 3, 9])
说明

lexsort
根据
bin
的排序顺序,然后按照
vals
的顺序,对
vals的索引进行排序:

>>> np.lexsort((vals,bins))
array([7, 1, 0, 8, 2, 3, 4, 5, 6, 9])
因此,您可以通过排序的
从一个索引到下一个索引的不同位置来屏蔽:

>>> np.sort(bins)
array([0, 0, 0, 1, 1, 1, 2, 2, 2, 2])

# Find where sorted bins end, use that as your mask on the `lexsort`
>>> np.append(np.diff(np.sort(bins)),1)
array([0, 0, 1, 0, 0, 1, 0, 0, 0, 1])

>>> np.lexsort((vals,bins))[np.append(np.diff(np.sort(bins)),1).astype(bool)]
array([0, 3, 9])

好的,这是我的线性时间条目,只使用索引和
np.(max | min)inum.at
。它假设箱子从0上升到最大(箱子)


我知道你说过要用Numpy,但如果熊猫是可以接受的:

import numpy as np; import pandas as pd;
(pd.DataFrame(
    {'bins':np.array([0, 0, 1, 1, 2, 2, 2, 0, 1, 2]),
     'values':np.array([8, 7, 3, 4, 1, 2, 6, 5, 0, 9])}) 
.groupby('bins')
.idxmax())

      values
bins        
0          0
1          3
2          9

因此,k始终是唯一的存储箱数?是的,并且应该与
存储箱相同。max()+1
是否保证每个存储箱的值是唯一的?你想要所有的maxima吗?不保证,我想要第一个位置。像
np.array([1,2,2]).argmax()
返回
1
@user3483203Sure。。。(:对不起,我错过了。完成了!这很聪明(:时间复杂度和内存需求将随着大k而增加(我想)@piRSquared,我已经为此设置了一些基准。30个左右的存储箱效果很好,性能下降了1000次。只有3个存储箱,这是迄今为止最快的答案。我也在这样做。这应该与
vals
的长度成线性关系。当我应用Numba的
njit
时,我的初始方法是最快的。我会展示它。我想要一个O(n)Numpy方法。这确实很接近。看valida
res = pd.DataFrame(
       index=['chris', 'chris2', 'chris3', 'divakar', 'divakar2', 'user545424', 'user2699', 'sacul', 'piRSquared'],
       columns=[10, 50, 100, 500, 1000, 5000, 10000, 50000, 100000, 500000],
       dtype=float
)

k = 500

for f in res.index:
    for c in res.columns:
        bins = np.random.randint(0, k, c)
        vals = np.random.rand(c)
        df = pd.DataFrame({'bins': bins, 'vals': vals})
        stmt = '{}(df)'.format(f) if f in {'chris2'} else '{}(bins, vals, k)'.format(f)
        setp = 'from __main__ import bins, vals, df, k, {}'.format(f)
        res.at[f, c] = timeit(stmt, setp, number=50)

ax = res.div(res.min()).T.plot(loglog=True)
ax.set_xlabel("N");
ax.set_ylabel("time (relative)");

plt.show()
>>> import numpy as np
>>> bins = np.array([0, 0, 1, 1, 2, 2, 2, 0, 1, 2])
>>> vals = np.array([8, 7, 3, 4, 1, 2, 6, 5, 0, 9])
>>> k = 3
>>> np.argmax(vals*(bins == np.arange(k)[:,np.newaxis]),axis=-1)
array([0, 3, 9])
def binargmax_scale_sort(bins, vals):
    w = np.bincount(bins)
    valid_mask = w!=0
    last_idx = w[valid_mask].cumsum()-1
    scaled_vals = bins*(vals.max()+1) + vals
    #unique_bins = np.flatnonzero(valid_mask) # if needed
    return len(bins) -1 -np.argsort(scaled_vals[::-1], kind='mergesort')[last_idx]
def binargsort(bins,vals):
    s = np.lexsort((vals,bins))
    s2 = np.sort(bins)
    msk = np.roll(s2,-1) != s2
    # or use this for msk, but not noticeably better for performance:
    # msk = np.append(np.diff(np.sort(bins)),1).astype(bool)
    return s[msk]

array([0, 3, 9])
>>> np.lexsort((vals,bins))
array([7, 1, 0, 8, 2, 3, 4, 5, 6, 9])
>>> np.sort(bins)
array([0, 0, 0, 1, 1, 1, 2, 2, 2, 2])

# Find where sorted bins end, use that as your mask on the `lexsort`
>>> np.append(np.diff(np.sort(bins)),1)
array([0, 0, 1, 0, 0, 1, 0, 0, 0, 1])

>>> np.lexsort((vals,bins))[np.append(np.diff(np.sort(bins)),1).astype(bool)]
array([0, 3, 9])
def via_at(bins, vals):
    max_vals = np.full(bins.max()+1, -np.inf)
    np.maximum.at(max_vals, bins, vals)
    expanded = max_vals[bins]
    max_idx = np.full_like(max_vals, np.inf)
    np.minimum.at(max_idx, bins, np.where(vals == expanded, np.arange(len(bins)), np.inf))
    return max_vals, max_idx
import numpy as np; import pandas as pd;
(pd.DataFrame(
    {'bins':np.array([0, 0, 1, 1, 2, 2, 2, 0, 1, 2]),
     'values':np.array([8, 7, 3, 4, 1, 2, 6, 5, 0, 9])}) 
.groupby('bins')
.idxmax())

      values
bins        
0          0
1          3
2          9