Pandas groupby ewm - PullRequest
       34

Pandas groupby ewm

1 голос
/ 19 сентября 2019

Я пометил данные о событиях (временных рядах), где события происходят с произвольными интервалами для данной метки.Я хотел бы вычислить внутри группы ewma и добавить его к кадру данных в качестве нового столбца "X1_EWMA".Вот код на данный момент:

import pandas as pd
import numpy as np
import altair as alt

n = 1000
df = pd.DataFrame({
    'T': pd.date_range('20190101', periods=n, freq='H'),
    'C1': np.random.choice(list('PYTHON'), n),
    'C2': np.random.choice(list('FUN'), n),
    'X1': np.random.randn(n),
    'X2': 100 + 10 * np.random.randn(n)
})

ts = df.set_index('T')

display(df.head())
display(ts.head())

Благодаря SO: Pandas Groupby и методу применения с пользовательской функцией ) я могу вычислить сгруппированный EWMA с помощью:

ewm = ts.groupby(['C1']).apply(lambda x: x['X1'].ewm(halflife=10).mean())
ewm.head()

Создает серию, проиндексированную одной из категориальных переменных и датой-временем.Длина серии совпадает с исходным кадром данных и временным рядом (df и ts)

Теперь я думаю, что я мог бы сделать некоторую гимнастику, чтобы соединить это вместе с исходным кадром данных (df), присоединившись кИндекс строки (при условии, что порядок сортировки не изменился), но это не кажется правильным, и это может быть даже рискованным подходом, так как группировка была в пределах только одной из категориальных меток - мне нужно быть осторожным и сделать некоторые проверки/ сорт / повторное индексирование.

Похоже, что должен быть более простой способ добавить столбец временного ряда непосредственно в кадр данных (df) или временной ряд (ts), не создавая отдельные серии или кадры данных и не объединяя их.То же самое было бы верно, если бы я хотел добавить скользящую статистику, такую ​​как:

ts.groupby('C1').rolling(10).mean()

Заранее благодарен за любую помощь или входные данные.

Результаты, основанные на принятом ответе:

import pandas as pd
import numpy as np
import math
import altair as alt

alt.renderers.enable('notebook')      # for rendering in the notebook
alt.data_transformers.enable('json')  # for plotting data larger than 5000 points

# make a dataframe to test
n = 1000
df = pd.DataFrame({
    'T': pd.date_range('20190101', periods=n, freq='H'),
    'C1': np.random.choice(list('PYTHON'), n),
    'C2': np.random.choice(list('FUN'), n),
    'X1': np.linspace(0, 2*math.pi, n),
    'X2': np.random.randn(n),
})

# add a new variable that is a function of X1, X2 + a random outlier probability
df['X3'] = 0.2 * df['X2'] + np.sin(df['X1']) + np.random.choice(a=[0, 2], size=n, p=[0.98, 0.02])

# make it a time series for later resampling use cases.
ts = df.set_index('T')

#  SOLUTION: Add the ewma line with groupby().transform().
ts['ewm'] = ts.groupby(['C1'])['X3'].transform(lambda x: x.ewm(halflife=1).mean())

# plot the points and ewma using altair faceting and layering
points = alt.Chart().mark_circle(size=20, opacity=0.9).encode(
    x = 'T', 
    y = 'X3',
    color = 'C2',
).properties(width=270, height=170)

lines = alt.Chart().mark_line(size=1, color='red', opacity=1).encode(
    x = 'T', 
    y = 'ewm'
)

alt.layer(points, lines).facet(facet='C1', data=ts.reset_index()).properties(columns=3)

enter image description here

Ответы [ 2 ]

1 голос
/ 19 сентября 2019

Давайте исправим проблему, используя transform:

t['ewm'] = ts.groupby(['C1'])['X1'].transform(lambda x: x.ewm(halflife=10).mean()).values()
0 голосов
/ 19 сентября 2019

Можете ли вы попробовать это?НЕ устанавливайте ts = df.set_index('T').Тогда вы можете сделать, как показано ниже

ts['ewm']=ts.groupby(['C1'], sort=False).apply(lambda x: x['X1'].ewm(halflife=10).mean()).reset_index(drop=True)
...