过去一周,我一直在努力尝试使用应用在整个熊猫数据框中使用功能,包括滚动窗口, groupby ,尤其是多个输入列和多个输出列。我在SO上发现了大量有关此主题的问题,以及许多过时和过时的答案。因此,我开始为x输入和输出,滚动,滚动和分组方式的每种可能组合创建一个笔记本,并且我也专注于性能。由于我不是唯一一个在这些问题上苦苦挣扎的人,因此我想在这里为我的解决方案提供一些可行的示例,希望它能帮助任何现有/未来的熊猫用户。
答案 0 :(得分:3)
首先创建一个数据框,该数据框将在以下所有示例中使用,其中包括groupby示例的组列。 对于滚动窗口和多个输入/输出列,我在下面的所有代码示例中仅使用2,但是显然,它可以是任何数字> 1。
df = pd.DataFrame(np.random.randint(0,5,size=(6, 2)), columns=list('ab'))
df['group'] = [0, 0, 0, 1, 1, 1]
df = df[['group', 'a', 'b']]
它看起来像这样:
group a b
0 0 2 2
1 0 4 1
2 0 0 4
3 1 0 2
4 1 3 2
5 1 3 0
基本
def func_i1_o1(x):
return x+1
df['c'] = df['b'].apply(func_i1_o1)
滚动
def func_i1_o1_rolling(x):
return (x[0] + x[1])
df['d'] = df['c'].rolling(2).apply(func_i1_o1_rolling, raw=True)
滚动和分组方式
将reset_index解决方案(请参见上面的注释)添加到滚动功能。
df['e'] = df.groupby('group')['c'].rolling(2).apply(func_i1_o1_rolling, raw=True).reset_index(drop=True)
基本
def func_i2_o1(x):
return np.sum(x)
df['f'] = df[['b', 'c']].apply(func_i2_o1, axis=1, raw=True)
滚动
如以上注释中第2点所述,对于2个输入,没有“正常”解决方案。以下解决方法使用“ raw = False”来确保输入为pd.Series,这意味着我们还可以在值旁边获取索引。这使我们能够从其他列获取要使用的正确索引处的值。
def func_i2_o1_rolling(x):
values_b = x
values_c = df.loc[x.index, 'c'].to_numpy()
return np.sum(values_b) + np.sum(values_c)
df['g'] = df['b'].rolling(2).apply(func_i2_o1_rolling, raw=False)
滚动和分组方式
将reset_index解决方案(请参见上面的注释)添加到滚动功能。
df['h'] = df.groupby('group')['b'].rolling(2).apply(func_i2_o1_rolling, raw=False).reset_index(drop=True)
基本
您可以通过返回pd.Series使用“正常”解决方案:
def func_i1_o2(x):
return pd.Series((x+1, x+2))
df[['i', 'j']] = df['b'].apply(func_i1_o2)
或者您可以使用大约8倍的zip / tuple组合!
def func_i1_o2_fast(x):
return x+1, x+2
df['k'], df['l'] = zip(*df['b'].apply(func_i1_o2_fast))
滚动
如以上注释中第1点所述,如果要结合使用 rolling&apply 来返回多个值,则需要一种解决方法。我找到了两种可行的解决方案。
1
def func_i1_o2_rolling_solution1(x):
output_1 = np.max(x)
output_2 = np.min(x)
# Last index is where to place the final values: x.index[-1]
df.at[x.index[-1], ['m', 'n']] = output_1, output_2
return 0
df['m'], df['n'] = (np.nan, np.nan)
df['b'].rolling(2).apply(func_i1_o2_rolling_solution1, raw=False)
优点:一切都在1个功能内完成。
缺点:您必须先创建列,并且由于它不使用原始输入,因此速度较慢。
2
rolling_w = 2
nan_prefix = (rolling_w - 1) * [np.nan]
output_list_1 = nan_prefix.copy()
output_list_2 = nan_prefix.copy()
def func_i1_o2_rolling_solution2(x):
output_list_1.append(np.max(x))
output_list_2.append(np.min(x))
return 0
df['b'].rolling(rolling_w).apply(func_i1_o2_rolling_solution2, raw=True)
df['o'] = output_list_1
df['p'] = output_list_2
优点:它使用原始输入,使其输入速度快大约两倍。而且由于它不使用索引来设置输出值,因此代码看起来更加清晰(至少对我而言)。
缺点:您必须自己创建nan-prefix,这需要花费更多的代码行。
滚动和分组方式
通常,我会使用上面更快的第二个解决方案。但是,由于我们要合并组并滚动,这意味着您必须在数据集中间某个位置的正确索引处手动设置NaN /零(取决于组数)。在我看来,当结合使用滚动,分组和多个输出列时,第一个解决方案更加容易,并且可以自动解决自动NaN /分组问题。再一次,我最后使用了reset_index解决方案。
def func_i1_o2_rolling_groupby(x):
output_1 = np.max(x)
output_2 = np.min(x)
# Last index is where to place the final values: x.index[-1]
df.at[x.index[-1], ['q', 'r']] = output_1, output_2
return 0
df['q'], df['r'] = (np.nan, np.nan)
df.groupby('group')['b'].rolling(2).apply(func_i1_o2_rolling_groupby, raw=False).reset_index(drop=True)
基本
我建议使用与i1_o2相同的“快速”方式,唯一的区别是您要使用2个输入值。
def func_i2_o2(x):
return np.mean(x), np.median(x)
df['s'], df['t'] = zip(*df[['b', 'c']].apply(func_i2_o2, axis=1))
滚动
由于我使用了一种解决方法来对多个输入进行滚动,而我使用了另一种解决方法来对多个输入进行滚动,因此我猜想我需要将它们结合起来。
1.使用索引从其他列获取值(请参见func_i2_o1_rolling)
2.将最终的多个输出设置在正确的索引上(请参见func_i1_o2_rolling_solution1)
def func_i2_o2_rolling(x):
values_b = x.to_numpy()
values_c = df.loc[x.index, 'c'].to_numpy()
output_1 = np.min([np.sum(values_b), np.sum(values_c)])
output_2 = np.max([np.sum(values_b), np.sum(values_c)])
# Last index is where to place the final values: x.index[-1]
df.at[x.index[-1], ['u', 'v']] = output_1, output_2
return 0
df['u'], df['v'] = (np.nan, np.nan)
df['b'].rolling(2).apply(func_i2_o2_rolling, raw=False)
滚动和分组方式
将reset_index解决方案(请参见上面的注释)添加到滚动功能。
def func_i2_o2_rolling_groupby(x):
values_b = x.to_numpy()
values_c = df.loc[x.index, 'c'].to_numpy()
output_1 = np.min([np.sum(values_b), np.sum(values_c)])
output_2 = np.max([np.sum(values_b), np.sum(values_c)])
# Last index is where to place the final values: x.index[-1]
df.at[x.index[-1], ['w', 'x']] = output_1, output_2
return 0
df['w'], df['x'] = (np.nan, np.nan)
df.groupby('group')['b'].rolling(2).apply(func_i2_o2_rolling_groupby, raw=False).reset_index(drop=True)