python - Numpy 按多个向量分组,获取组索引

标签 python numpy

我有几个 numpy 数组;我想构建一个 groupby 方法,该方法将具有这些数组的组 ID。然后它将允许我在组 ID 上索引这些数组以对组执行操作。

举个例子:

import numpy as np
import pandas as pd
a = np.array([1,1,1,2,2,3])
b = np.array([1,2,2,2,3,3])

def group_np(groupcols):
    groupby = np.array([''.join([str(b) for b in bs]) for bs in zip(*[c for c in groupcols])])
    _, groupby = np.unique(groupby, return_invesrse=True)
   return groupby

def group_pd(groupcols):
    df = pd.DataFrame(groupcols[0])
    for i in range(1, len(groupcols)):
        df[i] = groupcols[i]
    for i in range(len(groupcols)):
        df[i] = df[i].fillna(-1)
    return df.groupby(list(range(len(groupcols)))).grouper.group_info[0]

输出:

group_np([a,b]) -> [0, 1, 1, 2, 3, 4]
group_pd([a,b]) -> [0, 1, 1, 2, 3, 4]

是否有更有效的实现方式,最好是在纯 numpy 中?目前的瓶颈似乎是构建一个向量,该向量将对每个组具有唯一值 - 目前我正在通过将每个向量的值连接为字符串来实现这一点。

我希望它适用于任意数量的输入向量,这些向量可以包含数百万个元素。

编辑:这是另一个测试用例:

a = np.array([1,2,1,1,1,2,3,1])
b = np.array([1,2,2,2,2,3,3,2])

这里,组元素2,3,4,7应该都是一样的。

Edit2:添加一些基准。

a = np.random.randint(1, 1000, 30000000)
b = np.random.randint(1, 1000, 30000000)
c = np.random.randint(1, 1000, 30000000)

def group_np2(groupcols):
    _, groupby = np.unique(np.stack(groupcols), return_inverse=True, axis=1)
    return groupby

%timeit group_np2([a,b,c])
# 25.1 s +/- 1.06 s per loop (mean +/- std. dev. of 7 runs, 1 loop each)
%timeit group_pd([a,b,c])
# 21.7 s +/- 646 ms per loop (mean +/- std. dev. of 7 runs, 1 loop each)

最佳答案

在数组ab 上使用np.stack 后,如果将参数return_inverse 设置为np.unique 中的 True那么它就是您正在寻找的输出:

a = np.array([1,2,1,1,1,2,3,1])
b = np.array([1,2,2,2,2,3,3,2])
_, inv = np.unique(np.stack([a,b]), axis=1, return_inverse=True)
print (inv)

array([0, 2, 1, 1, 1, 3, 4, 1], dtype=int64)

并且您可以将 np.stack 中的 [a,b] 替换为所有向量的列表。

编辑:一个更快的解决方案是在数组的sum上使用np.unique乘以累积乘积(np .cumprod) 的 max 加上 groupcols 中所有先前数组的 1。比如:

def group_np_sum(groupcols):
    groupcols_max = np.cumprod([ar.max()+1 for ar in groupcols[:-1]])
    return np.unique( sum([groupcols[0]] +
                          [ ar*m for ar, m in zip(groupcols[1:],groupcols_max)]), 
                      return_inverse=True)[1]

检查:

a = np.array([1,2,1,1,1,2,3,1])
b = np.array([1,2,2,2,2,3,3,2])
print (group_np_sum([a,b]))
array([0, 2, 1, 1, 1, 3, 4, 1], dtype=int64)

注意:每个组关联的数字可能不一样(这里我把a的第一个元素改成了3)

a = np.array([3,2,1,1,1,2,3,1])
b = np.array([1,2,2,2,2,3,3,2])
print(group_np2([a,b]))
print (group_np_sum([a,b]))
array([3, 1, 0, 0, 0, 2, 4, 0], dtype=int64)
array([0, 2, 1, 1, 1, 3, 4, 1], dtype=int64)

但组本身是相同的。

现在检查时间:

a = np.random.randint(1, 100, 30000)
b = np.random.randint(1, 100, 30000)
c = np.random.randint(1, 100, 30000)
groupcols = [a,b,c]

%timeit group_pd(groupcols)
#13.7 ms ± 1.22 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)

%timeit group_np2(groupcols)
#34.2 ms ± 6.88 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

%timeit group_np_sum(groupcols)
#3.63 ms ± 562 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

关于python - Numpy 按多个向量分组,获取组索引,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53760898/

相关文章:

python - 康威的生命游戏邻居计数

numpy - 如何从 numpy 结构化数组中获取 dtypes 列表?

python - 使用 Numpy 代替循环

python - CX-卡住 "ImportError: DLL load failed: %1 is not a valid Win32 application"

python - 使用 QmediaPlayer 的 PyQt5 访问框架

javascript - Python有没有办法通过函数传递参数

python - PySerial:如何在串行线路上发送 Ctrl-C 命令

python - numpy数组将多列除以一列

python - pandas 到 numpy 数组用于 sklearn 管道

Python 字典 - 查找值等于的最小键