添加像计数器这样的 Numpy 数组
Adding Numpy arrays like Counters
由于 collections.Counter 太慢了,我在 Python 2.7 中寻求一种更快的求和映射值的方法。这似乎是一个简单的概念,我对内置的 Counter 方法有点失望。
基本上,我需要能够采用这样的数组:
array([[ 0., 2.],
[ 2., 2.],
[ 3., 1.]])
array([[ 0., 3.],
[ 1., 1.],
[ 2., 5.]])
然后 "add" 他们看起来像这样:
array([[ 0., 5.],
[ 1., 1.],
[ 2., 7.],
[ 3., 1.]])
如果没有快速有效地执行此操作的好方法,我愿意接受任何其他允许我做类似事情的想法,并且我愿意接受 Numpy 以外的模块。
谢谢!
编辑:准备好进行一些速度测试了吗?
英特尔赢得 64 位机器。以下所有值均以秒为单位; 20000 个循环。
collections.Counter results:
2.131000, 2.125000, 2.125000
Divakar's union1d + masking results:
1.641000, 1.633000, 1.625000
Divakar's union1d + indexing results:
0.625000, 0.625000, 0.641000
Histogram results:
1.844000, 1.938000, 1.858000
Pandas results:
16.659000, 16.686000, 16.885000
结论:union1d + 索引获胜,数组大小太小以至于 Pandas 无效,直方图方法的简单性让我大吃一惊,但我猜创建它需要太多开销.不过,我收到的所有回复都非常好。 This is what I used to get the numbers.再次感谢!
编辑:应该提到的是,尽管做了同样的事情(65.671000 秒),但使用 Counter1.update(Counter2.elements())
还是很糟糕。
稍后编辑:我一直在思考这个问题,我开始意识到,使用 Numpy,用零填充每个数组可能更有效 所以甚至不需要第一列,因为我们可以只使用索引,这也将使将多个数组加在一起以及执行其他功能变得更加容易。此外,Pandas 比 Numpy 更有意义,因为不需要填充 0,而且对于大型数据集肯定会更有效(但是,Numpy 具有在更多平台上兼容的优势,例如 GAE ,如果这很重要的话)。最后,我检查的答案绝对是我提出的确切问题的最佳答案——按照我展示的方式添加两个数组——但我认为我需要的是改变视角。
这是 np.union1d
和 masking
-
的一种方法
def app1(a,b):
c0 = np.union1d(a[:,0],b[:,0])
out = np.zeros((len(c0),2))
out[:,0] = c0
mask1 = np.in1d(c0,a[:,0])
out[mask1,1] = a[:,1]
mask2 = np.in1d(c0,b[:,0])
out[mask2,1] += b[:,1]
return out
样本运行-
In [174]: a
Out[174]:
array([[ 0., 2.],
[ 12., 2.],
[ 23., 1.]])
In [175]: b
Out[175]:
array([[ 0., 3.],
[ 1., 1.],
[ 12., 5.]])
In [176]: app1(a,b)
Out[176]:
array([[ 0., 5.],
[ 1., 1.],
[ 12., 7.],
[ 23., 1.]])
这是另一个 np.union1d
和 indexing
-
def app2(a,b):
n = np.maximum(a[:,0].max(), b[:,0].max())+1
c0 = np.union1d(a[:,0],b[:,0])
out0 = np.zeros((int(n), 2))
out0[a[:,0].astype(int),1] = a[:,1]
out0[b[:,0].astype(int),1] += b[:,1]
out = out0[c0.astype(int)]
out[:,0] = c0
return out
对于a
和b
中的第一列值覆盖所有索引的情况-
def app2_specific(a,b):
c0 = np.union1d(a[:,0],b[:,0])
n = c0[-1]+1
out0 = np.zeros((int(n), 2))
out0[a[:,0].astype(int),1] = a[:,1]
out0[b[:,0].astype(int),1] += b[:,1]
out0[:,0] = c0
return out0
样本运行-
In [234]: a
Out[234]:
array([[ 0., 2.],
[ 2., 2.],
[ 3., 1.]])
In [235]: b
Out[235]:
array([[ 0., 3.],
[ 1., 1.],
[ 2., 5.]])
In [236]: app2_specific(a,b)
Out[236]:
array([[ 0., 5.],
[ 1., 1.],
[ 2., 7.],
[ 3., 1.]])
Pandas 有一些功能完全符合您的意图
import pandas as pd
pda = pd.DataFrame(a).set_index(0)
pdb = pd.DataFrame(b).set_index(0)
result = pd.concat([pda, pdb], axis=1).fillna(0).sum(axis=1)
编辑:如果您确实需要以 numpy 格式返回数据,只需执行
array_res = result.reset_index(name=1).values
如果知道字段数,请使用np.bincount
。
c = np.vstack([a, b])
counts = np.bincount(c[:, 0], weights = c[:, 1], minlength = numFields)
out = np.vstack([np.arange(numFields), counts]).T
如果您一次获取所有数据,此方法有效。列出您的数组并 vstack
它们。如果您按顺序获取数据块,则可以使用 np.add.at
来做同样的事情。
out = np.zeros(2, numFields)
out[:, 0] = np.arange(numFields)
np.add.at(out[:, 1], a[:, 0], a[:, 1])
np.add.at(out[:, 1], b[:, 0], b[:, 1])
这是一个典型的分组问题,numpy_indexed(免责声明:我是它的作者)是为了优雅高效地解决问题而创建的:
import numpy_indexed as npi
C = np.concatenate([A, B], axis=0)
labels, sums = npi.group_by(C[:, 0]).sum(C[:, 1])
注意:将标签数组维护为单独的 int 数组会更干净;浮点数在标记事物时很挑剔,带有正零和负零,并且打印的值不中继所有二进制状态。最好为此使用整数。
您可以使用基本直方图,。如果需要,您可以过滤掉零计数条目。
import numpy as np
x = np.array([[ 0., 2.],
[ 2., 2.],
[ 3., 1.]])
y = np.array([[ 0., 3.],
[ 1., 1.],
[ 2., 5.],
[ 5., 3.]])
c, w = np.vstack((x,y)).T
h, b = np.histogram(c, weights=w,
bins=np.arange(c.min(),c.max()+2))
r = np.vstack((b[:-1], h)).T
print(r)
# [[ 0. 5.]
# [ 1. 1.]
# [ 2. 7.]
# [ 3. 1.]
# [ 4. 0.]
# [ 5. 3.]]
r_nonzero = r[r[:,1]!=0]
由于 collections.Counter 太慢了,我在 Python 2.7 中寻求一种更快的求和映射值的方法。这似乎是一个简单的概念,我对内置的 Counter 方法有点失望。
基本上,我需要能够采用这样的数组:
array([[ 0., 2.],
[ 2., 2.],
[ 3., 1.]])
array([[ 0., 3.],
[ 1., 1.],
[ 2., 5.]])
然后 "add" 他们看起来像这样:
array([[ 0., 5.],
[ 1., 1.],
[ 2., 7.],
[ 3., 1.]])
如果没有快速有效地执行此操作的好方法,我愿意接受任何其他允许我做类似事情的想法,并且我愿意接受 Numpy 以外的模块。
谢谢!
编辑:准备好进行一些速度测试了吗? 英特尔赢得 64 位机器。以下所有值均以秒为单位; 20000 个循环。
collections.Counter results: 2.131000, 2.125000, 2.125000
Divakar's union1d + masking results: 1.641000, 1.633000, 1.625000
Divakar's union1d + indexing results: 0.625000, 0.625000, 0.641000
Histogram results: 1.844000, 1.938000, 1.858000
Pandas results: 16.659000, 16.686000, 16.885000
结论:union1d + 索引获胜,数组大小太小以至于 Pandas 无效,直方图方法的简单性让我大吃一惊,但我猜创建它需要太多开销.不过,我收到的所有回复都非常好。 This is what I used to get the numbers.再次感谢!
编辑:应该提到的是,尽管做了同样的事情(65.671000 秒),但使用 Counter1.update(Counter2.elements())
还是很糟糕。
稍后编辑:我一直在思考这个问题,我开始意识到,使用 Numpy,用零填充每个数组可能更有效 所以甚至不需要第一列,因为我们可以只使用索引,这也将使将多个数组加在一起以及执行其他功能变得更加容易。此外,Pandas 比 Numpy 更有意义,因为不需要填充 0,而且对于大型数据集肯定会更有效(但是,Numpy 具有在更多平台上兼容的优势,例如 GAE ,如果这很重要的话)。最后,我检查的答案绝对是我提出的确切问题的最佳答案——按照我展示的方式添加两个数组——但我认为我需要的是改变视角。
这是 np.union1d
和 masking
-
def app1(a,b):
c0 = np.union1d(a[:,0],b[:,0])
out = np.zeros((len(c0),2))
out[:,0] = c0
mask1 = np.in1d(c0,a[:,0])
out[mask1,1] = a[:,1]
mask2 = np.in1d(c0,b[:,0])
out[mask2,1] += b[:,1]
return out
样本运行-
In [174]: a
Out[174]:
array([[ 0., 2.],
[ 12., 2.],
[ 23., 1.]])
In [175]: b
Out[175]:
array([[ 0., 3.],
[ 1., 1.],
[ 12., 5.]])
In [176]: app1(a,b)
Out[176]:
array([[ 0., 5.],
[ 1., 1.],
[ 12., 7.],
[ 23., 1.]])
这是另一个 np.union1d
和 indexing
-
def app2(a,b):
n = np.maximum(a[:,0].max(), b[:,0].max())+1
c0 = np.union1d(a[:,0],b[:,0])
out0 = np.zeros((int(n), 2))
out0[a[:,0].astype(int),1] = a[:,1]
out0[b[:,0].astype(int),1] += b[:,1]
out = out0[c0.astype(int)]
out[:,0] = c0
return out
对于a
和b
中的第一列值覆盖所有索引的情况-
def app2_specific(a,b):
c0 = np.union1d(a[:,0],b[:,0])
n = c0[-1]+1
out0 = np.zeros((int(n), 2))
out0[a[:,0].astype(int),1] = a[:,1]
out0[b[:,0].astype(int),1] += b[:,1]
out0[:,0] = c0
return out0
样本运行-
In [234]: a
Out[234]:
array([[ 0., 2.],
[ 2., 2.],
[ 3., 1.]])
In [235]: b
Out[235]:
array([[ 0., 3.],
[ 1., 1.],
[ 2., 5.]])
In [236]: app2_specific(a,b)
Out[236]:
array([[ 0., 5.],
[ 1., 1.],
[ 2., 7.],
[ 3., 1.]])
Pandas 有一些功能完全符合您的意图
import pandas as pd
pda = pd.DataFrame(a).set_index(0)
pdb = pd.DataFrame(b).set_index(0)
result = pd.concat([pda, pdb], axis=1).fillna(0).sum(axis=1)
编辑:如果您确实需要以 numpy 格式返回数据,只需执行
array_res = result.reset_index(name=1).values
如果知道字段数,请使用np.bincount
。
c = np.vstack([a, b])
counts = np.bincount(c[:, 0], weights = c[:, 1], minlength = numFields)
out = np.vstack([np.arange(numFields), counts]).T
如果您一次获取所有数据,此方法有效。列出您的数组并 vstack
它们。如果您按顺序获取数据块,则可以使用 np.add.at
来做同样的事情。
out = np.zeros(2, numFields)
out[:, 0] = np.arange(numFields)
np.add.at(out[:, 1], a[:, 0], a[:, 1])
np.add.at(out[:, 1], b[:, 0], b[:, 1])
这是一个典型的分组问题,numpy_indexed(免责声明:我是它的作者)是为了优雅高效地解决问题而创建的:
import numpy_indexed as npi
C = np.concatenate([A, B], axis=0)
labels, sums = npi.group_by(C[:, 0]).sum(C[:, 1])
注意:将标签数组维护为单独的 int 数组会更干净;浮点数在标记事物时很挑剔,带有正零和负零,并且打印的值不中继所有二进制状态。最好为此使用整数。
您可以使用基本直方图,
import numpy as np
x = np.array([[ 0., 2.],
[ 2., 2.],
[ 3., 1.]])
y = np.array([[ 0., 3.],
[ 1., 1.],
[ 2., 5.],
[ 5., 3.]])
c, w = np.vstack((x,y)).T
h, b = np.histogram(c, weights=w,
bins=np.arange(c.min(),c.max()+2))
r = np.vstack((b[:-1], h)).T
print(r)
# [[ 0. 5.]
# [ 1. 1.]
# [ 2. 7.]
# [ 3. 1.]
# [ 4. 0.]
# [ 5. 3.]]
r_nonzero = r[r[:,1]!=0]