我有两个大数据框,其中df1
比df2
有更多的行,因为df1
在所讨论的物流的更精细的时间分辨率下运行。我想将df2
的两个值列匹配到df1
,并使用df.dt.floor()
函数创建一个时间参考列,以便可以应用df1.time_ref == df2.time
满射映射。想象一下这样的内容:
df1: df2:
time time_ref time sale nbr
0 10.10 01.10 01.10 27344 4
1 17.10 01.10 01.11 31160 5
2 24.10 01.10 01.12 19482 3
3 31.10 01.10
4 07.11 01.11
5 14.11 01.11
6 21.11 01.11
7 28.11 01.11
8 05.12 01.12
目标是显示一个月的销售额/nbr与该月每个星期的比例,以供参考。因此,它应该像这样结束:
df1:
time time_ref monthlyObjAvg
0 10.10 01.10 6836
1 17.10 01.10 6836
2 24.10 01.10 6836
3 31.10 01.10 6836
4 07.11 01.11 6232
5 14.11 01.11 6232
6 21.11 01.11 6232
7 28.11 01.11 6232
8 05.12 01.12 6494
虽然我还没有考虑过,但在SQL中这可能真的很容易。使用一些近似伪SQL,该操作可能具有以下性质:
SELECT df1.*FROM df1, df2
JOIN df2.sale/df2.nbr AS "monthlyObjAvg" WHERE df1.time_ref = df2.time
在Pandas中,我花了很多时间来解决甚至研究这个问题,因为所有的搜索引擎结果都只导致.map()函数或条件列选择问题。注意,不能应用类似df1[df1["time_ref"] == df2["time"]]["sale"]
的经典条件选择,因为在Pandas中两个数据帧之间的比较是非法的。我的直觉还认为,Pandas可能具有某种检测功能,可以注意到满射无二义映射的存在,然后将这种表达合理化,但结果证明这是错误的。
注意,在此之前我已经使用循环解决了这个问题。如下所示:
advIdx = 0
for n in range(df1.shape[0]):
for m in range(advIdx, df2.shape[0]):
if df1['time_ref'][n] == df2['time'][m]:
df1.loc[n, 'monthlyObjAvg'] = df2.loc[m, 'sale'] / df2.loc[m, 'nbr']
advIdx = m
break
使用向前移动索引(因为旧时间不再相关),甚至可以将复杂性从n*m降低到大约n+m。然而,即使有了如此巨大的改进,将循环解决方案应用于10,000-1,000,000+行的数据集仍然需要几秒钟甚至几分钟的时间来运行,这意味着它仍然需要一个适当的矢量化解决方案。
虽然花了一些时间,但我最终弄清楚了熊猫函数是如何模拟条件参数的。虽然它不能非常直接地完成,但pandas.merge
基本上实现了上面的SQL语句。您必须重命名将应用"join"/"merge"的列,即df2。时间:
df2.rename(columns = {'time':'time_ref'}, inplace=True)
然后应用左侧连接:
df1 = pd.merge(df1, df2, how='left', on=['time_ref'])
最后创建目标列并删除其余部分:
df1['monthlyObjAvg'] = df1['sale']/df2['nbr']
df1.drop(["time_ref", "sale", "nbr"], axis=1, inplace=True)
这正确地产生了我所寻找的闪电般的快速解决方案(在50,000+样本上以毫秒范围运行),但它仍然看起来有点不优雅。我想把它留在这里,作为一个浮标,供未来像我一样的人想知道这个,使用这些搜索词找不到什么,因为它是毕竟是一个合适的解决方案。
如果有人能提供一种更优雅和直观的方法来做到这一点(例如,直接将分数投影到满足条件的地方,而不是复制所有的分数,然后计算,然后删除副本),那么欢迎这样做。