优化Python代码。优化Pandas应用。麻木比纯Python慢

2024-10-03 06:22:37 发布

您现在位置:Python中文网/ 问答频道 /正文

我正面临一个巨大的瓶颈,我对Pandas DataFrame中的每一行都应用了method()。执行时间大概在15-20分钟之间。在

现在,我使用的代码如下:

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

其中,imputeTargetCrossSameDate()方法如下:

^{pr2}$

有什么方法可以优化这个apply()调用来减少总的时间吗。 注意,我必须在DataFrame上运行这个过程,它存储了2年的数据。我运行了15天,花了15-20分钟,而运行了1个月的数据,执行了超过45分钟,之后我不得不强制停止进程,这样在运行全数据集的情况下,这将是一个巨大的问题。在

还请注意,我遇到了一些引入numba来优化代码的示例http://pandas.pydata.org/pandas-docs/stable/enhancingperf.html,下面是我的numba实现:

调用numba方法的语句:

backup['ACTL_CNTRS_BY_DAY'] = self.compute_(tmp, backup)

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

这是一个包装器方法,它取代了Pandas的apply,对每一行调用Impute方法。使用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

对于时间周期为5天的数据,我运行了normal apply()方法和numba()方法,结果如下:

With Numba:
749805 microseconds

With DF.apply()
484603 microseconds.

如你所见,numba比较慢,这是不应该发生的,所以万一我遗漏了什么,请告诉我,这样我就可以优化这段代码。在

提前谢谢

编辑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

Tags: 方法selfdfbynanbackuptmpapply
1条回答
网友
1楼 · 发布于 2024-10-03 06:22:37

这是一个有点仓促的解决方案,因为我现在就要离开到周末了,但它奏效了。在

输入数据帧:

index    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                10
895       2014-05-29                NaN
898       2014-05-29                NaN

代码:

^{pr2}$

基本上不需要apply一个函数。我在这里做的是:

  • 获取值不为null的所有唯一日期。->;tt
  • 只创建非空值的数据帧。->;vv
  • 迭代所有行并测试每行中的日期是否存在于tt中。在
  • 如果为true,则从vv获取值,其中df中的日期相同,并将其分配给df。在
  • 然后用0.0填充所有其他空值。在

迭代行不是一件很快的事情,但我希望它比旧代码更快。如果我有更多的时间,我会想出一个没有迭代的解决方案,也许在周一。在

编辑: 使用pd.merge()而不进行迭代的解决方案:

dg = df[pd.notnull(df.ACTL_CNTRS_BY_DAY)].groupby("APPT_SCHD_ARVL_D").first()["ACTL_CNTRS_BY_DAY"].to_frame().reset_index()
df = pd.merge(df,dg,on="APPT_SCHD_ARVL_D",how='outer').rename(columns={"ACTL_CNTRS_BY_DAY_y":"ACTL_CNTRS_BY_DAY"}).drop("ACTL_CNTRS_BY_DAY_x",axis=1).fillna(0.0)

您的数据意味着ACTL_CNTRS_BY_DAY中最多只有一个值不是空的,所以我使用groupby中的first()来选取唯一存在的值。在

相关问题 更多 >