我面临着一个巨大的瓶颈,我将方法()应用于Pandas DataFrame中的每一行。执行时间为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
而且,方法ImputeTargetAcrossSameDate()方法如下:
def ImputeTargetAcrossSameDate(self, x, tmp):
ret = tmp[tmp['APPT_SCHD_ARVL_D'] == x['APPT_SCHD_ARVL_D']]
ret = ret['ACTL_CNTRS_BY_DAY']
if ret.empty:
r = 0
else:
r = ret.values
r = r[0]
return r
有没有办法优化此apply()调用以减少总时间。 请注意,我必须在DataFrame上运行此过程,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'的包装方法。适用于在每一行上调用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天的数据,我运行了普通的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
答案 0 :(得分:1)
这是一个有点匆忙的解决方案,因为我现在即将离开周末,但它确实有效。
输入数据框:
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
代码:
tt = df[pd.notnull(df.ACTL_CNTRS_BY_DAY)].APPT_SCHD_ARVL_D.unique()
vv = df[pd.notnull(df.ACTL_CNTRS_BY_DAY)]
for i,_ in df.iterrows():
if df.ix[i,"APPT_SCHD_ARVL_D"] in tt:
df.ix[i,"ACTL_CNTRS_BY_DAY"] = vv[vv.APPT_SCHD_ARVL_D == df.ix[i,"APPT_SCHD_ARVL_D"]]["ACTL_CNTRS_BY_DAY"].values[0]
df = df.fillna(0.0)
基本上没有必要apply
一个功能。我在这里做的是:
tt
vv
tt
中每行的日期是否存在。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
中最多只有一个值不为空,因此我在first()
中使用groupby
来选择唯一存在的值。< / p>