2016-09-27 3 views
0

Предположим у меня есть n Интервалы длины len на оси. Каждому из них присваивается значение в ndarray. Теперь я хотел бы посмотреть некоторые значения в позициях query_pos, заданных как числа с плавающей запятой. В настоящее время я планирую сделать это следующим образом:Быстрый поиск по индексу с плавающей запятой в массив

n = 100 
len = 0.39483 
data = ... # creates ndarray with data of length n = 100 
query_pos = ... # creates an ndarray with positions to query 
values_at_query_pos = data[(query_pos/len).astype(int)] # questionable line 

Это хороший способ сделать это, или есть более эффективные способы для преобразования позиции запроса с плавающей запятой в целочисленный индекс, а затем прочитать из массива ? Я особенно удивляюсь, что astype(int) - дешевая или дорогая операция, по сравнению, например, с делением или памятью.

еще несколько замечаний:

  • Наконец, он будет использоваться в 2-х и 3-х измерениях. В настоящее время я планирую позиции уловов, которые приведут к незаконным индексам до, они идут на этап поиска.

  • Массив data будет иметь достаточно высокое разрешение, так что я не должен нуждаться в любой фильтрации или интерполяции. Это будет сделано в предыдущих этапах .

ответ

4

Вместо деления каждого элемента query_pos этим скаляр, мы можем заранее рассчитать обратный скаляр и использовать умножение вместо этого для некоторого ускорения там. Интуиция заключается в том, что разделение является более дорогостоящим делом, чем умножением.

Вот быстрый тест во время выполнения на нем -

In [177]: # Setup inputs 
    ...: n = 100 
    ...: len1 = 0.39483 
    ...: data = np.random.rand(100) 
    ...: query_pos = np.random.randint(0,25,(100000)) 
    ...: 

In [178]: %timeit query_pos/len1 
1000 loops, best of 3: 612 µs per loop 

In [179]: %timeit query_pos * (1/len1) 
10000 loops, best of 3: 173 µs per loop 

Во-вторых, если есть много повторных индексов, так же, как в установке, используемой для теста выполнения, показанном выше, мы можем использовать np.take для некоторого дальнейшего незначительное улучшение , как показано на рисунок ниже -

In [196]: %timeit data[(query_pos *(1/len1)).astype(int)] 
1000 loops, best of 3: 538 µs per loop 

In [197]: %timeit np.take(data,(query_pos * (1/len1)).astype(int)) 
1000 loops, best of 3: 526 µs per loop 

Если вы планируете использовать его на общих ndarrays, мы должны были бы использовать axis параметры с np.take.

Сравнивая его с оригинальным подходом -

In [202]: %timeit data[(query_pos/len1).astype(int)] 
1000 loops, best of 3: 967 µs per loop 

Наконец, по вопросу о том, как операция деления складывает против преобразования в int, они кажутся сопоставимыми на большом наборе данных. Но, похоже, вы не можете избежать преобразования, необходимого для индексирования. Вот тест времени на нем -

In [210]: idx = query_pos * (1/len1) 

In [211]: %timeit query_pos * (1/len1) 
10000 loops, best of 3: 165 µs per loop 

In [212]: %timeit idx.astype(int) 
10000 loops, best of 3: 110 µs per loop 
1

Вы можете вывести результат деления прямо на int массива:

idx = np.empty_like(query_pos, int) 
np.divide(query_pos, len, out=idx, casting='unsafe') 

Это будет заметно быстрее только для больших массивов. Но этот код сложнее читать, поэтому только оптимизируйте, если это узкое место!

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