Numpy间接索引

2024-09-30 08:38:07 发布

您现在位置:Python中文网/ 问答频道 /正文

我试图通过使用另一个包含索引的数组在一个或多个数组上执行聚合函数。这些索引可能包含需要根据聚合函数处理的重复项(我对实现“间接索引”的一般方法感兴趣,因此我希望不需要区分聚合函数)

例如,假设我们希望通过ix中的索引从v中的元素中获得一个和w

ix = [  0,   7,   0,   1,   7,   3,   0,   2,   2, 5, 6, 4]
v =  [100, 200, 300, 400, 500, 600, 700, 800, 900, 0, 0, 0]

=>

#       0                  1               2    3  4  5  6  7
w = [1100 (100+300+700), 400, 1700 (800+900), 600, 0, 0, 0, 700 (200+500)]

sum可能是一个简单的方法,但举例来说,加权平均法将更为复杂(在分解为w之前,将v1v2相乘)。是否有一种数组/numpy方法来执行此操作


Tags: 方法函数gtnumpy元素数组感兴趣v2
3条回答

您正在寻找一个groupby操作。Pandas有一个非常广泛的api来处理这类事情,并将numpy封装在引擎盖下,这样就可以实现矢量化(在某些操作中,速度与numpy一样快)。以下是一个例子:

import pandas as pd
ix = [  0,   7,   0,   1,   7,   3,   0,   2,   2, 5, 6, 4]
v =  [100, 200, 300, 400, 500, 600, 700, 800, 900, 0, 0, 0]

df = pd.DataFrame(zip(ix, v), columns=["idx", "v"])

# groupby the index, apply a sum function, convert type to numpy:
# array([1100,  400, 1700,  600,    0,    0,    0,  700])
w = df.groupby(df.idx).v.sum().to_numpy()

为了方便起见,您可以进行更复杂的计算并使用重载算术运算:

df["weights"] = np.random.rand(len(df))
df["weights"].mul(df["v"]).groupby("idx").sum()

通常表现为:

n = 1000000
df = pd.DataFrame({"idx": np.random.choice(10, n), "v": np.random.rand(n)})

%timeit df.groupby("idx")["v"].sum()
# 11.7 ms ± 214 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

作为多功能性的证明:您可以应用更多的奇异函数,例如每个组的调和平均值(apply稍微慢一点):

from scipy.stats.mstats import hmean
%timeit df.groupby("idx").apply(hmean)
# 51.3 ms ± 1.74 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
idx
0    0.083368
1    0.049457
2    0.077801
3    0.074263
4    0.065142
5    0.035001
6    0.080105
7    0.002465
8    0.076336
9    0.036461

或自定义函数:

def my_func(rows):
     return np.max(rows)/np.min(rows)

%timeit df.groupby("idx")["v"].apply(my_func)
# 46.6 ms ± 2.76 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
idx
0    8.265517e+04
1    8.900603e+05
2    1.874362e+05
3    1.419228e+05
4    4.722633e+05
5    1.382114e+06
6    1.000876e+05
7    3.939510e+07
8    7.747462e+04
9    8.919914e+05

试试这个:

[np.sum(v[ix == [x]]) for x in range(ix.max() + 1)]

结果:

[1100  400 1700  600    0    0    0  700]

&13; 第13部分,;
<script type="text/javascript" src="//cdn.datacamp.com/dcl-react.js.gz"></script>

<div data-datacamp-exercise data-lang="python">
  <code data-type="sample-code">
import numpy as np
ix = np.array([0, 7, 0, 1, 7, 3, 0, 2, 2, 5, 6, 4])
v = np.array([100, 200, 300, 400, 500, 600, 700, 800, 900, 0, 0, 0])

print([np.sum(v[ix == [x]]) for x in range(ix.max() + 1)])
  </code>
</div>
和#13;
和#13;

快速numpy方法:

In [107]: ix = np.array([  0,   7,   0,   1,   7,   3,   0,   2,   2, 5, 6, 4])
     ...: v =  np.array([100, 200, 300, 400, 500, 600, 700, 800, 900, 0, 0, 0])
In [108]: 
In [108]: np.bincount(ix,v)
Out[108]: array([1100.,  400., 1700.,  600.,    0.,    0.,    0.,  700.])

另一个,速度不太快,但可能更灵活(使用其他ufunc):

In [119]: a = np.zeros(8,int)
     ...: np.add.at(a, ix,v)
     ...: a
     ...: 
     ...: 
Out[119]: array([1100,  400, 1700,  600,    0,    0,    0,  700])

此小示例的计时:

In [121]: timeit [np.sum(v[ix == [x]]) for x in range(ix.max() + 1)]
159 µs ± 311 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)

In [122]: %%timeit
     ...: df = pd.DataFrame(zip(ix, v), columns=["idx", "v"])
     ...: w = df.groupby(df.idx).v.sum().to_numpy()
1.48 ms ± 884 ns per loop (mean ± std. dev. of 7 runs, 1000 loops each)

In [123]: timeit np.bincount(ix,v)
2.15 µs ± 6.79 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

In [124]: %%timeit
     ...: a = np.zeros(8,int)
     ...: np.add.at(a, ix,v)
     ...: a
9.4 µs ± 348 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

相关问题 更多 >

    热门问题