Как заменить выброс после группового? - PullRequest
4 голосов
/ 16 мая 2019

У меня есть датафрейм портфелей с номерами рисков.Я хочу сгруппировать по столбцу «Порт» в кадре данных ниже, а затем заменить значения в столбце «Риск», если он превышает квантиль 95% своей группы, на медиану этой группы портфеля

df =

    Date           Port       Risk
   2019-04-30        a         21.8
   2019-03-29        a         22.6
   2019-02-28        a         500
   2019-01-31        a         26.1
   2019-04-30        b         36.4
   2019-03-29        b         43.3
   2019-02-28        b         40
   2019-01-31        b         364

Я пробовал приведенный ниже код, который я нашел в stackoverflow, но он не работает.

def replace(group):
    q = group.quantile(0.95)
    outlier = group>q
    group[outlier] = group.median()
    return group

    df.groupby('Port').transform(replace)

Также пробовал

q = pd.DataFrame(df.groupby('Port')['Risk'].quantile(0.95))
df.loc[(((q.loc[df.Port,'Risk']<df['Risk'].values)))]=q.loc[df.Port,'Risk']

Ожидаемый результат - заменатретья запись порта "a" с медианой группы "a", которая составляет 22,2, и четвертая запись порта "b" с медианой группы "b", которая составляет 41,6

df =

    Date           Port       Risk
   2019-04-30        a         21.8
   2019-03-29        a         22.6
   2019-02-28        a         22.2
   2019-01-31        a         26.1
   2019-04-30        b         36.4
   2019-03-29        b         43.3
   2019-02-28        b         40
   2019-01-31        b         41.6

Ответы [ 3 ]

2 голосов
/ 16 мая 2019

Чтобы придерживаться кода, который вы отправили:

def replace(group):
    q = group.quantile(0.95)
    outlier = group>q
    group[outlier] = group.median()
    return group

df['Risk'] = (df.groupby('Port').transform(replace))
print(df)

вывод:

         Date Port   Risk
0  2019-04-30    a  21.80
1  2019-03-29    a  22.60
2  2019-02-28    a  24.35
3  2019-01-31    a  26.10
4  2019-04-30    b  36.40
5  2019-03-29    b  43.30
6  2019-02-28    b  40.00
7  2019-01-31    b  41.65
2 голосов
/ 16 мая 2019

Медианы, кажется, немного отличаются от того, что вы говорите (см. Комментарий в выходном фрейме данных). Вот один подход с использованием GroupBy.transform с where

g = df.groupby('Port').Risk
df['Risk'] = (df.Risk.where(g.transform('quantile', q=0.95) > df.Risk, 
                            g.transform('median')))

      Date     Port  Risk
0  2019-04-30    a  21.80
1  2019-03-29    a  22.60
2  2019-02-28    a  24.35 # -> np.median([21.8, 22.6, 500, 26.1]) = 24.35
3  2019-01-31    a  26.10
4  2019-04-30    b  36.40
5  2019-03-29    b  43.30
6  2019-02-28    b  40.00
7  2019-01-31    b  41.65
1 голос
/ 16 мая 2019

Вот способ сделать это:

df = pd.DataFrame({"Port" : ['a', 'a', 'a', 'a', 'b', 'b', 'b' ,'b'],
    "Risk" : [21.8, 22.6, 500, 26.1, 36.4,43.3,40,364]
})

for port in df['Port'].unique():
    mask_port = df['Port'] == port
    quantile_port = df[mask_port].quantile(0.95)
    median_port = df[mask_port].median()
    df.loc[(mask_port) & (df['Risk']>quantile_port.Risk), 'Risk'] = median_port.Risk

In [1] : print(df)
Out[1] :   Port   Risk
0    a  21.80
1    a  22.60
2    a  24.35
3    a  26.10
4    b  36.40
5    b  43.30
6    b  40.00
7    b  41.65
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...