python - 慢双 for 循环
问题描述
我有一个双循环,它在一个数据集上运行,在多个匹配条件下将第一行与下一行进行比较。当满足条件时,匹配的对被添加到列表中。该代码适用于小型数据集,但对于超过 5k 行的任何内容都非常慢。
如果有人对提高性能有任何想法,我将不胜感激。
以下是代码和基础数据。
基本上我从excel导入数据。
result = []
def is_match(base_row, next_row):
return base_row['NOTIONAL'] == next_row['NOTIONAL'] and base_row['LEG'] != next_row['LEG'] and \
base_row['REF_RATE'] == next_row['REF_RATE'] and abs(base_row['RATE'] - next_row['RATE']) < 0.15 and \
abs((base_row['MATURITY_DATE'] - next_row['MATURITY_DATE']).days) <= 30
df_len = len(df.index)
for base_index, base_row in df.iterrows():
for i in range(base_index + 1, df_len):
if is_match(base_row, df.loc[i]):
result.append((base_index, i))
BUSN_DATE | TRADE_ID | 腿 | 类型 | REF_RATE | 速度 | 名义上的 | 成人礼 |
---|---|---|---|---|---|---|---|
2020 年 9 月 12 日 | 12345_P | 支付 | 固定的 | 固定的 | 1.50 | 10000000 | 2021 年 12 月 31 日 |
2020 年 9 月 12 日 | 12345_R | 录音 | 漂浮 | BBSW | 1.25 | 10000000 | 2021 年 12 月 31 日 |
2020 年 9 月 12 日 | 12346_R | 录音 | 固定的 | 固定的 | 1.55 | 10000000 | 27/12/2021 |
2020 年 9 月 12 日 | 12346_P | 支付 | 漂浮 | BBSW | 1.30 | 10000000 | 27/12/2021 |
df = pd.DataFrame({'BUSN_DATE': {0: '9/12/2020', 1: '9/12/2020', 2: '9/12/2020', 3: '9/12/2020'},
'TRADE_ID': {0: '12345_P', 1: '12345_R', 2: '12346_R', 3: '12346_P'},
'LEG': {0: 'PAY', 1: 'REC', 2: 'REC', 3: 'PAY'},
'TYPE': {0: 'FIXED', 1: 'FLOAT', 2: 'FIXED', 3: 'FLOAT'},
'REF_RATE': {0: 'FIXED', 1: 'BBSW', 2: 'FIXED', 3: 'BBSW'},
'RATE': {0: '1.50', 1: '1.25', 2: '1.55', 3: '1.30'},
'NOTIONAL': {0: '10000000', 1: '10000000', 2: '10000000', 3: '10000000'},
'MATURITY_DATE': {0: '31/12/2021',
1: '31/12/2021',
2: '27/12/2021',
3: '27/12/2021'}})
解决方案
创建一个函数create_cross_join
以使用原始 df 创建组合交叉连接数据框。
import itertools
def create_cross_join(dfn):
dfn = dfn.reset_index()
# create a combinations with 2 elemens
# -> [0,1,2,3]
# -> [(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)]
df_combine_idx = pd.DataFrame(itertools.combinations(dfn.index, 2), columns = ['a', 'b'])
dfa = dfn.loc[df_combine_idx['a']].reset_index(drop=True)
dfb = dfn.loc[df_combine_idx['b']].reset_index(drop=True).add_suffix('_2')
dfab = pd.concat([dfa, dfb], axis = 1)
return dfab
# convert float type and datetime type
df['RATE'] = df['RATE'].astype(float)
for col in ['BUSN_DATE', 'MATURITY_DATE']:
df[col] = pd.to_datetime(df[col], format='%d/%m/%Y')
# refer columns
cols = ['NOTIONAL', 'REF_RATE', 'LEG', 'RATE', 'MATURITY_DATE']
print(create_cross_join(df[cols]))
| | index | NOTIONAL | REF_RATE | LEG | RATE | MATURITY_DATE | index_2 | NOTIONAL_2 | REF_RATE_2 | LEG_2 | RATE_2 | MATURITY_DATE_2 |
|---:|--------:|-----------:|:-----------|:------|-------:|:--------------------|----------:|-------------:|:-------------|:--------|---------:|:--------------------|
| 0 | 0 | 10000000 | FIXED | PAY | 1.5 | 2021-12-31 00:00:00 | 1 | 10000000 | BBSW | REC | 1.25 | 2021-12-31 00:00:00 |
| 1 | 0 | 10000000 | FIXED | PAY | 1.5 | 2021-12-31 00:00:00 | 2 | 10000000 | FIXED | REC | 1.55 | 2021-12-27 00:00:00 |
| 2 | 0 | 10000000 | FIXED | PAY | 1.5 | 2021-12-31 00:00:00 | 3 | 10000000 | BBSW | PAY | 1.3 | 2021-12-27 00:00:00 |
| 3 | 1 | 10000000 | BBSW | REC | 1.25 | 2021-12-31 00:00:00 | 2 | 10000000 | FIXED | REC | 1.55 | 2021-12-27 00:00:00 |
| 4 | 1 | 10000000 | BBSW | REC | 1.25 | 2021-12-31 00:00:00 | 3 | 10000000 | BBSW | PAY | 1.3 | 2021-12-27 00:00:00 |
| 5 | 2 | 10000000 | FIXED | REC | 1.55 | 2021-12-27 00:00:00 | 3 | 10000000 | BBSW | PAY | 1.3 | 2021-12-27 00:00:00 |
对相同的 NOTIONAL 和 REF_RATE 使用 group by 'NOTIONAL', 'REF_RATE',并减少交叉连接大小。
def main(df):
result_list = []
for (NOTIONAL, REF_RATE), group in df.groupby(['NOTIONAL', 'REF_RATE']):
print(f'handle <NOTIONAL:{NOTIONAL}, REF_RATE:{REF_RATE}>')
col_cond = ['LEG', 'RATE', 'MATURITY_DATE']
dfm = create_cross_join(group[col_cond])
cond = True
cond &= dfm['LEG'] != dfm['LEG_2']
cond &= abs(dfm['RATE'] - dfm['RATE_2']) < 0.15
cond &= abs((dfm['MATURITY_DATE'] - dfm['MATURITY_DATE_2']).dt.days) <= 30
match_list = dfm.loc[cond, ['index', 'index_2']].values.tolist()
if len(match_list) > 0:
result_list.extend(match_list)
return (result_list)
result_list = main(df)
速度测试:
data = [{'BUSN_DATE': '9/12/2020', 'TRADE_ID': '12345_P', 'LEG': 'PAY', 'TYPE': 'FIXED', 'REF_RATE': 'FIXED', 'RATE': 1.5, 'NOTIONAL': 10000000, 'MATURITY_DATE': '31/12/2021'},
{'BUSN_DATE': '9/12/2020', 'TRADE_ID': '12345_R', 'LEG': 'REC', 'TYPE': 'FLOAT', 'REF_RATE': 'BBSW', 'RATE': 1.25, 'NOTIONAL': 10000000, 'MATURITY_DATE': '31/12/2021'},
{'BUSN_DATE': '9/12/2020', 'TRADE_ID': '12346_R', 'LEG': 'REC', 'TYPE': 'FIXED', 'REF_RATE': 'FIXED', 'RATE': 1.55, 'NOTIONAL': 10000000, 'MATURITY_DATE': '27/12/2021'},
{'BUSN_DATE': '9/12/2020', 'TRADE_ID': '12346_P', 'LEG': 'PAY', 'TYPE': 'FLOAT', 'REF_RATE': 'BBSW', 'RATE': 1.3, 'NOTIONAL': 10000000, 'MATURITY_DATE': '27/12/2021'}]
df = pd.DataFrame(data*2500)
df['RATE'] = df['RATE'].astype(float)
for col in ['BUSN_DATE', 'MATURITY_DATE']:
df[col] = pd.to_datetime(df[col], format='%d/%m/%Y')
cols = ['NOTIONAL', 'REF_RATE', 'LEG', 'RATE', 'MATURITY_DATE']
print(df.shape)
result_list = main(df)
# 10k rows -> 46s -> 12500000
结果:
result_list[:10]
[[1, 3],
[1, 7],
[1, 11],
[1, 15],
[1, 19],
[1, 23],
[1, 27],
[1, 31],
[1, 35],
[1, 39]]
推荐阅读
- symfony - 最后输入的用户未捕获
- c# - 如果在 C# 中验证失败,如何使用 RegularExpressionAttribute 通过抛出异常来验证字符串属性?
- tensorflow - 如何在张量流中训练线性回归模型时确定分类特征中值的相应权重?
- javascript - ES6:如何使用变量类名?
- element - 编写一个函数,将 0 放入数组的元素 1。确保函数返回 null
- python - 使用 Python 插入 SQL 数据库 - 性能
- java - 如何使形状在 for 循环中只出现一次?
- angular - Openlayers 地图不可拖动
- python - 使用 SimpleImputer 中的 .fit() 进行插补时出错
- x86 - AMD 是否支持 x2APIC?