Панды Python: примените функцию к dataframe.rolling()

У меня есть этот кадр данных:

In[1]df = pd.DataFrame([[1,2,3,4,5],[6,7,8,9,10],[11,12,13,14,15],[16,17,18,19,20],[21,22,23,24,25]])
In[2]df
Out[2]: 
    0   1   2   3   4
0   1   2   3   4   5
1   6   7   8   9  10
2  11  12  13  14  15
3  16  17  18  19  20
4  21  22  23  24  25

Мне нужно добиться этого:

  1. для каждой строки в моем кадре данных,
  2. если 2 или более значений в любых 3 последовательных ячейках больше 10,
  3. то последняя из этих 3 ячеек должна быть помечена как True.

Результирующий кадр данных df1 должен быть того же размера, что и True или False, в соответствии с указанными выше критериями:

In[3]df1
Out[3]: 
    0   1      2      3      4
0 NaN NaN  False  False  False
1 NaN NaN  False  False  False
2 NaN NaN   True   True   True
3 NaN NaN   True   True   True
4 NaN NaN   True   True   True
  • df1.iloc[0,1] — это NaN, потому что в этой ячейке были даны только два числа, но для выполнения теста требовалось как минимум 3 числа.
  • df1.iloc[1,3] имеет значение False, так как в [7,8,9] нет больше 10
  • df1.iloc[3,4] имеет значение True, поскольку 2 или более в [18,19,20] больше 10

Я подумал, что dataframe.rolling.apply() с функцией может быть решением, но как именно?


person Yi Fang    schedule 15.04.2018    source источник
comment
какой у Вас вопрос?   -  person penguin2048    schedule 15.04.2018
comment
@penguin2048 Penguin2048 Я отредактировал сообщение, мой вопрос в том, как добиться 1 2 3 4 в сообщении.   -  person Yi Fang    schedule 15.04.2018
comment
Добро пожаловать в StackOverflow. Пожалуйста, найдите время, чтобы прочитать этот пост о как предоставить отличный пример панд, а также как предоставить минимальный, полный и поддающийся проверке пример и соответствующим образом изменить свой вопрос. Эти советы о том, как правильно задать вопрос, также могут быть полезны.   -  person jezrael    schedule 15.04.2018
comment
пожалуйста, опубликуйте ожидаемый результат   -  person Vivek Kalyanarangan    schedule 15.04.2018
comment
@VivekKalyanarangan Я перефразировал вопрос.   -  person Yi Fang    schedule 15.04.2018


Ответы (3)


Вы правы, что лучше всего использовать rolling(). Однако вы должны иметь в виду, что rolling() заменяет значение в конце окна новым значением, поэтому вы не можете просто пометить окно True, вы также получите False всякий раз, когда условие не применимо.

Вот код, который использует ваш пример фрейма данных и выполняет желаемое преобразование:

df = pd.DataFrame([[1,2,3,4,5],[6,7,8,9,10],[11,12,13,14,15],[16,17,18,19,20],[21,22,23,24,25]])

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

def fun(x):
    num = 0
    for i in x:
        num += 1 if i > 10 else 0
    return 1 if num >= 2 else -1

Я жестко запрограммировал порог как 10. Поэтому, если в каком-либо окне количество значений больше 10 больше или равно 2, то последнее значение заменяется на 1 (обозначает True), в противном случае оно заменяется на -1 (обозначает False ).

Если вы хотите сохранить пороговые параметры как переменные, посмотрите этот ответ, чтобы передать их в качестве аргументов.

Теперь применяя функцию в скользящем окне, используя размер окна как 3, ось 1 и, кроме того, если вы не хотите NaN, вы также можете установить min_periods равным 1 в аргументах.

df.rolling(3, axis=1).apply(fun)

производит вывод как

  0   1    2    3    4
0 NaN NaN -1.0 -1.0 -1.0
1 NaN NaN -1.0 -1.0 -1.0
2 NaN NaN  1.0  1.0  1.0
3 NaN NaN  1.0  1.0  1.0
4 NaN NaN  1.0  1.0  1.0
person penguin2048    schedule 15.04.2018
comment
Спасибо за ваше объяснение, что, если мне нужно, чтобы threadhold (0 в вашем if i › 0) и больше 1 (1 в вашем if num › 1) в качестве аргумента функции, как мне переписать 'df.rolling (3, axis=1, min_periods=1).apply(fun)'? Функция в .apply принимает в качестве аргумента больше, чем я? - person Yi Fang; 15.04.2018
comment
stackoverflow.com/questions/12182744/ проверьте это - person penguin2048; 15.04.2018

Используйте sum для логического фрейма данных.

df.gt(10).rolling(3, axis=1).sum().ge(2)

       0      1      2      3      4
0  False  False  False  False  False
1  False  False  False  False  False
2  False  False   True   True   True
3  False  False   True   True   True
4  False  False   True   True   True

Вы можете указать точный запрошенный вывод, замаскировав, где na.

df.gt(10).rolling(3, axis=1).sum().pipe(lambda d: d.ge(2).mask(d.isna()))

    0   1      2      3      4
0 NaN NaN  False  False  False
1 NaN NaN  False  False  False
2 NaN NaN   True   True   True
3 NaN NaN   True   True   True
4 NaN NaN   True   True   True
person piRSquared    schedule 15.04.2018

Тебе нужно -

import pandas as pd
import numpy as np
df = pd.DataFrame([[1,2,3,4,5],[6,7,8,9,10],[11,12,13,14,15],[16,17,18,19,20],[21,22,23,24,25]])
df1 = df.apply(lambda x: pd.Series([np.nan, np.nan]+[all(j>10 for j in i) for i in zip(x[0::1], x[1::1], x[2::1])]), axis=1)

print(df1)

Вывод

0   1      2      3      4
0 NaN NaN  False  False  False
1 NaN NaN  False  False  False
2 NaN NaN   True   True   True
3 NaN NaN   True   True   True
4 NaN NaN   True   True   True

Пояснение

list(zip(x[0::1], x[1::1], x[2::1])

разбивает его на 3 столбца за раз для каждой строки -

0             [(1, 2, 3), (2, 3, 4), (3, 4, 5)]
1            [(6, 7, 8), (7, 8, 9), (8, 9, 10)]
2    [(11, 12, 13), (12, 13, 14), (13, 14, 15)]
3    [(16, 17, 18), (17, 18, 19), (18, 19, 20)]
4    [(21, 22, 23), (22, 23, 24), (23, 24, 25)]

all(j>10 for j in i)

Проверяет каждый элемент в списке кортежей, а затем выводит True, если все элементы в кортеже больше 10.

Объединение [np.nan, np.nan] для соответствия вашему выводу. Надеюсь, это поможет.

person Vivek Kalyanarangan    schedule 15.04.2018