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

У меня есть список значений с плавающей точкой, которые представляют время наблюдения. (Каждое значение с плавающей точкой на самом деле может быть представлено как целое число, но я надеюсь обобщить для возможных будущих обстоятельств).

list_hrs = [4,6,8,8,10] # actual list is thousands of floats

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

list_hrs = [0,0,0,0,4,0,6,0,8,8,0,10]

Первые четыре записи 0 потому что есть четыре числа из 0 в 3, 0 между 4 а также 6 разыскивается там, потому что 5 пропал, отсутствует; аналогично для 0 между 6 а также 8, 0 между 8 а также 10 разыскивается там, потому что значение 9 пропал, отсутствует. Также дубликат 8остаются нетронутыми, так как они будут рассмотрены позже в моем коде; только единственное вхождение дубликата 8должны быть посчитаны до заполнения 0"S.

Моей первой попыткой было попробовать это:

for index in range(len(list_hrs)):
    if list_hrs != index:
        list_hrs.insert(index, 0)

>> [0, 0, 0, 0, 0, 4, 6, 8, 8, 10]

Затем я прочитал различные сообщения SO и у меня сложилось впечатление, что лучше всего сначала составить список 0's, для которого длина должна быть равна количеству рассматриваемых точек данных. Тогда ненулевые записи могут заменить 0 записей. Итак, я попробовал следующее:

def make_zeros(hrs=list_hrs): # make list of 0's
    num_zer = int(max(hrs))
    list_zer = [0 for index in range(num_zer+1)]
    return list_zer

Но я не уверен, как реализовать условие для достижения желаемого результата после этого момента. Я думаю, что есть способ использовать enumerate проверить, соответствует ли индекс значению этого индекса, но я не уверен, как действовать из-за повторяющихся записей (таких как 8в примере выше).

Является ли этот метод хорошим направлением для дальнейшего развития или существует более эффективный / более простой способ достижения желаемого результата? Любая помощь или совет будут оценены.

2 ответа

Решение

Вот один векторизованный подход -

def make_zeros_vectorized(A, dtype=float):
    a = np.asarray(A).astype(int)
    idx = a + np.r_[0, (a[1:] == a[:-1]).cumsum()]
    out = np.zeros(idx[-1]+1,dtype=dtype)
    out[idx] = A
    return out

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

In [95]: A
Out[95]: [4.0, 6.0, 8.0, 8.0, 10.0, 10.0, 10.0, 14.0, 16.0]

In [96]: make_zeros_vectorized(A)
Out[96]: 
array([  0.,   0.,   0.,   0.,   4.,   0.,   6.,   0.,   8.,   8.,   0.,
        10.,  10.,  10.,   0.,   0.,   0.,  14.,   0.,  16.])

In [100]: A
Out[100]: [4.0, 4.0, 4.0, 4.0, 6.0, 8.0, 8.0, 10.0, 10.0, 10.0, 14.0, 16.0]

In [101]: make_zeros_vectorized(A)
Out[101]: 
array([  0.,   0.,   0.,   0.,   4.,   4.,   4.,   4.,   0.,   6.,   0.,
         8.,   8.,   0.,  10.,  10.,  10.,   0.,   0.,   0.,  14.,   0.,
        16.])

Вовлеченные шаги

Входной список

In [71]: A = [4.0,6.0,8.0,8.0,10.0,10.0,10.0,14.0,16.0]

Преобразовать в массив

In [72]: a = np.asarray(A).astype(int)

In [73]: a
Out[73]: array([ 4,  6,  8,  8, 10, 10, 10, 14, 16])

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

In [74]: a[1:] == a[:-1]
Out[74]: array([False, False,  True, False,  True,  True, False, False], dtype=bool)

In [75]: (a[1:] == a[:-1]).cumsum()
Out[75]: array([0, 0, 1, 1, 2, 3, 3, 3])

Добавьте ноль в начале, так как более раннее "a[1:] == a[:-1]" привело бы к массиву на один элемент меньше

In [76]: np.r_[0, (a[1:] == a[:-1]).cumsum()]
Out[76]: array([0, 0, 0, 1, 1, 2, 3, 3, 3])

Наконец, добавьте к входному массиву так, чтобы дубликаты были сдвинуты / добавлены на одну сторону вверх и, таким образом, дали нам индексы, которым должен быть назначен выходной массив

In [77]: a + np.r_[0, (a[1:] == a[:-1]).cumsum()]
Out[77]: array([ 4,  6,  8,  9, 11, 12, 13, 17, 19])

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


Если вам нужна маска нулей или этих индексов, вот модифицированная версия -

def get_zeros_mask(A):
    a = np.asarray(A).astype(int)
    idx = a + np.r_[0, (a[1:] == a[:-1]).cumsum()]
    mask = np.ones(idx[-1]+1,dtype=bool)
    mask[idx] = 0
    return mask

Пробный прогон -

In [93]: A
Out[93]: [4.0, 6.0, 8.0, 8.0, 10.0, 10.0, 10.0, 14.0, 16.0]

In [94]: make_zeros_vectorized(A)
Out[94]: 
array([  0.,   0.,   0.,   0.,   4.,   0.,   6.,   0.,   8.,   8.,   0.,
        10.,  10.,  10.,   0.,   0.,   0.,  14.,   0.,  16.])

In [95]: get_zeros_mask(A)
Out[95]: 
array([ True,  True,  True,  True, False,  True, False,  True, False,
       False,  True, False, False, False,  True,  True,  True, False,
        True, False], dtype=bool)

In [96]: np.flatnonzero(get_zeros_mask(A))
Out[96]: array([ 0,  1,  2,  3,  5,  7, 10, 14, 15, 16, 18])

Еще один пример:

list_hrs = [4,6,8,8,10]
lh = iter(list_hrs)
fit = range(int(max(list_hrs))+1)

result = [0 if i not in list_hrs else next(lh) for i in fit for _ in range(list_hrs.count(i)) or [1]]
Другие вопросы по тегам