Счётчик в столбце по ненулевым данным с учётом значений в ещё одном столбце

Есть посуточные показания датчиков. Необходимо задать счётчик ненулевых значений по датчикам и по количеству показаний, т.е. от первого ненулевого значения до нуля - 1; от следующего ненулевого значения до нуля -2; при этом для нового датчика счётчик должен начаться снова с 1.

Пример фрейма данных (англ. DataFrame):


    import pandas as pd
    
    d={
      'Дата':['01.01.2020','02.01.2020','03.01.2020','04.01.2020','05.01.2020','01.01.2020','02.01.2020','03.01.2020','04.01.2020','05.01.2020'],
      'Датчик':[1,1,1,1,1,1,2,2,2,2],
      'Показания':[0,2,3,5,0,22,3,0,11,22]
    }
    df= pd.DataFrame(data=d)

Вид таблицы:

введите сюда описание изображения

Желаемый результат:

введите сюда описание изображения

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

Объединил оба примера в один и в результате получил нужный счётчик:

df = pd.DataFrame({
  'Дата':    ['01.01.2020','02.01.2020','03.01.2020','04.01.2020','05.01.2020','01.01.2020','02.01.2020','03.01.2020','04.01.2020','05.01.2020','06.02.2021','07.02.2021','08.02.2021','09.02.2021','10.02.2021','11.02.2021'],
  'Датчик': [1,1,1,1,1,1,2,2,2,2,3,3,3,3,3,3],
  'Показания':   [0,2,3,5,0,22,3,0,11,22,0,0,37,8,0,95] })

grp1=df[~df['Показания'].isin([0])] 
grp = grp1.groupby("Датчик")["Показания"]

first_idxs = grp.apply(lambda x: x.index[0])

df["счетчик"] = np.where(
    df["Показания"] != 0,
    df.groupby("Датчик")["Показания"].apply(lambda x:  ((x.shift().eq(0) & x.ne(0))| (x.ne(0)&(x.index.isin(first_idxs))) ).cumsum()),
    0
)
df
          Дата  Датчик  Показания  счетчик
0   01.01.2020       1          0        0
1   02.01.2020       1          2        1
2   03.01.2020       1          3        1
3   04.01.2020       1          5        1
4   05.01.2020       1          0        0
5   01.01.2020       1         22        2
6   02.01.2020       2          3        1
7   03.01.2020       2          0        0
8   04.01.2020       2         11        2
9   05.01.2020       2         22        2
10  06.02.2021       3          0        0
11  07.02.2021       3          0        0
12  08.02.2021       3         37        1
13  09.02.2021       3          8        1
14  10.02.2021       3          0        0
15  11.02.2021       3         95        2

Может кто-нибудь более подробно объяснить что происходит в строчке

grp = grp1.groupby("Датчик")["Показания"]

При группировке по "Датчик" параметру "Показания" присваивается индекс первого встречного значения?

В этой строчке:

df.groupby("Датчик")["Показания"].apply(lambda x:  ((x.shift().eq(0) & x.ne(0))| (x.ne(0)&(x.index.isin(first_idxs))) ).cumsum())

Функция lambda возвращает x если условие сработало и затем считается их количество? apply нужно чтобы применить функцию lambda к каждому датчику отдельно? интуитивно вроде понятно, но все же...


Ответы (2 шт):

Автор решения: MaxU

Если я правильно понял вопрос:

grp = df.groupby("Датчик")["Показания"]

first_idxs = grp.apply(lambda x: x.index[0])

df["счетчик"] = np.where(
    df["Показания"] != 0,
    grp.apply(lambda x: (x.index.isin(first_idxs) | x.eq(0)).cumsum()),
    0
)

результат:

In [71]: df
Out[71]:
         Дата  Датчик  Показания  счетчик
0  01.01.2020       1          0        0
1  02.01.2020       1          2        1
2  03.01.2020       1          3        1
3  04.01.2020       1          5        1
4  05.01.2020       1          0        0
5  01.01.2020       1         22        2
6  02.01.2020       2          3        1
7  03.01.2020       2          0        0
8  04.01.2020       2         11        2
9  05.01.2020       2         22        2
→ Ссылка
Автор решения: MaxU

Вариант ответа для измененных входных данных:

d = {
  'Дата':['01.01.2020','02.01.2020','03.01.2020','04.01.2020','05.01.2020','01.01.2020','02.01.2020','03.01.2020','04.01.2020','05.01.2020'], 
  'Датчик':[1,1,1,1,1,1,2,2,2,2],
  'Показания':[0,2,3,5,0,22,0,0,11,22]
}
df= pd.DataFrame(data=d)

решение:

df["счетчик"] = \
    df.groupby("Датчик")["Показания"].apply(lambda x: (x.shift().eq(0) & x.ne(0)).cumsum())

результат:

In [110]: df
Out[110]:
         Дата  Датчик  Показания  счетчик
0  01.01.2020       1          0        0
1  02.01.2020       1          2        1
2  03.01.2020       1          3        1
3  04.01.2020       1          5        1
4  05.01.2020       1          0        1
5  01.01.2020       1         22        2
6  02.01.2020       2          0        0
7  03.01.2020       2          0        0
8  04.01.2020       2         11        1
9  05.01.2020       2         22        1

df["счетчик"] = np.where(
    df["Показания"] != 0,
    df.groupby("Датчик")["Показания"].apply(lambda x:  (x.shift().eq(0) & x.ne(0)).cumsum()),
    0
)

In [156]: df
Out[156]:
          Дата  Датчик  Показания  счетчик
0   01.01.2020       1          0        0
1   02.01.2020       1          2        1
2   03.01.2020       1          3        1
3   04.01.2020       1          5        1
4   05.01.2020       1          0        0
5   01.01.2020       1         22        2
6   02.01.2020       2          3        0
7   03.01.2020       2          0        0
8   04.01.2020       2         11        1
9   05.01.2020       2         22        1
10  06.02.2021       3          0        0
11  07.02.2021       3          0        0
12  08.02.2021       3         37        1
13  09.02.2021       3          8        1
14  10.02.2021       3          0        0
15  11.02.2021       3         95        2
→ Ссылка