Как ускорить применение метода с лямбда в пандах с датами времени



Я новичок в пандах.



У меня есть очень простой фрейм данных с именем dlf с индексом и двумя столбцами с 40K-строкой. Он загружается следующим образом:



d = pd.DataFrame.from_csv(csvsLocation + 'name.csv', index_col='ID', infer_datetime_format=True)
d['LAST'] = pd.to_datetime(d['LAST'], format = '%d-%b-%y')
d['FIRST'] = pd.to_datetime(d['FIRST'], format = '%d-%b-%y')
dlf = d[['LAST', 'FIRST']]


Это выглядит примерно так:



    LAST    FIRST
ID
1 1997-04-17 1991-10-04
3 2009-02-13 1988-07-07
5 2009-10-24 1995-12-06
6 1996-04-31 1989-03-14


Запуск этого метода apply занимает 5 секунд :



year = 1997
dlf[str(year)] = dlf.apply(lambda row: 1*(year >= row['FIRST'].year and year <= row['LAST'].year), axis=1)


Мне нужно ускорить этот процесс, потому что я намерен запустить его сотни раз.

Я подозреваю, что проблема заключается в использовании лямбды.



Что я сделал не так, и/или как я могу ускорить это?

486   3  

3 ответов:

Решение

Вы можете получить доступ к году через dt.year в обоих столбцах даты:

year = 1999
df[str(year)] = 1 * ((df['FIRST'].dt.year <= year) & (df['LAST'].dt.year >= year))
print(df)

Вывод:

         LAST      FIRST  1999
ID                            
1  1997-04-17 1991-10-14     0
3  2009-02-13 1988-07-07     1
5  2009-10-24 1995-10-06     1
6  1996-04-30 1969-03-14     0

Вы также можете сохранить логическое значение как результат:

df[str(year)] = (df['FIRST'].dt.year <= year) & (df['LAST'].dt.year >= year)
print(df)

Вывод:

         LAST      FIRST   1999
ID                             
1  1997-04-17 1991-10-14  False
3  2009-02-13 1988-07-07   True
5  2009-10-24 1995-10-06   True
6  1996-04-30 1969-03-14  False

Производительность

Измерение производительности-это всегда весело. Но измерение может быть сложным. Если мы просто используем наш крошечный пример фрейма данных с 4 строками, все становится немного медленнее:

%timeit dlf[str(year)] = dlf.apply(lambda row: 1*(year >= row['FIRST'].year and year <= row['LAST'].year), axis=1)

1000 loops, best of 3: 1.27 ms per loop


%timeit df[str(year)] = 1 * ((df['FIRST'].dt.year <= year) & (df['LAST'].dt.year >= year))

100 loops, best of 3: 1.7 ms per loop

Но давайте посмотрим на 40K строк:

big = pd.concat([df] * 10000)

>>> big.info()
<class 'pandas.core.frame.DataFrame'>
Int64Index: 40000 entries, 1 to 6
Data columns (total 4 columns):
LAST     40000 non-null datetime64[ns]
FIRST    40000 non-null datetime64[ns]
1999     40000 non-null bool
1997     40000 non-null int64
dtypes: bool(1), datetime64[ns](2), int64(1)
memory usage: 1.3 MB

Теперь мы можем видеть значительное ускорение:

%timeit big[str(year)] = big.apply(lambda row: 1*(year >= row['FIRST'].year and year <= row['LAST'].year), axis=1)

1 loops, best of 3: 6.51 s per loop

%timeit big[str(year)] = 1 * ((big['FIRST'].dt.year <= year) & (big['LAST'].dt.year >= year))

100 loops, best of 3: 8.33 ms per loop
Это примерно в 780 раз быстрее.

Я бы предварительно вычислил first_year и last_year, чтобы упростить сравнения:

dlf[year] = dlf[dlf['first_year'] <= year & [dlf['last_year'] >= year]

Если я правильно понял ваш вопрос, вы собираетесь добавить несколько столбцов (несколько лет), вот общее векторизованное решение , поэтому вам не нужно повторять его 100 раз:

years = [1997, 2016, 2000, 1989]
years = sorted(years)

dfy = pd.DataFrame(pd.Series(years * len(df)).reshape(len(df),len(years)), columns=years)

df = df.join(dfy.apply(lambda x: x.between(df.FIRST.dt.year, df.LAST.dt.year)).astype(int))

df.columns = df.columns.astype(str)

Шаг за шагом:

In [160]: years = [1997, 2016, 2000, 1989]

In [161]: years = sorted(years)

In [162]: dfy = pd.DataFrame(pd.Series(years * len(df)).reshape(len(df),len(years)), columns=years)

In [163]: dfy
Out[163]:
   1989  1997  2000  2016
0  1989  1997  2000  2016
1  1989  1997  2000  2016
2  1989  1997  2000  2016
3  1989  1997  2000  2016

In [164]: dfy.apply(lambda x: x.between(df.FIRST.dt.year, df.LAST.dt.year)).astype(int)
Out[164]:
   1989  1997  2000  2016
0     0     1     0     0
1     1     1     1     0
2     0     1     1     0
3     1     0     0     0

In [165]: df = df.join(dfy.apply(lambda x: x.between(df.FIRST.dt.year, df.LAST.dt.year)).astype(int))

In [166]: df.columns = df.columns.astype(str)

In [167]: df
Out[167]:
       FIRST       LAST  1989  1997  2000  2016
0 1991-10-04 1997-04-17     0     1     0     0
1 1988-07-07 2009-02-13     1     1     1     0
2 1995-12-06 2009-10-24     0     1     1     0
3 1989-03-14 1996-04-30     1     0     0     0

Comments

    Ничего не найдено.