تجميع تراكمي لمجموعة numpy بواسطة الفهرس
سؤال
افترض أن لديك مجموعة من القيم التي ستحتاج إلى تلخيصها معًا
d = [1,1,1,1,1]
ومصفوفة ثانية تحدد العناصر التي يجب تلخيصها معًا
i = [0,0,1,2,2]
سيتم تخزين النتيجة في مجموعة جديدة من الحجم max(i)+1
. على سبيل المثال i=[0,0,0,0,0]
سيكون مكافئًا لجمع جميع عناصر d
وتخزين النتيجة في الموقف 0
من مجموعة جديدة من الحجم 1
.
حاولت تنفيذ هذا باستخدام
c = zeros(max(i)+1)
c[i] += d
ومع ذلك ، فإن +=
تضيف العملية كل عنصر مرة واحدة فقط ، وبالتالي إعطاء النتيجة غير المتوقعة لـ
[1,1,1]
بدلاً من
[2,1,2]
كيف يمكن للمرء تنفيذ هذا النوع من التجميع بشكل صحيح؟
المحلول
يجب أن يكون هذا الحل أكثر كفاءة في المصفوفات الكبيرة (يتكرر على قيم الفهرس المحتملة بدلاً من الإدخالات الفردية لـ i
):
import numpy as np
i = np.array([0,0,1,2,2])
d = np.array([0,1,2,3,4])
i_max = i.max()
c = np.empty(i_max+1)
for j in range(i_max+1):
c[j] = d[i==j].sum()
print c
[1. 2. 7.]
نصائح أخرى
إذا فهمت السؤال بشكل صحيح ، فهناك وظيفة سريعة لهذا (طالما أن صفيف البيانات هو 1D)
>>> i = np.array([0,0,1,2,2])
>>> d = np.array([0,1,2,3,4])
>>> np.bincount(i, weights=d)
array([ 1., 2., 7.])
NP.Bincount إرجاع مجموعة لجميع أعداد صحيحة (الحد الأقصى (I)) ، حتى لو كانت بعض التهم صفر
تعليق Juh_ هو الحل الأكثر كفاءة. هنا رمز العمل:
import numpy as np
import scipy.ndimage as ni
i = np.array([0,0,1,2,2])
d = np.array([0,1,2,3,4])
n_indices = i.max() + 1
print ni.sum(d, i, np.arange(n_indices))
def zeros(ilen):
r = []
for i in range(0,ilen):
r.append(0)
i_list = [0,0,1,2,2]
d = [1,1,1,1,1]
result = zeros(max(i_list)+1)
for index in i_list:
result[index]+=d[index]
print result
في الحالة العامة عندما ترغب في جمع التخطيطات الفرعية حسب الملصقات ، يمكنك استخدام الكود التالي
import numpy as np
from scipy.sparse import coo_matrix
def labeled_sum1(x, labels):
P = coo_matrix((np.ones(x.shape[0]), (labels, np.arange(len(labels)))))
res = P.dot(x.reshape((x.shape[0], np.prod(x.shape[1:]))))
return res.reshape((res.shape[0],) + x.shape[1:])
def labeled_sum2(x, labels):
res = np.empty((np.max(labels) + 1,) + x.shape[1:], x.dtype)
for i in np.ndindex(x.shape[1:]):
res[(...,)+i] = np.bincount(labels, x[(...,)+i])
return res
الطريقة الأولى تستخدم مضاعفة المصفوفة المتفرقة. والثاني هو تعميم إجابة user333700. كلتا الطريقتين لهما سرعة مماثلة:
x = np.random.randn(100000, 10, 10)
labels = np.random.randint(0, 1000, 100000)
%time res1 = labeled_sum1(x, labels)
%time res2 = labeled_sum2(x, labels)
np.all(res1 == res2)
انتاج:
Wall time: 73.2 ms
Wall time: 68.9 ms
True