应用 returns 单个系列的组特定函数
Applying group-specific function that returns a single series
我正在尝试为以下场景找出一个有效的 split/apply/combine 方案。考虑下面定义的 pandas 数据框 demoAll
:
import datetime
import pandas as pd
demoA = pd.DataFrame({'date':[datetime.date(2010,1,1), datetime.date(2010,1,2), datetime.date(2010,1,3)],
'ticker':['A', 'A', 'A'],
'x1':[10,20,30],
'close':[120, 133, 129]}).set_index('date', drop=True)
demoB = pd.DataFrame({'date':[datetime.date(2010,1,1), datetime.date(2010,1,2), datetime.date(2010,1,3)],
'ticker':['B', 'B', 'B'],
'x1':[18,11,45],
'close':[50, 49, 51]}).set_index('date', drop=True)
demoAll = pd.concat([demoA, demoB])
print(demoAll)
结果是:
ticker x1 close
date
2010-01-01 A 10 120
2010-01-02 A 20 133
2010-01-03 A 30 129
2010-01-01 B 18 50
2010-01-02 B 11 49
2010-01-03 B 45 51
我还有一个代码到模型对象的字典映射
ticker2model = {'A':model_A, 'B':model_B,...}
其中每个模型都有一个 self.predict(df)
方法接收整个数据帧和 returns 一系列相同长度的数据。
我现在想创建一个与这些预测相对应的新列 demoAll['predictions']
。 cleanest/most-efficient 这样做的方法是什么?需要注意的几点:
demoAll
是代码特定数据帧的串联,每个数据帧仅按日期索引。因此 demoAll
的索引不是唯一的。 (但是,date/ticker 的组合是唯一的。)
我的想法是做类似下面示例的事情,但是 运行 遇到了索引、数据类型强制转换和慢 运行 时间等问题。真实数据集相当大(包括行和列)。
demoAll['predictions'] = demoAll.groupby('ticker').apply(
lambda x: ticker2model[x.name].predict(x)
)
我可能误解了您通过模型进行预测的内容,但如果我理解正确,我会执行以下操作:
- 预分配
demoAll
的 predictions
列
- 遍历代码和过滤器的唯一值
demoAll
- 过滤掉代码行
- 使用过滤后的 df 预测结果
- 将结果保存在
demoAll['predictions']
中的正确位置
使用您的代码的示例:
# get non 'ticker' columns
non_ticker_cols = [col for col in demoAll.columns if col is not 'ticker']
# get unique set of tickers
tickers = demoAll.ticker.unique()
# create and prepopulate the predictions column
demoAll['predictions'] = 0
for ticker in tickers:
# get boolean Series to filter the Dataframes by.
filter_by_ticker = demoAll.ticker == ticker
# filter, predict and allocate
demoAll.loc[filter_by_ticker, 'predictions'] = ticker2model[
ticker].predict(
demoAll.loc[filter_by_ticker,
non_ticker_cols]
)
输出如下:
ticker x1 close predictions
date
2010-01-01 A 10 120 10.0
2010-01-02 A 20 133 10.0
2010-01-03 A 30 129 10.0
2010-01-01 B 18 50 100.0
2010-01-02 B 11 49 100.0
2010-01-03 B 45 51 100.0
与使用应用的比较
我们可以按行使用应用程序,但正如您提到的,它会变慢。我将比较两者以了解加速。
设置
我将使用 sklearn
中的 DummyRegressor
来允许我调用 predict
方法并创建您在问题中提到的字典。
model_a = DummyRegressor(strategy='mean')
model_b = DummyRegressor(strategy='median')
model_a.fit([[10,14]], y=np.array([10]))
model_b.fit([[200,200]], [100])
ticker2model = {'A':model_a, 'B':model_b}
将两者定义为函数
def predict_by_ticker_filter(df, model_dict):
# get non 'ticker' columns
non_ticker_cols = [col for col in df.columns if col is not 'ticker']
# get unique set of tickers
tickers = df.ticker.unique()
# create and prepopulate the predictions column
df['predictions'] = 0
for ticker in tickers:
# get boolean Series to filter the Dataframes by.
filter_by_ticker = df.ticker==ticker
# filter, predict and allocate
df.loc[filter_by_ticker,'predictions'] = model_dict[ticker].predict(
df.loc[filter_by_ticker,
non_ticker_cols]
)
return df
def model_apply_by_row(df_row, model_dict):
# includes some conversions to list to allow the predict method to run
return model_dict[df_row['ticker']].predict([df_row[['x1','close']].tolist()])[0]
性能
我在函数调用中使用 timeit
得到以下结果
你的例子demoAll
:
model_apply_by_row
%timeit demoAll.apply(model_apply_by_row,model_dict=ticker2model, axis=1)
3.78 ms ± 227 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
predict_by_ticker_filter
%timeit predict_by_ticker_filter(demoAll, ticker2model)
6.24 ms ± 1.11 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)
将 demoAll
的大小增加到 (606, 3)
:
model_apply_by_row
%timeit demoAll.apply(model_apply_by_row,model_dict=ticker2model, axis=1)
320 ms ± 28 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
predict_by_ticker_filter
%timeit predict_by_ticker_filter(demoAll, ticker2model)
6.1 ms ± 512 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
将 demoAll
的大小增加到 (6006, 3)
:
model_apply_by_row
%timeit demoAll.apply(model_apply_by_row,model_dict=ticker2model, axis=1)
3.15 s ± 371 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
predict_by_ticker_filter
%timeit predict_by_ticker_filter(demoAll, ticker2model)
9.1 ms ± 767 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
我正在尝试为以下场景找出一个有效的 split/apply/combine 方案。考虑下面定义的 pandas 数据框 demoAll
:
import datetime
import pandas as pd
demoA = pd.DataFrame({'date':[datetime.date(2010,1,1), datetime.date(2010,1,2), datetime.date(2010,1,3)],
'ticker':['A', 'A', 'A'],
'x1':[10,20,30],
'close':[120, 133, 129]}).set_index('date', drop=True)
demoB = pd.DataFrame({'date':[datetime.date(2010,1,1), datetime.date(2010,1,2), datetime.date(2010,1,3)],
'ticker':['B', 'B', 'B'],
'x1':[18,11,45],
'close':[50, 49, 51]}).set_index('date', drop=True)
demoAll = pd.concat([demoA, demoB])
print(demoAll)
结果是:
ticker x1 close
date
2010-01-01 A 10 120
2010-01-02 A 20 133
2010-01-03 A 30 129
2010-01-01 B 18 50
2010-01-02 B 11 49
2010-01-03 B 45 51
我还有一个代码到模型对象的字典映射
ticker2model = {'A':model_A, 'B':model_B,...}
其中每个模型都有一个 self.predict(df)
方法接收整个数据帧和 returns 一系列相同长度的数据。
我现在想创建一个与这些预测相对应的新列 demoAll['predictions']
。 cleanest/most-efficient 这样做的方法是什么?需要注意的几点:
demoAll
是代码特定数据帧的串联,每个数据帧仅按日期索引。因此demoAll
的索引不是唯一的。 (但是,date/ticker 的组合是唯一的。)我的想法是做类似下面示例的事情,但是 运行 遇到了索引、数据类型强制转换和慢 运行 时间等问题。真实数据集相当大(包括行和列)。
demoAll['predictions'] = demoAll.groupby('ticker').apply( lambda x: ticker2model[x.name].predict(x) )
我可能误解了您通过模型进行预测的内容,但如果我理解正确,我会执行以下操作:
- 预分配
demoAll
的 - 遍历代码和过滤器的唯一值
demoAll
- 过滤掉代码行
- 使用过滤后的 df 预测结果
- 将结果保存在
demoAll['predictions']
中的正确位置
predictions
列
使用您的代码的示例:
# get non 'ticker' columns
non_ticker_cols = [col for col in demoAll.columns if col is not 'ticker']
# get unique set of tickers
tickers = demoAll.ticker.unique()
# create and prepopulate the predictions column
demoAll['predictions'] = 0
for ticker in tickers:
# get boolean Series to filter the Dataframes by.
filter_by_ticker = demoAll.ticker == ticker
# filter, predict and allocate
demoAll.loc[filter_by_ticker, 'predictions'] = ticker2model[
ticker].predict(
demoAll.loc[filter_by_ticker,
non_ticker_cols]
)
输出如下:
ticker x1 close predictions
date
2010-01-01 A 10 120 10.0
2010-01-02 A 20 133 10.0
2010-01-03 A 30 129 10.0
2010-01-01 B 18 50 100.0
2010-01-02 B 11 49 100.0
2010-01-03 B 45 51 100.0
与使用应用的比较
我们可以按行使用应用程序,但正如您提到的,它会变慢。我将比较两者以了解加速。
设置
我将使用 sklearn
中的 DummyRegressor
来允许我调用 predict
方法并创建您在问题中提到的字典。
model_a = DummyRegressor(strategy='mean')
model_b = DummyRegressor(strategy='median')
model_a.fit([[10,14]], y=np.array([10]))
model_b.fit([[200,200]], [100])
ticker2model = {'A':model_a, 'B':model_b}
将两者定义为函数
def predict_by_ticker_filter(df, model_dict):
# get non 'ticker' columns
non_ticker_cols = [col for col in df.columns if col is not 'ticker']
# get unique set of tickers
tickers = df.ticker.unique()
# create and prepopulate the predictions column
df['predictions'] = 0
for ticker in tickers:
# get boolean Series to filter the Dataframes by.
filter_by_ticker = df.ticker==ticker
# filter, predict and allocate
df.loc[filter_by_ticker,'predictions'] = model_dict[ticker].predict(
df.loc[filter_by_ticker,
non_ticker_cols]
)
return df
def model_apply_by_row(df_row, model_dict):
# includes some conversions to list to allow the predict method to run
return model_dict[df_row['ticker']].predict([df_row[['x1','close']].tolist()])[0]
性能
我在函数调用中使用 timeit
得到以下结果
你的例子demoAll
:
model_apply_by_row
%timeit demoAll.apply(model_apply_by_row,model_dict=ticker2model, axis=1)
3.78 ms ± 227 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
predict_by_ticker_filter
%timeit predict_by_ticker_filter(demoAll, ticker2model)
6.24 ms ± 1.11 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)
将 demoAll
的大小增加到 (606, 3)
:
model_apply_by_row
%timeit demoAll.apply(model_apply_by_row,model_dict=ticker2model, axis=1)
320 ms ± 28 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
predict_by_ticker_filter
%timeit predict_by_ticker_filter(demoAll, ticker2model)
6.1 ms ± 512 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
将 demoAll
的大小增加到 (6006, 3)
:
model_apply_by_row
%timeit demoAll.apply(model_apply_by_row,model_dict=ticker2model, axis=1)
3.15 s ± 371 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
predict_by_ticker_filter
%timeit predict_by_ticker_filter(demoAll, ticker2model)
9.1 ms ± 767 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)