[英]Pandas apply, rolling, groupby with multiple input & multiple output columns
I've been struggling the past week trying to use apply to use functions over an entire pandas dataframe, including rolling windows, groupby , and especially multiple input columns and multiple output columns. 我在 SO 上發現了大量關於這個主題的問題以及許多舊的和過時的答案。 因此,我開始為 x 輸入和輸出、滾動、滾動和 groupby 組合的每種可能組合創建一個筆記本,並且我也專注於性能。 由於我不是唯一一個在這些問題上苦苦掙扎的人,我想我會在這里提供我的解決方案和工作示例,希望它可以幫助任何現有/未來的熊貓用戶。
讓我們首先創建一個 dataframe,它將在下面的所有示例中使用,包括 groupby 示例的組列。 對於滾動 window 和多個輸入/輸出列,我在下面的所有代碼示例中只使用 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 解決方案(見上文注釋)添加到滾動 function。
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 解決方案(見上文注釋)添加到滾動 function。
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 點所解釋的,如果我們想在結合使用滾動和應用時返回超過 1 個值,我們需要一種解決方法。 我找到了 2 個可行的解決方案。
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 function 內完成。
缺點:您必須先創建列,而且速度較慢,因為它不使用原始輸入。
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
優點:它使用原始輸入,使其速度提高了兩倍。 而且由於它不使用索引來設置 output 值,因此代碼看起來更清晰(至少對我而言)。
缺點:您必須自己創建 nan 前綴,並且需要更多的代碼行。
滾動和分組
通常,我會使用上面更快的第二種解決方案。 但是,由於我們正在組合組並滾動這意味着您必須在數據集中間某處的正確索引處手動設置 NaN/零(取決於組的數量)。 在我看來,當結合滾動、groupby 和多個 output 列時,第一個解決方案更容易並自動解決自動 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 解決方案(見上文注釋)添加到滾動 function。
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)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.