并行化 Pandas apply函数

9

对于初学者的我来说,我想要并行化一个逐行操作的应用程序。到目前为止,我找到了Parallelize apply after pandas groupby,但是这似乎仅适用于分组的数据框。

我的用例不同:我有一个假期列表,并且针对当前的行/日期,我想找到在假期之前和之后到下个假日的天数。

这是我通过应用调用的函数:

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')

如何提高速度?

编辑

我尝试过一些 Python 的池技术 - 但代码既不好看,也没有得到计算结果。


@AmiTavory 我在github上添加了一个最小的代码示例:https://github.com/geoHeil/pythonQuestions/blob/master/improvementsDateOperations.ipynb - Georg Heiler
@geoHeil 所以关于设置的问题,假期是每年固定日期还是会变动? - Ami Tavory
他们来自http://www.timeanddate.com/holidays/germany/,我筛选了国家假日。这些通常是固定的,例如12月24日的圣诞节。 - Georg Heiler
@AmiTavory 我刚刚更新了最小示例:https://github.com/geoHeil/pythonQuestions/blob/master/improvementsDateOperations.ipynb 即使对于我的第三种方法,也只返回现有的列。我看不到计算结果。 - Georg Heiler
@geoHeil,我尝试了一种不依赖于并行处理的方法来回答你的问题... 我不是100%确定你有多么坚定地想要那个方法,或者你只是想尝试任何可以加速的方法... - Jon Clements
显示剩余12条评论
4个回答

6

我认为现在使用pandarallel包使得这变得更加容易了。虽然我还没有深入研究,但应该能解决问题。


6

关于并行方法的答案,基于Pandas分组后并行应用的解决方案

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)。


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进行计算,但它应该作为(我希望)相对较好的基础。


我已经使用你的代码更新了最小示例(请参见底部)。尝试在假期中使用“我的日期时间索引”,我收到了一个越界的索引。 - Georg Heiler
评论不适合进行长时间的讨论;此对话已被移至聊天室 - Jon Clements

2
您还可以使用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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接