Я столкнулся с огромным узким местом, где я применяю метод() к каждой строке в Pandas DataFrame. Время выполнения составляет 15-20 минут.Оптимизируйте код Python. Оптимизируйте Pandas. Numba медленный, чем чистый python
Теперь код, который я использую следующим образом:
def FillTarget(self, df):
backup = df.copy()
target = list(set(df['ACTL_CNTRS_BY_DAY']))
df = df[~df['ACTL_CNTRS_BY_DAY'].isnull()]
tmp = df[df['ACTL_CNTRS_BY_DAY'].isin(target)]
tmp = tmp[['APPT_SCHD_ARVL_D', 'ACTL_CNTRS_BY_DAY']]
tmp.drop_duplicates(subset='APPT_SCHD_ARVL_D', inplace=True)
t1 = dt.datetime.now()
backup['ACTL_CNTRS_BY_DAY'] = backup.apply(self.ImputeTargetAcrossSameDate,args=(tmp,), axis=1)
# backup['ACTL_CNTRS_BY_DAY'] = self.compute_(tmp, backup)
t2 = dt.datetime.now()
print("Time for the bottleneck is ", (t2-t1).microseconds)
print("step f")
return backup
И метод ImputeTargetAcrossSameDate() метод заключается в следующем:
def ImputeTargetAcrossSameDate(self, x, tmp):
ret = tmp[tmp['APPT_SCHD_ARVL_D'] == x['APPT_SCHD_ARVL_D']]
ret = ret['ACTL_CNTRS_BY_DAY']
if ret.empty:
r = 0
else:
r = ret.values
r = r[0]
return r
Есть ли способ, чтобы оптимизировать это применить() чтобы сократить общее время. Обратите внимание, что мне придется запустить этот процесс в DataFrame, который хранит данные в течение 2 лет. Я запускал его в течение 15 дней, и мне потребовалось 15-20 минут, а когда он работал в течение 1 месяца данных, он выполнялся более 45 минут, после чего мне пришлось принудительно остановить процесс, таким образом, работая на полном наборе данных , это будет огромная проблема.
Также отмечу, что, я наткнулся на несколько примеров http://pandas.pydata.org/pandas-docs/stable/enhancingperf.html для введения Numba для оптимизации коды, а следующая моя реализация Numba:
Заявления назвать Numba метода:
backup['ACTL_CNTRS_BY_DAY'] = self.compute_(tmp, backup)
Compute метод Numba :
@numba.jit
def compute_(self, df1, df2):
n = len(df2)
result = np.empty(n, dtype='float64')
for i in range(n):
d = df2.iloc[i]
result[i] = self.apply_ImputeTargetAcrossSameDate_method(df1['APPT_SCHD_ARVL_D'].values, df1['ACTL_CNTRS_BY_DAY'].values,
d['APPT_SCHD_ARVL_D'], d['ACTL_CNTRS_BY_DAY'])
return result
Это метод обертка, который заменяет панды применить для вызова метода приписывать на каждой строке. Метод Условная оценка с помощью Numba выглядит следующим образом:
@numba.jit
def apply_ImputeTargetAcrossSameDate_method(self, df1col1, df1col2, df2col1, df2col2):
dd = np.datetime64(df2col1)
idx1 = np.where(df1col1 == dd)[0]
if idx1.size == 0:
idx1 = idx1
else:
idx1 = idx1[0]
val = df1col2[idx1]
if val.size == 0:
r = 0
else:
r = val
return r
Я побежал метод нормальный применить(), а также Numba() метод для данных, имеющих период времени 5 дней и после были мои результаты:
With Numba:
749805 microseconds
With DF.apply()
484603 microseconds.
Как вы можете видеть, numba медленнее, чего не должно быть, поэтому на случай, если я что-то упустил, lemme знает, что я могу оптимизировать этот кусок кода.
Заранее спасибо
Редактировать 1 В соответствии с просьбой, данные пропущено (глава 20 лучших строк) добавляется следующим образом: Перед:
APPT_SCHD_ARVL_D ACTL_CNTRS_BY_DAY
919 2020-11-17 NaN
917 2020-11-17 NaN
916 2020-11-17 NaN
915 2020-11-17 NaN
918 2020-11-17 NaN
905 2014-06-01 NaN
911 2014-06-01 NaN
913 2014-06-01 NaN
912 2014-06-01 NaN
910 2014-06-01 NaN
914 2014-06-01 NaN
908 2014-06-01 NaN
906 2014-06-01 NaN
909 2014-06-01 NaN
907 2014-06-01 NaN
898 2014-05-29 NaN
892 2014-05-29 NaN
893 2014-05-29 NaN
894 2014-05-29 NaN
895 2014-05-29 NaN
После того, как:
APPT_SCHD_ARVL_D ACTL_CNTRS_BY_DAY
919 2020-11-17 0.0
917 2020-11-17 0.0
916 2020-11-17 0.0
915 2020-11-17 0.0
918 2020-11-17 0.0
905 2014-06-01 0.0
911 2014-06-01 0.0
913 2014-06-01 0.0
912 2014-06-01 0.0
910 2014-06-01 0.0
914 2014-06-01 0.0
908 2014-06-01 0.0
906 2014-06-01 0.0
909 2014-06-01 0.0
907 2014-06-01 0.0
898 2014-05-29 0.0
892 2014-05-29 0.0
893 2014-05-29 0.0
894 2014-05-29 0.0
895 2014-05-29 0.0
Что делает этот метод? В приведенном выше примере данных вы можете увидеть, что некоторые даты повторяются, а значения против них - NaN. Если все строки с одинаковой датой имеют значение NaN, оно заменяет их на 0. Но есть некоторые случаи, скажем, например: 2014-05-29, где будет 10 строк с одинаковой датой, и только 1 строка против этой даты, когда будет какая-то ценность. (Скажем, 10). Затем метод() будет заполнять все значения с этой конкретной даты с помощью 10 вместо NaN.
Пример:
898 2014-05-29 NaN
892 2014-05-29 NaN
893 2014-05-29 NaN
894 2014-05-29 10
895 2014-05-29 NaN
выше должно стать:
898 2014-05-29 10
892 2014-05-29 10
893 2014-05-29 10
894 2014-05-29 10
895 2014-05-29 10
Вместо '~ df ['ACTL_CNTRS_BY_DAY']. Isnull()' вы можете использовать 'df ['ACTL_CNTRS_BY_DAY']. Notnull()', как незначительное улучшение. – Khris
Это утверждение не является узким местом, но метод apply() для каждой строки является узким местом. –
Я знаю, это была лишь незначительная вещь, которую я заметил. Можете ли вы привести пример того, как выглядят ваши данные раньше и как это должно выглядеть после? – Khris