Не работает отбор значений по min

Выделяю минимальные значения каждого периода из столбца Input. Это 'min'.
Должно получатся минимальное число, но выходит 0.

Как сделать правильно?

from itertools import product
import xlrd

df = pd.read_csv('1111.csv')

def first(s):
    return s.loc[(s!=0).idxmax()] # с изменением на idxmin()] не работает

col = 'Input'

mask = (df[col].ne(0) & df[col].shift(-1).eq(0))    
tmp = df.groupby(mask.shift().fillna(0).cumsum())[col].agg([first, 'last', 'min'])

new_cols = ["<first>", "<last>", "<min>"]
df = df.assign(**dict(product(new_cols, [""])))

df.loc[mask, new_cols] = tmp.iloc[:mask.sum()].values
Пример данных:
    Input
7   0
8   0
9   0
10  0
11  0
12  0
13  0
14  0
15  0
16  22.65
17  22.65
18  22.66
19  22.65
20  22.65
21  22.65
22  22.66
23  22.65
24  22.65
25  22.65
26  22.64
27  22.64
28  22.65
29  0
30  0
31  0
32  0

Что должно быть:

    Input   <first> <last>  <min>
7   0           
8   0           
9   0           
10  0           
11  0           
12  0           
13  0           
14  0           
15  0           
16  22.65           
17  22.65           
18  22.66           
19  22.65           
20  22.65           
21  22.65           
22  22.66           
23  22.65           
24  22.65           
25  22.65           
26  22.64           
27  22.64           
28  22.65   22.65   22.65   22.64
29  0           
30  0           
31  0           
32  0           
33  0           
34  0           
35  0           
36  22.64   22.64   22.64   22.64
37  0           
38  0           
39  0           
40  0           

Файл данных.
Пример CSV - в нем уже есть first, last, min, но min пустой - т.к. этот код его не вычислил.


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

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

Ваша проблема в том, что ваша функция

def first(s):
    return s.loc[(s!=0).idxmax()] # с изменением на idxmin()] не работает

не работает правильно ни с .idxmax()!

Посмотрите:

  • (s!=0) — это серия из логижеских значений, т. е. True / False,
  • когда в этой серии будут присутствовать обе эти значения, то минимальным будет False и максимальным будет True,
  • в вашем случае она начинается с False, значит, (s!=0).idxmax() будет 0,
  • пусть она будет например False, False, True, True, True, False,
  • тогда (s!=0).idxmax() будет 2, другими словами, индекс первого ненулевого значения серии s, что вы вероятно не хотели.
→ Ссылка
Автор решения: MaxU

можно написать собственную функцию для нахождения минимума, которая будет игнорировать нулевые значения:

def mmin(ser):
    return ser.loc[ser != 0].min()

дальше пользуемся практически тем же кодом, что и у вас в вопросе:

mask = (df[col].ne(0) & df[col].shift(-1).eq(0))
tmp = df.groupby(mask.shift().fillna(0).cumsum())[col].agg([first, "last", mmin])
# NOTE: -------------------------------------------------------------->    ^^^^

new_cols = ["<first>", "<last>", "<min>"]
df = df.assign(**dict(product(new_cols, [""])))

df.loc[mask, new_cols] = tmp.iloc[:mask.sum()].values

результат:

In [39]: df
Out[39]:
    Input <first> <last>  <min>
7    0.00
8    0.00
9    0.00
10   0.00
11   0.00
12   0.00
13   0.00
14   0.00
15   0.00
16  22.65
17  22.65
18  22.66
19  22.65
20  22.65
21  22.65
22  22.66
23  22.65
24  22.65
25  22.65
26  22.64
27  22.64
28  22.65   22.65  22.65  22.64
29   0.00
30   0.00
31   0.00
32   0.00
→ Ссылка
Автор решения: MarianD
def applyfunc(df):
    funcs = ["first", "last", "min", "max"]
    n = len(df)
    s = n * [""]
    for func in funcs:
        colname = f"<{func}>"
        if func == "first":
            val = df.Input.iloc[0]
        elif func == "last":
            val = df.Input.iloc[-1]
        else:
            val = getattr(df.Input, func)()
        s[n-1] = val if val != 0 else "" 
        df[colname] = s
    return df   

key = df.Input.apply("sign").abs().diff().abs().fillna(0).cumsum()
df.groupby(key).apply(applyfunc)
→ Ссылка