распараллелить зональные вычисления на массиве NumPy - PullRequest
2 голосов
/ 30 сентября 2019

Я пытаюсь вычислить режим для всех ячеек одной и той же зоны (одно и то же значение) в массиве numpy. Я приведу вам пример кода ниже. В этом примере последовательный подход работает хорошо, но многопроцессорный подход ничего не делает. Я не нахожу свою ошибку.

Кто-то видит мою ошибку?

Я хотел бы распараллелить вычисления, потому что мой реальный массив - это массив 10k * 10k с зонами 1M.

import numpy as np
import scipy.stats as ss
import multiprocessing as mp

def zone_mode(i, a, b, output):
    to_extract = np.where(a == i)
    val = b[to_extract]
    output[to_extract] = ss.mode(val)[0][0]
    return output

def zone_mode0(i, a, b):
    to_extract = np.where(a == i)
    val = b[to_extract]
    output = ss.mode(val)[0][0]
    return output

np.random.seed(1)

zone = np.array([[1, 1, 1, 2, 3],
                 [1, 1, 2, 2, 3],
                 [4, 2, 2, 3, 3],
                 [4, 4, 5, 5, 3],
                 [4, 6, 6, 5, 5],
                 [6, 6, 6, 5, 5]])
values = np.random.randint(8, size=zone.shape)

output = np.zeros_like(zone).astype(np.float)

for i in np.unique(zone):
    output = zone_mode(i, zone, values, output)

# for multiprocessing    
zone0 = zone - 1

pool = mp.Pool(mp.cpu_count() - 1)
results = [pool.apply(zone_mode0, args=(u, zone0, values)) for u in np.unique(zone0)]
pool.close()
output = results[zone0]

1 Ответ

1 голос
/ 30 сентября 2019

Для положительных целых чисел в массивах - zone и values мы можем использовать np.bincount. Основная идея заключается в том, что мы будем рассматривать zone и values как строки и столбцы на двумерной сетке. Таким образом, можно сопоставить их с их эквивалентными числами линейного индекса. Они будут использоваться в качестве бинов для суммированного бин с np.bincount. Их argmax ID будут номерами режимов. Они отображаются обратно в зона-сетка с индексацией в zone.

Следовательно, решение будет -

m = zone.max()+1
n = values.max()+1
ids = zone*n + values
c = np.bincount(ids.ravel(),minlength=m*n).reshape(-1,n).argmax(1)
out = c[zone]

Для разреженных данных (хорошо распределенные целые числа во входных массивах), мы можем заглянуть в разреженную матрицу, чтобы получить идентификаторы argmax c. Следовательно, с разреженной матрицей SciPy -

from scipy.sparse import coo_matrix

data = np.ones(zone.size,dtype=int)
r,c = zone.ravel(),values.ravel()
c = coo_matrix((data,(r,c))).argmax(1).A1

Для небольших перф. Boost, укажите форму -

c = coo_matrix((data,(r,c)),shape=(m,n)).argmax(1).A1

Решение для универсального values

Мы будем использовать pandas.factorize, вот так -

import pandas as pd

ids,unq = pd.factorize(values.flat)
v = ids.reshape(values.shape)
# .. same steps as earlier with bincount, using v in place of values
out = unq[c[zone]]

Обратите внимание, что для связующих случаев случайным элементом будет выбрано значение values. Если вы хотите выбрать первый, используйте pd.factorize(values.flat, sort=True).

...