Как использовать Pandas Groupby и применять лямбда для оценки логического состояния
Я учил себя Python, используя биржевые данные, но я застрял в этом вопросе. Я пытаюсь определить пересечение скользящей средней. Я работаю с ежедневными данными в панде MultiIndex DataFrame. Ниже приведен фрагмент структуры данных, с которой я работаю.
import pandas as pd
import numpy as np
data = {'date': pd.Series(['2016-1-4', '2016-1-4', '2016-1-4',
'2016-1-5', '2016-1-5', '2016-1-5',
'2016-1-6', '2016-1-6', '2016-1-6']),
'ticker': pd.Series(['NYMX', 'EVAR', 'PMV',
'NYMX', 'EVAR', 'PMV',
'NYMX', 'EVAR', 'PMV']),
'twohundredsma': pd.Series([2.3, 3.58, 0.458,
2.31, 3.56, 0.459,
2.32, 3.55, 0.46]),
'fiveema': pd.Series([2.33, 1.31, 0.54,
2.33, 1.28, 0.54,
2.3, 1.25, 0.54])}
df = pd.DataFrame(data)
df['date'] = pd.to_datetime(df['date'])
df.set_index(['date', 'ticker'], inplace=True)
Пересечение можно определить, взяв разницу между двумя скользящими средними и используя shift
проверить на изменение знака с предыдущего дня. Я проверил этот подход (без группового), и он прекрасно работает, обеспечивая True
значение всякий раз, когда произошло пересечение.
Тем не менее, проблема у меня с использованием groupby
функция для применения этой функции к каждому биржевому тикеру. Мой первоначальный подход состоял в том, чтобы использовать apply
lambda
функция. Код ниже добавляет 2 новых столбца, но столбец "Five200bull" заполняется значениями "nan" без ошибок.
def five_cross(df):
df['fiveminus200'] = df['fiveema'] - df['twohundredsma']
df['five200bull'] = df.groupby(level='ticker').apply(lambda x:
np.sign(x['fiveminus200'])!=np.sign(x['fiveminus200'].shift(1)))
Поэтому я попробовал другой подход, при котором каждый тикер передавался в виде информационного кадра в отдельную функцию. Этот подход был намного медленнее при работе с большим фреймом данных, но он тоже не работал.
def add_five_bull(df):
df['five200bull'] = np.sign(df['fiveminus200']) != np.sign(df['fiveminus200'].shift(1))
def five_cross(df):
df['fiveminus200'] = df['fiveema'] - df['twohundredsma']
# group by ticker
grouped = df.groupby(level='ticker')
# pass each ticker in a df to function
for tick, group in grouped:
add_five_bull(group)
При таком подходе столбец "Five200bull" никогда не добавляется к df, и я получаю печально известную SettingWithCopyWarning
, Я пытался добавить df.loc[:, 'fiveminus200']
к add_five_bull
функционировать, но, кроме того, что с большим набором данных требуется гораздо больше времени, он, похоже, не дал никакого результата.
Очевидно, что в моей логике есть какой-то недостаток, и я был бы признателен за любую помощь в решении.
1 ответ
Я считаю, что вам нужен параметр group_keys=False
для удаления добавляем новый уровень в выводе - тогда данные выровнены. Также shift
вернуть первое значение NaN
на группу, так np.sign
поднять предупреждение:
RuntimeWarning: недопустимое значение, встречающееся в знаке np.sign(x['fiveminus200'])!= Np.sign(x['fiveminus200']. Shift(1)))
Решение заменить NaN
к некоторому значению, например False
или же True
от fillna
:
def five_cross(df):
df['fiveminus200'] = df['fiveema'] - df['twohundredsma']
df['five200bull'] = df.groupby(level='ticker', group_keys=False).apply(lambda x:
np.sign(x['fiveminus200'])!=np.sign(x['fiveminus200'].shift(1).fillna(False)))
return df
print (five_cross(df))
fiveema twohundredsma fiveminus200 five200bull
date ticker
2016-01-04 NYMX 2.33 2.300 0.030 True
EVAR 1.31 3.580 -2.270 True
PMV 0.54 0.458 0.082 True
2016-01-05 NYMX 2.33 2.310 0.020 False
EVAR 1.28 3.560 -2.280 False
PMV 0.54 0.459 0.081 False
2016-01-06 NYMX 2.30 2.320 -0.020 True
EVAR 1.25 3.550 -2.300 False
PMV 0.54 0.460 0.080 False
def five_cross(df):
df['fiveminus200'] = df['fiveema'] - df['twohundredsma']
df1 = df.groupby(level='ticker').apply(lambda x:
np.sign(x['fiveminus200'])!=np.sign(x['fiveminus200'].shift(1).fillna(False)))
return df1
print (five_cross(df))
ticker date ticker
EVAR 2016-01-04 EVAR True
2016-01-05 EVAR False
2016-01-06 EVAR False
NYMX 2016-01-04 NYMX True
2016-01-05 NYMX False
2016-01-06 NYMX True
PMV 2016-01-04 PMV True
2016-01-05 PMV False
2016-01-06 PMV False
Name: fiveminus200, dtype: bool