2016-08-19 1 views
3

Учитывая массив пороговых значений numpy, каков наиболее эффективный способ создания массива счетчиков другого массива, удовлетворяющего этим значениям?Быстрый подсчет элементов массива numpy по значениям порогов в другом массиве

Предположим, что массив порогового значения мал и отсортирован, а массив значений, подлежащих подсчету, является большим и является несортированным.

Пример: для каждого элемента valueLevels, подсчитывать элементы values больше или равна ей:

import numpy as np 

n = int(1e5) # size of example 

# example levels: the sequence 0, 1., 2.5, 5., 7.5, 10, 5, ... 50000, 75000 
valueLevels = np.concatenate(
        [np.array([0.]), 
        np.concatenate([ [ x*10**y for x in [1., 2.5, 5., 7.5] ] 
            for y in range(5) ]) 
        ] 
       ) 

np.random.seed(123) 
values = np.random.uniform(low=0, high=1e5, size=n) 

До сих пор я пытался список понимание подхода.

  • np.array([sum(values>=x) for x in valueLevels]) было неприемлемо медленно
  • np.array([len(values[values>=x]) for x in valueLevels]) было улучшение
  • сортировки values сделал ускорить понимание (в этом примере, от ~ 7 до 0,5 мс), но стоимость рода (~ 8 мс) превысил сбережения для одноразового использования

лучшее, что я сейчас это понимание this approach:

%%timeit 
np.array([np.count_nonzero(values>=x) for x in valueLevels]) 
# 1000 loops, best of 3: 1.26 ms per loop 

, который является приемлемым для моих целей, но из любопытства,

То, что я хотел бы знать, является

  • Если список понимание является путь, это может быть ускорено? Или,
  • Существуют ли другие подходы быстрее? (У меня есть смутное ощущение, что это можно сделать, передавая массив значений по массиву пороговых значений, но я не могу понять, как получить размеры для np.broadcast_arrays().

ответ

4

Самый быстрый я до сих пор

%timeit count_nonzero(values >= atleast_2d(valueLevels).T, axis=1) 
# 1000 loops, best of 3: 860 µs per loop 

sum медленнее:

%timeit sum(values >= atleast_2d(valueLevels).T, axis=1) 
# 100 loops, best of 3: 2.5 ms per loop 

@ версии Divakar является еще медленнее:

%timeit count_nonzero(values[:, None] >= valueLevels, axis=1) 
# 100 loops, best of 3: 3.86 ms per loop 

Однако, я бы, вероятно, по-прежнему использовать список вашего понимания, что не намного медленнее, и не создает большой 2D boolean array в качестве промежуточного этапа:

%timeit np.array([np.count_nonzero(values>=x) for x in valueLevels]) 
# 1000 loops, best of 3: 987 µs per loop 
+0

Хорошо знать, как сделать 2D-подход, +1. С точки зрения производительности, я тоже не вижу решающей разницы. Я приму это, если ничего не выйдет. Благодарю. – C8H10N4O2

+0

Yup, я добавил новую ось вдоль более длинного массива по ошибке, заплатив штраф. Хорошая работа с 'count_nonzero'! – Divakar

2

Подход № 1 Использование np.searchsorted -

values.size - np.searchsorted(values,valueLevels,sorter=values.argsort()) 

Подход № 2 Использование NumPy broadcasting -

(values[:,None]>=valueLevels).sum(0) 
+0

Подход №1 нуждается в целом 8,06 мс за цикл –

+0

Это помогает ответить на вопрос e что подход вещания не обязательно быстрее - +1 – C8H10N4O2

+0

@TimFuchs И сортировка по огромному массиву не помогла :) – Divakar

Смежные вопросы