如何使用 pandas groupby 并应用 lambda 来评估布尔条件
How to use pandas groupby and apply lambda to evaluate a boolean condition
我一直在自学 python 使用股票数据,但我一直被这个问题困住。我正在尝试确定移动平均线交叉点。我在 pandas 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
检查前一天的符号变化来识别交叉。我已经测试了这种方法(没有 groupby)并且效果很好,只要发生交叉就会提供 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
函数,但除了对大型数据集花费更长的时间外,它似乎没有任何结果。
显然我的逻辑存在一些缺陷,如果您能帮助我解决问题,我将不胜感激。
我相信您需要参数 group_keys=False
来删除在输出中附加的新级别 - 然后对齐数据。另外 shift
return 每组第一个值 NaN
,因此 np.sign
发出警告:
RuntimeWarning: invalid value encountered in sign
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
我一直在自学 python 使用股票数据,但我一直被这个问题困住。我正在尝试确定移动平均线交叉点。我在 pandas 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
检查前一天的符号变化来识别交叉。我已经测试了这种方法(没有 groupby)并且效果很好,只要发生交叉就会提供 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
函数,但除了对大型数据集花费更长的时间外,它似乎没有任何结果。
显然我的逻辑存在一些缺陷,如果您能帮助我解决问题,我将不胜感激。
我相信您需要参数 group_keys=False
来删除在输出中附加的新级别 - 然后对齐数据。另外 shift
return 每组第一个值 NaN
,因此 np.sign
发出警告:
RuntimeWarning: invalid value encountered in sign 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