Обратный счет в массиве numpy

Предположим, у меня есть ряд целочисленных значений, расположенных в таком массиве numpy.

nan = np.nan
arr = np.array([3, nan, nan, nan, 5, nan, nan, nan, nan, nan])

nan значения должны быть заполнены обратным счетом от первого ненулевого значения до нуля.

[3, 2, 1, 0, 5, 4, 3, 2, 1, 0]

Панды тоже работают?

cs95 27.05.2019 17:50

да абсолютно

Marco Fumagalli 27.05.2019 17:51
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
4
2
431
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

ИМО, самый простой способ панд сделать это — использовать groupby и cumcount с ascending=False:

s = pd.Series(np.cumsum(~np.isnan(arr)))
s.groupby(s).cumcount(ascending=False)

0    3
1    2
2    1
3    0
4    5
5    4
6    3
7    2
8    1
9    0
dtype: int64

@QuangHoang Извините, не уверен, что понимаю. Он помечает строки в каждой группе (убывающим) количеством.

cs95 27.05.2019 18:03

Извините, я, должно быть, замечтался. +1

Quang Hoang 27.05.2019 18:04
import pandas as pd
import numpy as np
import math

arr = pd.Series([3,np.nan,np.nan,np.nan,5,np.nan,np.nan,np.nan,np.nan,np.nan])

for i in range(len(arr)):
    # Check if each element is "NaN"
    if math.isnan(arr[i]):
        # If NaN then take the previous element and subtract 1
        arr[i] = arr[i-1]-1

# print the final array
print(arr)

Результат:

0    3.0
1    2.0
2    1.0
3    0.0
4    5.0
5    4.0
6    3.0
7    2.0
8    1.0
9    0.0
dtype: float64

1) вы не объяснили свой код (да, это очевидно для меня, но не для всех, кто его читает), и 2) использование циклов с numpy - нет-нет.

cs95 27.05.2019 18:04

Я новичок в numpy, почему циклы плохие?

trker 27.05.2019 18:09

Операции NumPy реализованы так, чтобы быть быстрее и более масштабируемыми, чем циклы. Процесс называется "векторизация" и с точки зрения производительности на несколько шагов выше циклов.

cs95 27.05.2019 18:15
Ответ принят как подходящий

Вот векторизованный с NumPy -

def backward_count(a):
    m = ~np.isnan(a)
    idx = np.flatnonzero(m)

    p = np.full(len(a), -1, dtype=a.dtype)
    p[idx[0]] = a[idx[0]]+idx[0]

    d = np.diff(idx)
    p[idx[1:]] = np.diff(a[m]) + d - 1
    out = p.cumsum()
    out[:idx[0]] = np.nan
    return out

Пример запуска с более общим случаем -

In [238]: a
Out[238]: array([nan,  3., nan,  5., nan, 10., nan, nan,  4., nan, nan])

In [239]: backward_count(a)
Out[239]: array([nan,  3.,  2.,  5.,  4., 10.,  9.,  8.,  4.,  3.,  2.])

Бенчмаркинг

Настройка с масштабированием данного образца на 10,000x -

In [240]: arr = np.array([3, nan, nan, nan, 5, nan, nan, nan, nan, nan])

In [241]: arr = np.tile(arr,10000)

# Pandas based one by @cs95
In [243]: %%timeit
     ...: s = pd.Series(np.cumsum(~np.isnan(arr)))
     ...: s.groupby(s).cumcount(ascending=False)
35.9 ms ± 258 µs per loop (mean ± std. dev. of 7 runs, 1 loop each)

In [245]: %timeit backward_count(arr)
3.04 ms ± 4.35 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Другие вопросы по теме