Отображение местоположений, удовлетворяющих условию в массиве - PullRequest
0 голосов
/ 09 мая 2018

Учитывая логический массив (значения True / False) и диапазон индексов, которые могут не начинаться с 0, я хочу создать новый массив того же размера, что и этот диапазон, где каждый элемент содержит индекс ближайшего предшествующего True ,

Пример с логическим массивом, таким как:

[1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0]

и подмножество индексов:
[0, 1, 2, 3, 4, 5, 6, 7] (здесь, начинается с 0, но, возможно, не обязательно)

результат будет:

[0, 0, 0, 3, 4, 5, 5, 7]

У меня есть рабочее решение (ниже), но я ищу альтернативы, более быстрые и / или более элегантные и / или более удобочитаемые, учитывая, что размер массивов может варьироваться от тысяч до миллионов.

import numpy as np

def map_nearest_preceding_true_indices(tmask, irange):

    true_indices = np.where(tmask)[0]
    mapped_indices = np.empty(len(irange), dtype=np.int)

    for i, index in enumerate(irange):
        index_loc = np.where(true_indices <= index)[0][-1]
        mapped_indices[i] = true_indices[index_loc]

    return mapped_indices

Ответы [ 2 ]

0 голосов
/ 09 мая 2018

Вот векторизованное решение с np.searchsorted -

def map_locations(tmask, irange, invalid_index=-1):
    idx = np.where(tmask)[0]
    sidx = np.searchsorted(idx, irange, 'right')-1
    return np.where(sidx==-1,invalid_index, idx[sidx])

Образцы прогонов -

In [124]: # Considering a more generic case
     ...: tmask = np.array([1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0], dtype=bool)
     ...: irange = np.array([4, 8, 11, 18])

In [125]: map_locations(tmask, irange, invalid_index=-1)
Out[125]: array([4, 7, 9, 9])

In [129]: # Original case with first mask element being false
     ...: tmask = np.array([0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0], dtype=bool).tolist()
     ...: irange = np.array([0, 1, 2, 3, 4, 5, 6, 17]).tolist()

In [130]: map_locations(tmask, irange, invalid_index=-1)
Out[130]: array([-1, -1, -1,  3,  4,  5,  5,  9])
0 голосов
/ 09 мая 2018

Имейте понимание списка с одной линией:

data = [1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0]
indices = [0, 1, 2, 3, 4, 5, 6, 7]

nearest_true = [next(v for v in range(ind, -1, -1) if data[v]) for ind in indices]

Для каждого индекса в массиве индексов next пересекает массив данных в обратном направлении, начиная с этого индекса, и возвращает первый индекс для массива данных, который содержит истинное значение.

Однако, как обсуждалось в комментариях, этот код не работал бы, если бы у какого-либо индекса не было хотя бы одного истинного значения в нем или позади него. Мы можем решить эту проблему, предоставив значение по умолчанию для next(), передав его в качестве второго параметра, в этом случае нам придется заключить генератор в скобки, поскольку он больше не является единственным аргументом:

NOT_FOUND = -1
nearest_true = [next((v for v in range(ind, -1, -1) if data[v]), NOT_FOUND) 
                for ind in indices]

Тогда вывод для data = [0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0] будет [-1, -1, -1, 3, 4, 5, 5, 7]

...