Как намекает в комментариях, вам нужен лучший алгоритм.Одна из возможных оптимизаций - это сортировка col_B
, и таким образом вы можете выполнить бинарный поиск, чтобы найти ближайший элемент.Я поместил обе реализации в файл test.py
с 1000 случайными элементами.Вот некоторый код начальной загрузки, который нужно проверить самостоятельно (find_nearest2
- улучшенная функция, я сохранил эти результаты в "other_new_col"
).
O (N log N) решение, которое поддерживает то же поведение, что и исходный O (N ^2) решение:
import bisect
import numpy as np
import pandas as pd
# Original
def find_nearest1(row,array):
idx,val = min(enumerate(array['col_B']), key=lambda x: abs(x[1]-row['col_A']))
return array['col_C'][idx]
# Optimized
def find_nearest2(row,array):
idx = bisect.bisect_left(array['col_B'].values, row['col_A'])
arr_len = len(array['col_C'])
if idx == 0:
return array['col_C'].iloc[0]
elif idx == arr_len:
return array['col_C'].iloc[-1]
else:
diff1 = abs(array['col_B'].iloc[idx] - row['col_A'])
diff2 = abs(array['col_B'].iloc[idx-1] - row['col_A'])
m = min(diff1, diff2)
if np.isclose(m, diff1):
return array['col_C'].iloc[idx]
else:
return array['col_C'].iloc[idx-1]
np.random.seed(1) # Set seed for reproducability
size = 1000
df1_data = np.random.random(size)
df1_cols = ["col_A"]
df_1 = pd.DataFrame(df1_data, columns=df1_cols)
df2_data = {"col_B": np.random.random(size), "col_C": np.random.random(size)}
df_2 = pd.DataFrame.from_dict(df2_data)
if __name__ == "__main__":
# Original
df_1['new_col']=df_1.apply(lambda row: find_nearest1(row,df_2),axis=1)
# Optimized
df_2_sorted = df_2.sort_values('col_B')
df_1['other_new_col'] = df_1.apply(lambda row: find_nearest2(row, df_2_sorted), axis=1)
print(df_1)
и это выводит данные (чтобы вы могли проверить, поддерживается ли исходное поведение):
col_A new_col other_new_col
0 0.417022 0.842518 0.842518
1 0.720324 0.633461 0.633461
2 0.000114 0.327524 0.327524
3 0.302333 0.947542 0.947542
4 0.146756 0.985317 0.985317
5 0.092339 0.875530 0.875530
6 0.186260 0.348727 0.348727
7 0.345561 0.471819 0.471819
8 0.396767 0.674607 0.674607
9 0.538817 0.696878 0.696878
10 0.419195 0.695152 0.695152
...
990 0.812507 0.584883 0.584883
991 0.283802 0.497448 0.497448
992 0.527847 0.490089 0.490089
993 0.339417 0.905808 0.905808
994 0.554667 0.745292 0.745292
995 0.974403 0.615726 0.615726
996 0.311703 0.594144 0.594144
997 0.668797 0.571321 0.571321
998 0.325967 0.576978 0.576978
999 0.774477 0.963032 0.963032
Некоторые результаты производительности:
bash-3.2$ ipython
Python 3.6.4 |Anaconda, Inc.| (default, Jan 16 2018, 12:04:33)
Type 'copyright', 'credits' or 'license' for more information
IPython 6.2.1 -- An enhanced Interactive Python. Type '?' for help.
In [1]: from test import find_nearest1, find_nearest2, df_1, df_2
In [2]: %timeit df_1['new_col']=df_1.apply(lambda row: find_nearest1(row,df_2),axis=1)
6.04 s ± 13.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [3]: %timeit df_2_sorted = df_2.sort_values('col_B')
201 µs ± 3.25 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [4]: df_2_sorted = df_2.sort_values('col_B')
In [5]: %timeit df_1.apply(lambda row: find_nearest2(row, df_2_sorted), axis=1)
111 ms ± 1.67 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
Итак, оптимизированная реализация примерно в 50 раз быстрее. Это потому, что исходный алгоритм O (N ^ 2), но вы можете сделать это за O (N log N).Коэффициент ускорения будет еще лучше для 100 миллионов строк.
HTH.