numpy.unique против коллекций. Вопрос о производительности - PullRequest
0 голосов
/ 24 октября 2018

Я пытаюсь вычислить количество вхождений пар значений.При выполнении следующего кода Numy-версия (pair_frequency2) более чем на 50% медленнее, чем версия, использующая collection.Counter (она ухудшается при увеличении количества точек).Может кто-нибудь объяснить, почему.

Возможно ли переписать код для достижения лучшей производительности?

Заранее спасибо.

import numpy as np
from collections import Counter

def pairs_frequency(x, y):
    counts = Counter(zip(x, y))
    res = np.array([[f, a, b] for ((a, b), f) in counts.items()])
    return res[:, 0], res[:, 1], res[:, 2]

def pairs_frequency2(x, y):
    unique, counts = np.unique(np.column_stack((x,y)), axis=0, return_counts=True)
    return counts, unique[:,0], unique[:,1]


x = np.random.randint(low=1, high=11, size=50000)
y = x + np.random.randint(1, 5, size=x.size)

%timeit pairs_frequency(x, y)

%timeit pairs_frequency2(x, y)

1 Ответ

0 голосов
/ 24 октября 2018

numpy.unique сортирует свой аргумент, поэтому его временная сложность равна O (n * log (n)).Похоже, класс Counter может быть O (n).

Если значения в ваших массивах являются неотрицательными целыми числами, которые не слишком велики, эта версия довольно быстрая:

def pairs_frequency3(x, y, maxval=15):
    z = maxval*x + y
    counts = np.bincount(z)
    pos = counts.nonzero()[0]
    ux, uy = np.divmod(pos, maxval)
    return counts[pos], ux, uy

Установите maxval на 1 плюс максимальное значение в x и y.(Вы можете удалить аргумент и добавить код, чтобы найти максимум в функции.)

Время (x и y были сгенерированы, как в вопросе):

In [13]: %timeit pairs_frequency(x, y)
13.8 ms ± 77.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

In [14]: %timeit pairs_frequency2(x, y)
32.9 ms ± 631 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [15]: %timeit pairs_frequency3(x, y)
129 µs ± 1.03 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

Обратите внимание на изменение в единицах времени третьего результата.

pairs_frequency3 возвращает массивы в том же порядке, что и pairs_frequency2, поэтому легко проверить, что они возвращают одинаковые значения:

In [26]: counts2, x2, y2 = pairs_frequency2(x, y)

In [27]: counts3, x3, y3 = pairs_frequency3(x, y)

In [28]: np.all(counts2 == counts3) and np.all(x2 == x3) and np.all(y2 == y3)
Out[28]: True
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...