Как эффективно вычесть годы из дат в python?

#python #r #pandas

#питон #r #панды

Вопрос:

Я вычитаю years из date column в Python, который чувствовал себя намного медленнее, чем R, что обычно не так, поэтому мне интересно, есть ли более быстрый/ эффективный способ сделать это в python ?

(Так как после многих лет работы в R я возвращаюсь к python, поэтому мои навыки работы на python больше не хороши и я ищу оптимизацию кода на python).

код на python:

 import numpy as np import pandas as pd import datetime as dt  import time  

Данные, которые я показываю ниже, являются просто фиктивными, чтобы дать представление о date format том, с чем я работаю (количество строк в моих исходных данных: 466285).

 df = pd.DataFrame({'date_str': ['Apr-84','Mar-66','May-85']})  df['date'] = pd.to_datetime(df['date_str'], format = '%b-%y')  

Поскольку я получал некоторые неправильные даты в годах, такие как year: 2066, 2085 и т. Д. поэтому написал небольшую функцию для корректировки дат в соответствии с моими потребностями:

 # year subtraction function def date_correction(x):  if x gt; pd.to_datetime('2017-12-01'):  x = (x - pd.to_timedelta(100 * 365.24, unit='d'))   else:  x = x    return x  start = time.time() df['date'] = df['date'].apply(date_correction) end = time.time()  print("Time taken: ", end - start)  

Time taken: 32.958526611328125

Вышеуказанное время, я думаю, в секундах, так как на это ушло много времени, и это заставило меня также засечь это в R.

Код R:

 library(tidyverse) library(lubridate) library(microbenchmark)  
 df = data.frame(date_str = c('Apr-84','Mar-66','May-85'))  df lt;- df %gt;%   mutate(date = lubridate::my(date_str))   

операция вычитания и времени:

 mbm lt;- microbenchmark( 'date_subt' = {  df lt;- df %gt;%   mutate(date = if_else(   df$date gt; ymd('2017-12-01'),  df$date %m-% years(100),  df$date  ))  }  )  
 mbm  

Результаты:

 Unit: milliseconds  expr min lq mean median uq max neval date_subt 324.3357 331.2416 372.4745 338.8745 396.3026 744.4625 100  
 autplot(mbm)  

введите описание изображения здесь

Комментарии:

1. Первой оптимизацией, которую я вижу, было бы вычисление объектов даты и времени 2017-12-01 и 100 лет вне функции. Это делает вычисления примерно в 10 раз быстрее (проверено с использованием 100000 элементов в фрейме данных).

2. Я только искал vectorized подход python , так как была огромная разница во времени выполнения R amp; python, что заставило меня задуматься о моем подходе.

Ответ №1:

Можете ли вы попробовать запустить свой код с помощью timedelta?

подобный этому:

 from datetime import timedelta if dt gt; pd.to_datetime('2017-12-01'):  dt -= timedelta(years=100)  

Комментарии:

1. Я получал ошибку при попытке dt -= timedelta(years=100) , так как нет аргумента «годы», поэтому вместо этого попытался использовать дни: x -= dt.timedelta(days=100 * 365.24) и снова это заняло Time taken: 32.42573404312134 секунды. спасибо, что предложили мне этот подход, но это также намного медленнее, чем операция R

2. хорошо.. я думаю, что подход Корралиена-лучшая практика

Ответ №2:

Векторизованный способ с использованием логической маски и DateOffset :

 df.update(df.loc[df['date'] gt; '2017-12-01', 'date'] - pd.DateOffset(years=100)  

Более лаконичный способ (без update )

 df.loc[df['date'] gt; '2017-12-01', 'date'] -= pd.DateOffset(years=100)  

Комментарии:

1. Спасибо @Corralien, я думал, что с помощью .apply() этого я векторизовал его, python но, думаю, я ошибся. Еще раз спасибо, что поделились кодом. Ценю твою помощь !

2. К сожалению, apply это удобный способ для петли (почти)

3. да, вы правы !!

4. И Time taken: 0.00697779655456543 на этот раз это заняло несколько секунд.