Заполните массив 1D numpy массивами с индексами

Задний план

У меня есть один массив 1D NumPy, инициализированный нулями.

import numpy as np section = np.zeros(1000) 

Затем у меня есть Pandas DataFrame, где у меня есть индексы в двух столбцах:

 d= {'start': {0: 7200, 1: 7500, 2: 7560, 3: 8100, 4: 11400}, 'end': {0: 10800, 1: 8100, 2: 8100, 3: 8150, 4: 12000}} df = pd.DataFrame(data=d, columns=['start', 'end']) 

Для каждой пары индексов я хочу установить значение соответствующих индексов в массиве numpy равным True.

Мое текущее решение

Я могу сделать это, применив функцию к DataFrame:

 def fill_array(row): section[row.start:row.end] = True df.apply(fill_array, axis=1) 

Я хочу, чтобы векторизовать эту операцию

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

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

2 Solutions collect form web for “Заполните массив 1D numpy массивами с индексами”

Трюк для реализации заключается в том, что мы ставим 1s в каждой начальной точке и -1s в каждой конечной точке инициализированного массива int нулей. Фактический трюк следующий, так как мы кумулятивно суммируем его, предоставляя нам ненулевые числа для позиций, охватываемых границами бина (старт-стоп). Итак, последний шаг – искать не-нули для окончательного вывода в виде булевого массива. Таким образом, мы имели бы два векторизованных решения, с их реализациями, показанными ниже –

 def filled_array(start, end, length): out = np.zeros((length), dtype=int) np.add.at(out,start,1) np.add.at(out,end,-1) return out.cumsum()>0 def filled_array_v2(start, end, length): #Using @Daniel's suggestion out =np.bincount(start, minlength=length) - np.bincount(end, minlength=length) return out.cumsum().astype(bool) 

Пример прогона –

 In [2]: start Out[2]: array([ 4, 7, 5, 15]) In [3]: end Out[3]: array([12, 12, 7, 17]) In [4]: out = filled_array(start, end, length=20) In [7]: pd.DataFrame(out) # print as dataframe for easy verification Out[7]: 0 0 False 1 False 2 False 3 False 4 True 5 True 6 True 7 True 8 True 9 True 10 True 11 True 12 False 13 False 14 False 15 True 16 True 17 False 18 False 19 False 

Векторизация

Вы уже сделали самую важную векторизацию, используя назначение срезов, но вы не можете полностью векторизовать это, используя срезы, поскольку python не поддерживает «множественные фрагменты».

Если вы действительно хотите использовать векторию, вы можете создать массив с индексами «True», например

 indices = np.r_[tuple(slice(row.start, row.end) for row in df.itertuples())] section[indices] = True 

Но это, скорее всего, будет медленнее, поскольку создает новый временный массив с индексами.

Удаление дублирующей работы

С учетом сказанного вы можете получить некоторые ускорения, сократив дублирующую работу. В частности, вы можете взять объединение диапазонов , предоставляя вам набор непересекающихся множеств.

В вашем случае первый интервал перекрывает все, кроме последнего, поэтому ваш фреймворк эквивалентен

 d= {'start': {0: 7200, 1: 11400}, 'end': {0: 10800, 1: 12000}} 

Это уменьшает объем работы до 60%! Но сначала нам нужно найти эти интервалы. Следуя приведенному выше ответу, мы можем это сделать:

 slices = [(row.start, row.end) for row in df.itertuples()] slices_union = [] for start, end in sorted(slices): if slices_union and slices_union[-1][1] >= start - 1: slices_union[-1][1] = max(slices_union[-1][1], end) else: slices_union.append([start, end]) 

Тогда вы можете использовать эти (надеюсь, гораздо меньшие фрагменты), как это

 for start, end in slices_union: section[start:end] = True 
Python - лучший язык программирования в мире.