我有一个数据框c
,其中包含许多不同的列。另外,arr
是与c
:arr = c[c['A_D'] == 'A']
的子集相对应的数据帧。
我的代码的主要思想是遍历c
数据框中的所有行,并搜索应该发生某些特定条件的所有可能情况(在arr
数据框中):>
c['A_D'] == D
和c['Already_linked'] == 0
的行hour
数据框中的arr
必须小于hour_aux
数据框中的c
Already_linked
数据帧的列arr
必须为零:arr.Already_linked == 0
Terminal
数据帧中,Operator
和arr
必须相同现在,条件使用布尔索引和groupby get_group进行存储:
arr
数据帧分组以选择相同的操作员和终端:g = groups.get_group((row.Operator, row.Terminal
))c
数据帧中的小时并且到达Already_linked == 0的到达:vb = g[(g.Already_linked==0) & (g.hour<row.hour_aux)]
对于c
数据框中验证所有条件的每一行,都会创建一个vb
数据框。自然地,该数据帧在每次迭代中具有不同的长度。创建vb
数据帧之后,我的目标是选择vb
数据帧的索引,以最小化vb.START
和c [{x
]之间的时间。然后,与此索引对应的FightID
存储在列c
的{{1}}数据框中。此外,由于到达已与出发相关联,因此a
数据框中的列Already_linked
从0更改为1。
请务必注意,arr
数据框的列Already_linked
可能在每次迭代中都发生变化(并且arr
是创建arr.Already_linked == 0
数据框的条件之一)。因此,无法并行处理此代码。
我已经使用vb
来提高效率,但是由于c.itertuples()
具有数百万行,因此此代码仍然很耗时。
其他选择还可以是对每一行使用c
。但是,这并不是很简单,因为在每个循环中,pd.apply
和c
中的值都发生变化(而且,我相信即使使用arr
也会非常慢)。 / p>
是否有可能在向量化解决方案中将此for循环转换(或将运行时间减少10倍(如果可能,甚至更多))?
初始数据框:
pd.apply
所需的输出(下面的数据框中不包括某些列。仅START END A_D Operator FlightID Terminal TROUND_ID tot
0 2017-03-26 16:55:00 2017-10-28 16:55:00 A QR QR001 4 QR002 70
1 2017-03-26 09:30:00 2017-06-11 09:30:00 D DL DL001 3 " " 84
2 2017-03-27 09:30:00 2017-10-28 09:30:00 D DL DL001 3 " " 78
3 2017-10-08 15:15:00 2017-10-22 15:15:00 D VS VS001 3 " " 45
4 2017-03-26 06:50:00 2017-06-11 06:50:00 A DL DL401 3 " " 9
5 2017-03-27 06:50:00 2017-10-28 06:50:00 A DL DL401 3 " " 19
6 2017-03-29 06:50:00 2017-04-19 06:50:00 A DL DL401 3 " " 3
7 2017-05-03 06:50:00 2017-10-25 06:50:00 A DL DL401 3 " " 32
8 2017-06-25 06:50:00 2017-10-22 06:50:00 A DL DL401 3 " " 95
9 2017-03-26 07:45:00 2017-10-28 07:45:00 A DL DL402 3 " " 58
和a
列是相关的)
Already_linked
代码:
START END A_D Operator a Already_linked
0 2017-03-26 16:55:00 2017-10-28 16:55:00 A QR 0 1
1 2017-03-26 09:30:00 2017-06-11 09:30:00 D DL DL402 1
2 2017-03-27 09:30:00 2017-10-28 09:30:00 D DL DL401 1
3 2017-10-08 15:15:00 2017-10-22 15:15:00 D VS No_link_found 0
4 2017-03-26 06:50:00 2017-06-11 06:50:00 A DL 0 0
5 2017-03-27 06:50:00 2017-10-28 06:50:00 A DL 0 1
6 2017-03-29 06:50:00 2017-04-19 06:50:00 A DL 0 0
7 2017-05-03 06:50:00 2017-10-25 06:50:00 A DL 0 0
8 2017-06-25 06:50:00 2017-10-22 06:50:00 A DL 0 0
9 2017-03-26 07:45:00 2017-10-28 07:45:00 A DL 0 1
初始groups = arr.groupby(['Operator', 'Terminal'])
for row in c[(c.A_D == "D") & (c.Already_linked == 0)].itertuples():
try:
g = groups.get_group((row.Operator, row.Terminal))
vb = g[(g.Already_linked==0) & (g.hour<row.hour_aux)]
aux = (vb.START - row.x).abs().idxmin()
c.loc[row.Index, 'a'] = vb.loc[aux].FlightID
arr.loc[aux, 'Already_linked'] = 1
continue
except:
continue
c['Already_linked'] = np.where((c.a != 0) & (c.a != 'No_link_found') & (c.A_D == 'D'), 1, c['Already_linked'])
c.Already_linked.loc[arr.Already_linked.index] = arr.Already_linked
c['a'] = np.where((c.Already_linked == 0) & (c.A_D == 'D'),'No_link_found',c['a'])
数据帧的代码:
c
答案 0 :(得分:3)
您的问题是,是否有一种向量化for循环的方法,但是我认为这个问题隐藏了您真正想要的东西,这是一种加速代码的简便方法。对于性能问题,始终要进行概要分析。但是,我强烈怀疑代码中的主要操作是.query(row.query_string)
。如果arr
很大,则为每一行运行该代码将很昂贵。
对于任意查询,如果不消除迭代之间的依赖性并并行化昂贵的步骤,则根本无法真正改善运行时间。不过,您可能会更幸运。您的查询字符串始终检查两个不同的列,以查看它们是否等于您关心的内容。但是,对于需要遍历整个arr
的每一行。由于切片每次都会更改,因此可能会引起问题,但是这里有一些建议:
arr
,因此请仅维护arr.Already_Linked==0
行的视图,以便遍历较小的对象。arr
和Terminal
对Operator
进行分组。然后,而不是遍历所有arr
,首先选择所需的组,然后进行切片和过滤。这将需要重新考虑query_string
的确切实现,但是这样做的好处是,如果您有许多终端和运算符,则通常要处理比arr
小得多的对象。而且,您甚至不必查询该对象,因为那是由groupby隐式完成的。aux.hour
通常与row.hour_aux
的关系,您可以通过在开头将aux
相对于hour
进行排序来进行改进。仅仅使用不等式运算符,您可能看不到任何收益,但您可以将其与对数搜索边界点配对,然后将其切成该边界点。arr
上为每一行进行的查询,都将获得比转换框架或向量化更多的收益点点滴滴。稍微扩展其中的一些内容,并稍微修改@DJK的代码,看看当我们进行以下更改时会发生什么。
groups = arr.groupby(['Operator', 'Terminal'])
for row in c[(c.A_D == 'D') & (c.Already_linked == 0)].itertuples():
g = groups.get_group((row.Operator, row.Terminal))
vb = g[(g.Already_linked==0) & (g.hour<row.hour_aux)]
try:
aux = (vb.START - row.x).abs().idxmin()
print(row.x)
c.loc[row.Index, 'a'] = vb.loc[aux,'FlightID']
g.loc[aux, 'Already_linked'] = 1
continue
except:
continue
查询如此之慢的部分原因是因为它每次都在arr
的所有内容中进行搜索。相反,.groupby()
的执行时间几乎与一个查询相同,但是对于以后的每次迭代,您只需使用.get_group()
即可高效地找到数据的一小部分你在乎。
基准测试时的一个有用的(极其粗略的)经验法则是十亿个事物需要一秒钟。如果您看到的时间要比以百万计的事物(例如数百万行)所度量的时间长得多,则意味着对于每一行,您要进行大量的工作来完成数十亿次操作。这就为更好的算法减少操作数量留下了巨大的潜力,而矢量化实际上只能带来恒定的因子改进(对于许多字符串/查询操作,甚至还没有很大的改进)。
答案 1 :(得分:3)
虽然这不是一个专门的解决方案,但是如果您的样本数据集模仿您的真实数据集,它应该可以加快速度。当前,您正在浪费时间遍历每一行,但是您只关心遍历['A_D'] == 'D'
和['Already_linked'] ==0
的行。取而代之的是删除if,然后在截短的数据帧(仅占初始数据帧的30%)上循环
for row in c[(c.A_D == 'D') & (c.Already_linked == 0)].itertuples():
vb = arr[(arr.Already_linked == 0) & (arr.hour < row.hour_aux)].copy().query(row.query_string)
try:
aux = (vb.START - row.x).abs().idxmin()
print(row.x)
c.loc[row.Index, 'a'] = vb.loc[aux,'FlightID']
arr.loc[aux, 'Already_linked'] = 1
continue
except:
continue