Распараллелить панды применяются

Я новичок в пандах, я уже хочу распараллелить операцию применения по строкам. До сих пор я обнаружил, что Parallelize применяется после pandas groupby. Однако это работает только для сгруппированных фреймов данных.

У меня другой случай использования: у меня есть список праздников, и для текущей строки / даты я хочу найти нерабочие дни до и после этого дня до следующего праздника.

Это функция, которую я вызываю через apply:

def get_nearest_holiday(x, pivot):
    nearestHoliday = min(x, key=lambda x: abs(x- pivot))
    difference = abs(nearesHoliday - pivot)
    return difference / np.timedelta64(1, 'D')

Как я могу ускорить это?

редактировать

Я немного поэкспериментировал с пулами питонов - но это не был ни хороший код, ни мои вычисленные результаты.

4 ответа

Решение

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

Давайте начнем с некоторых дат...

import pandas as pd

dates = pd.to_datetime(['2016-01-03', '2016-09-09', '2016-12-12', '2016-03-03'])

Мы будем использовать некоторые данные из отпуска pandas.tseries.holiday - обратите внимание, что в действительности мы хотим DatetimeIndex...

from pandas.tseries.holiday import USFederalHolidayCalendar

holiday_calendar = USFederalHolidayCalendar()
holidays = holiday_calendar.holidays('2016-01-01')

Это дает нам:

DatetimeIndex(['2016-01-01', '2016-01-18', '2016-02-15', '2016-05-30',
               '2016-07-04', '2016-09-05', '2016-10-10', '2016-11-11',
               '2016-11-24', '2016-12-26',
               ...
               '2030-01-01', '2030-01-21', '2030-02-18', '2030-05-27',
               '2030-07-04', '2030-09-02', '2030-10-14', '2030-11-11',
               '2030-11-28', '2030-12-25'],
              dtype='datetime64[ns]', length=150, freq=None)

Теперь мы находим индексы ближайшего ближайшего праздника для исходных дат, используя searchsorted:

indices = holidays.searchsorted(dates)
# array([1, 6, 9, 3])
next_nearest = holidays[indices]
# DatetimeIndex(['2016-01-18', '2016-10-10', '2016-12-26', '2016-05-30'], dtype='datetime64[ns]', freq=None)

Затем возьмите разницу между двумя:

next_nearest_diff = pd.to_timedelta(next_nearest.values - dates.values).days
# array([15, 31, 14, 88])

Вам нужно быть осторожным с индексами, чтобы не оборачиваться, а для предыдущей даты сделайте расчет с помощью indices - 1 но это должно действовать как (я надеюсь) относительно хорошая основа.

Для параллельного подхода это ответ, основанный на применении параллелизма после групповой панды:

from joblib import Parallel, delayed
import multiprocessing

def get_nearest_dateParallel(df):
    df['daysBeforeHoliday'] = df.myDates.apply(lambda x: get_nearest_date(holidays.day[holidays.day < x], x))
    df['daysAfterHoliday']  =  df.myDates.apply(lambda x: get_nearest_date(holidays.day[holidays.day > x], x))
    return df

def applyParallel(dfGrouped, func):
    retLst = Parallel(n_jobs=multiprocessing.cpu_count())(delayed(func)(group) for name, group in dfGrouped)
    return pd.concat(retLst)

print ('parallel version: ')
# 4 min 30 seconds
%time result = applyParallel(datesFrame.groupby(datesFrame.index), get_nearest_dateParallel)

но я предпочитаю подход @NinjaPuppy, потому что он не требует O(n * number_of_holidays)

Я думаю, что пакет pandarallel упрощает это сейчас. Я не очень разбирался в этом, но должен помочь.

Вы также можете легко распараллелить свои вычисления, используя библиотеку parallel-pandas . Всего две дополнительные строчки кода!

      # pip install parallel-pandas
import pandas as pd
import numpy as np
from parallel_pandas import ParallelPandas

#initialize parallel-pandas
ParallelPandas.initialize(n_cpu=8, disable_pr_bar=True)

def foo(x):
    """Your awesome function"""
    return np.sqrt(np.sum(x ** 2))    

df = pd.DataFrame(np.random.random((1000, 1000)))

%%time
res = df.apply(foo, raw=True)

Wall time: 5.3 s

# p_apply - is parallel analogue of apply method
%%time
res = df.p_apply(foo, raw=True, executor='processes')

Wall time: 1.2 s
Другие вопросы по тегам