在数据框架python上应用逐行条件函数



我有一个数据帧,我想在其中执行一个函数,检查实际值是否为相对最大值,并检查前"n"值是否低于实际值。

有一个数据框架'df_data':

temp_list = [128.71, 130.2242, 131.0, 131.45, 129.69, 130.17, 132.63, 131.63, 131.0499, 131.74, 133.6116, 134.74, 135.99, 138.789, 137.34, 133.46, 132.43, 134.405, 128.31, 129.1]
df_data = pd.DataFrame(temp)

首先,我创建一个函数来检查前面的条件:

def get_max(high, rolling_max, prev,post):
if ((high > prev) & (high>post) & (high>rolling_max)):
return 1
else: 
return 0
df_data['rolling_max'] = df_data.high.rolling(n).max().shift()

然后按行应用前一个条件:

df_data['ismax'] = df_data.apply(lambda x: get_max(df_data['high'], df_data['rolling_max'],df_data['high'].shift(1),df_data['high'].shift(-1)),axis = 1)

问题是我总是得到以下错误:

ValueError:一个Series的真值是不明确的。使用a.empty a.bool (), a.item (), a.any()或所有()。

这是由于将'get_max'函数中的布尔条件应用于Serie。

我希望有一个矢量化的函数,而不是使用循环。

尝试:

df_data['ismax'] = ((df_data['high'].gt(df_data.high.rolling(n).max().shift())) & (df_data['high'].gt(df_data['high'].shift(1))) & (df_data['high'].gt(df_data['high'].shift(-1)))).astype(int)

发生错误是因为您将整个系列(整个列)发送给get_max函数,而不是按行执行。为移动后的"prev"创建新列和";post"值,然后使用df.apply(func, axis = 1)通常会在这里工作得很好。

正如您所暗示的,这种解决方案效率非常低,并且随着数据帧大小的增加,循环遍历每一行将变得慢得多。

在我的电脑上,下面的代码发布:

  • LIST_MULTIPLIER = 1,向量化代码:0.29s,逐行代码:0.38s
  • LIST_MULTIPLIER = 100,向量化代码:0.31s,逐行代码= 13.27s

一般来说,因此最好避免使用df.apply(..., axis = 1),因为使用逻辑运算符几乎总是可以得到更好的解决方案。

import pandas as pd
from datetime import datetime
LIST_MULTIPLIER = 100
ITERATIONS = 100
def get_dataframe():
temp_list = [128.71, 130.2242, 131.0, 131.45, 129.69, 130.17, 132.63, 
131.63, 131.0499, 131.74, 133.6116, 134.74, 135.99, 
138.789, 137.34, 133.46, 132.43, 134.405, 128.31, 129.1] * LIST_MULTIPLIER
df = pd.DataFrame(temp_list)
df.columns = ['high']
return df
df_original = get_dataframe()
t1 = datetime.now()
for i in range(ITERATIONS):
df = df_original.copy()
df['rolling_max'] = df.high.rolling(2).max().shift()
df['high_prev'] = df['high'].shift(1)
df['high_post'] = df['high'].shift(-1)

mask_prev = df['high'] > df['high_prev']
mask_post = df['high'] > df['high_post']
mask_rolling = df['high'] > df['rolling_max']

mask_max = mask_prev & mask_post & mask_rolling

df['ismax'] = 0
df.loc[mask_max, 'ismax'] = 1


t2 = datetime.now()
print(f"{t2 - t1}")
df_first_method = df.copy()

t3 = datetime.now()
def get_max_rowwise(row):
if ((row.high > row.high_prev) & 
(row.high > row.high_post) & 
(row.high > row.rolling_max)):
return 1
else: 
return 0

for i in range(ITERATIONS):
df = df_original.copy()
df['rolling_max'] = df.high.rolling(2).max().shift()
df['high_prev'] = df['high'].shift(1)
df['high_post'] = df['high'].shift(-1)
df['ismax'] = df.apply(get_max_rowwise, axis = 1)
t4 = datetime.now()
print(f"{t4 - t3}")
df_second_method = df.copy()

最新更新