Pandas dataframe: проверьте, монотонно ли данные

У меня есть pandas dataframe:

    Balance       Jan       Feb       Mar       Apr
0  9.724135  0.389376  0.464451  0.229964  0.691504
1  1.114782  0.838406  0.679096  0.185135  0.143883
2  7.613946  0.960876  0.220274  0.788265  0.606402
3  0.144517  0.800086  0.287874  0.223539  0.206002
4  1.332838  0.430812  0.939402  0.045262  0.388466

Я хотел бы сгруппировать строки, выясняя, монотонно ли уменьшаются значения от Jan до до Apr (как в строках с индексом 1 и 3) или нет, а затем складывают остатки для каждой группы, то есть в конце Я хотел бы получить два числа (1.259299 для уменьшения временных рядов и 18.670919 для остальных).

Я думаю, что если бы я мог добавить столбец "убывает", то конвейер булевых я мог бы делать суммы с помощью pandas 'groupby, но как бы создать этот столбец?

Спасибо, Энн

Ответ 1

Вы можете использовать одну из функций is_monotonic из альгос:

In [10]: months = ['Jan', 'Feb', 'Mar', 'Apr']

In [11]: df.loc[:, months].apply(lambda x: pd.algos.is_monotonic_float64(-x)[0],
                                       axis=1)
Out[11]:
0    False
1     True
2    False
3     True
4    False
dtype: bool

is_monotonic проверяет, уменьшает ли массив его значение -x.values.

(Это кажется значительно быстрее, чем решение Tom, даже при использовании небольшого DataFrame.)

Ответ 2

months = ['Jan', 'Feb', 'Mar', 'Apr']

Транспонирование, чтобы мы могли использовать метод diff (который не принимает аргумент оси). Мы заполняем первую строку (январь) на 0. В противном случае это NaN.

In [77]: df[months].T.diff().fillna(0) <= 0
Out[77]: 
         0     1      2     3      4
Jan   True  True   True  True   True
Feb  False  True   True  True  False
Mar   True  True  False  True   True
Apr  False  True   True  True  False

Чтобы проверить, монотонно ли оно уменьшается, используйте метод .all(). По умолчанию это проходит по оси 0, строки (месяцы).

In [78]: is_decreasing = (df[months].T.diff().fillna(0) <= 0).all()

In [79]: is_decreasing
Out[79]: 
0    False
1     True
2    False
3     True
4    False
dtype: bool

In [80]: df['is_decreasing'] = is_decreasing

In [81]: df
Out[81]: 
    Balance       Jan       Feb       Mar       Apr is_decreasing
0  9.724135  0.389376  0.464451  0.229964  0.691504         False
1  1.114782  0.838406  0.679096  0.185135  0.143883          True
2  7.613946  0.960876  0.220274  0.788265  0.606402         False
3  0.144517  0.800086  0.287874  0.223539  0.206002          True
4  1.332838  0.430812  0.939402  0.045262  0.388466         False

И, как вы сказали, мы можем группировать is_decreasing и sum:

In [83]: df.groupby('is_decreasing')['Balance'].sum()
Out[83]: 
is_decreasing
False            18.670919
True              1.259299
Name: Balance, dtype: float64

Это времена, когда я люблю pandas.

Ответ 3

Pandas 0.19 добавил общедоступный Series.is_monotonic API (как уже упоминалось, модуль algos недокументирован и не гарантируется.)

Есть также is_monotonic_increasing и is_monotonic_decreasing. Все 3 не являются строгими (т.е. is_monotonic_decreasing проверяют, уменьшается или равномерна последовательность), но вы можете комбинировать их с is_unqiue если вам нужна строгость.

my_df = pd.DataFrame({'A':[1,2,3],'B':[1,1,1],'C':[3,2,1]})
my_df
Out[32]: 
   A  B  C
0  1  1  3
1  2  1  2
2  3  1  1

my_df.apply(lambda x: x.is_monotonic)
Out[33]: 
A     True
B     True
C    False
dtype: bool