Приложете функция върху масив от стойности, като използвате уникални елементи от друг

Да кажем, че имам два масива с еднакви размери: индексен масив 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 на panda имат 200 пъти подобрение на ефективността! - person Mike T; 25.01.2019