Примените функцию к массиву значений, используя уникальные элементы из другого

Допустим, у меня есть два массива с одинаковыми размерами: массив индексов idx и массив значений val:

import numpy as np

idx = np.array([[10, 10, 13],
                [13, 10, 18],
                [10, 16, 18]])

np.random.seed(42)
val = np.round(np.random.uniform(0, 100, 9).reshape((3, 3)), 1)

print(repr(val))
# array([[30.5,  9.8, 68.4],
#        [44. , 12.2, 49.5],
#        [ 3.4, 90.9, 25.9]])

Как я могу эффективно применить функцию, т. е. mean, к массиву значений, используя уникальный выбор из массива индексов?

Вот моя лучшая попытка перебора с for-циклом с ожидаемым результатом:

{i: val[idx == i].mean() for i in np.unique(idx)}
# {10: 13.975, 13: 56.2, 16: 90.9, 18: 37.7}

Хотя в этом примере это выглядит нормально, в реальном случае это занимает более 40 секунд с 6000 уникальными индексами в массиве с более чем 5 миллионами элементов. Это слишком долго, и я ищу более эффективный метод.


person Mike T    schedule 24.01.2019    source источник


Ответы (1)


Для больших массивов вы должны найти Pandas более эффективным, даже после учета затрат на настройку фрейма данных и преобразование серии в словарь:

import numpy as np, pandas as pd
# Python 3.7.0, Pandas 0.23.4, NumPy 1.15.1

np.random.seed(0)
n = 10**3
idx = np.random.randint(0, 20, (n, n))
val = np.random.random((n, n))

df = pd.DataFrame({'idx': idx.ravel(), 'val': val.ravel()})

%timeit pd.DataFrame({'idx': idx.ravel(), 'val': val.ravel()})  # 7.84 ms
%timeit df.groupby('idx')['val'].mean()                         # 34.6 ms
%timeit df.groupby('idx')['val'].mean().to_dict()               # 35.4 ms
%timeit {i: val[idx == i].mean() for i in np.unique(idx)}       # 108 ms
person jpp    schedule 24.01.2019
comment
В моем реальном случае методы groupby панды имеют 200-кратное повышение эффективности! - person Mike T; 25.01.2019