【发布时间】:2018-11-09 21:07:54
【问题描述】:
您好,我正在尝试为迭代问题找到矢量化(或更有效)的解决方案,其中我找到的唯一解决方案需要对具有多个循环的 DataFrame 进行逐行迭代。实际的数据文件很大,所以我目前的解决方案实际上是不可行的。如果您想看一下,我在最后包含了线分析器输出。真正的问题是相当复杂的,所以我将尝试用一个简单的例子来解释这个问题(我花了很长时间来简化它:)):
假设我们的机场有两个并排的着陆跑道。每架飞机降落(到达时间),在其中一个跑道上滑行一段时间,然后起飞(起飞时间)。一切都存储在 Pandas DataFrame 中,按到达时间排序,如下所示(请参阅 EDIT2 以获得更大的数据集进行测试):
PLANE STRIP ARRIVAL DEPARTURE
0 1 85.00 86.00
1 1 87.87 92.76
2 2 88.34 89.72
3 1 88.92 90.88
4 2 90.03 92.77
5 2 90.27 91.95
6 2 92.42 93.58
7 2 94.42 95.58
寻找两种情况的解决方案:
1. 建立一个事件列表,其中一次在一个带上存在多个平面。不包括事件的子集(例如,如果存在有效的 [3,4,5] 案例,则不显示 [3,4])。该列表应存储实际 DataFrame 行的索引。请参阅函数 findSingleEvents() 了解这种情况的解决方案(运行大约 5 毫秒)。
2. 建立一个事件列表,其中每个条带上一次至少有一个平面。不计算事件的子集,仅记录具有最大平面数的事件。 (例如,如果存在 [3,4,5] 情况,则不显示 [3,4])。不要计算在单个条带上完全发生的事件。该列表应存储实际 DataFrame 行的索引。请参阅函数 findMultiEvents() 了解这种情况的解决方案(运行大约 15 毫秒)。
工作代码:
import numpy as np
import pandas as pd
import itertools
from __future__ import division
data = [{'PLANE':0, 'STRIP':1, 'ARRIVAL':85.00, 'DEPARTURE':86.00},
{'PLANE':1, 'STRIP':1, 'ARRIVAL':87.87, 'DEPARTURE':92.76},
{'PLANE':2, 'STRIP':2, 'ARRIVAL':88.34, 'DEPARTURE':89.72},
{'PLANE':3, 'STRIP':1, 'ARRIVAL':88.92, 'DEPARTURE':90.88},
{'PLANE':4, 'STRIP':2, 'ARRIVAL':90.03, 'DEPARTURE':92.77},
{'PLANE':5, 'STRIP':2, 'ARRIVAL':90.27, 'DEPARTURE':91.95},
{'PLANE':6, 'STRIP':2, 'ARRIVAL':92.42, 'DEPARTURE':93.58},
{'PLANE':7, 'STRIP':2, 'ARRIVAL':94.42, 'DEPARTURE':95.58}]
df = pd.DataFrame(data, columns = ['PLANE','STRIP','ARRIVAL','DEPARTURE'])
def findSingleEvents(df):
events = []
for row in df.itertuples():
#Create temporary dataframe for each main iteration
dfTemp = df[(row.DEPARTURE>df.ARRIVAL) & (row.ARRIVAL<df.DEPARTURE)]
if len(dfTemp)>1:
#convert index values to integers from long
current_event = [int(v) for v in dfTemp.index.tolist()]
#loop backwards to remove elements that do not comply
for i in reversed(current_event):
if (dfTemp.loc[i].ARRIVAL > dfTemp.DEPARTURE).any():
current_event.remove(i)
events.append(current_event)
#remove duplicate events
events = map(list, set(map(tuple, events)))
return events
def findMultiEvents(df):
events = []
for row in df.itertuples():
#Create temporary dataframe for each main iteration
dfTemp = df[(row.DEPARTURE>df.ARRIVAL) & (row.ARRIVAL<df.DEPARTURE)]
if len(dfTemp)>1:
#convert index values to integers from long
current_event = [int(v) for v in dfTemp.index.tolist()]
#loop backwards to remove elements that do not comply
for i in reversed(current_event):
if (dfTemp.loc[i].ARRIVAL > dfTemp.DEPARTURE).any():
current_event.remove(i)
#remove elements only on 1 strip
if len(df.iloc[current_event].STRIP.unique()) > 1:
events.append(current_event)
#remove duplicate events
events = map(list, set(map(tuple, events)))
return events
print findSingleEvents(df[df.STRIP==1])
print findSingleEvents(df[df.STRIP==2])
print findMultiEvents(df)
验证输出:
[[1, 3]]
[[4, 5], [4, 6]]
[[1, 3, 4, 5], [1, 4, 6], [1, 2, 3]]
显然,这些既不是高效也不是优雅的解决方案。使用我拥有的巨大 DataFrame,运行它可能需要几个小时。我考虑了很长时间的矢量化方法,但无法提出任何可靠的方法。欢迎任何指示/帮助!我也对基于 Numpy/Cython/Numba 的方法持开放态度。
谢谢!
PS:如果你想知道我将如何处理这些列表:我将为每个 EVENT 分配一个 EVENT 编号,并通过合并上述数据构建一个单独的数据库,然后EVENT 数字作为单独的列,用于其他内容。对于案例 1,它看起来像这样:
EVENT PLANE STRIP ARRIVAL DEPARTURE
0 4 2 90.03 92.77
0 5 2 90.27 91.95
1 5 2 90.27 91.95
1 6 2 92.42 95.58
编辑:修改了代码和测试数据集。
EDIT2: 使用下面的代码生成一个 1000 行(或更多)长的 DataFrame 用于测试目的。 (根据@ImportanceOfBeingErnest 的建议)
import random
import pandas as pd
import numpy as np
data = []
for i in range(1000):
arrival = random.uniform(0,1000)
departure = arrival + random.uniform(2.0, 10.0)
data.append({'PLANE':i, 'STRIP':random.randint(1, 2),'ARRIVAL':arrival,'DEPARTURE':departure})
df = pd.DataFrame(data, columns = ['PLANE','STRIP','ARRIVAL','DEPARTURE'])
df = df.sort_values(by=['ARRIVAL'])
df = df.reset_index(drop=True)
df.PLANE = df.index
EDIT3:
已接受答案的修改版本。接受的答案无法删除事件的子集。修改后的版本满足规则“(例如,如果存在有效的 [3,4,5] 情况,则不显示 [3,4])”
def maximal_subsets_modified(sets):
sets.sort()
maximal_sets = []
s0 = frozenset()
for s in sets:
if not (s > s0) and len(s0) > 1:
not_in_list = True
for x in maximal_sets:
if set(x).issubset(set(s0)):
maximal_sets.remove(x)
if set(s0).issubset(set(x)):
not_in_list = False
if not_in_list:
maximal_sets.append(list(s0))
s0 = s
if len(s0) > 1:
not_in_list = True
for x in maximal_sets:
if set(x).issubset(set(s0)):
maximal_sets.remove(x)
if set(s0).issubset(set(x)):
not_in_list = False
if not_in_list:
maximal_sets.append(list(s0))
return maximal_sets
def maximal_subsets_2_modified(sets, d):
sets.sort()
maximal_sets = []
s0 = frozenset()
for s in sets:
if not (s > s0) and len(s0) > 1 and d.loc[list(s0), 'STRIP'].nunique() == 2:
not_in_list = True
for x in maximal_sets:
if set(x).issubset(set(s0)):
maximal_sets.remove(x)
if set(s0).issubset(set(x)):
not_in_list = False
if not_in_list:
maximal_sets.append(list(s0))
s0 = s
if len(s0) > 1 and d.loc[list(s), 'STRIP'].nunique() == 2:
not_in_list = True
for x in maximal_sets:
if set(x).issubset(set(s0)):
maximal_sets.remove(x)
if set(s0).issubset(set(x)):
not_in_list = False
if not_in_list:
maximal_sets.append(list(s0))
return maximal_sets
# single
def hal_3_modified(d):
sets = np.apply_along_axis(
lambda x: frozenset(d.PLANE.values[(d.PLANE.values <= x[0]) & (d.DEPARTURE.values > x[2])]),
1, d.values
)
return maximal_subsets_modified(sets)
# multi
def hal_5_modified(d):
sets = np.apply_along_axis(
lambda x: frozenset(d.PLANE.values[(d.PLANE.values <= x[0]) & (d.DEPARTURE.values > x[2])]),
1, d.values
)
return maximal_subsets_2_modified(sets, d)
【问题讨论】:
-
如果您正在寻找效率,我会考虑另一种数据结构,不过,这里有一些真正的
pandas向导,也许他们可以建议一些在 pandas 中表现的东西 -
请注意,您正在使用最糟糕的方式来迭代数据帧。使用
itertuples(),迭代索引并使用基于.loc的索引将会超级慢。 -
@juanpa.arrivillaga 我很清楚效率低下 :) 我还查看了
itertuples()和iterrows(),但我不知道如何实现所需的嵌套循环。跨度> -
@juanpa.arrivillaga 结果证明我基于
iloc的低效迭代无论如何都没有正确设置。我需要先解决这个问题。 -
它们的范围从 0 到 770 万。它们是 3 个月期间的秒数,在数据收集期开始时初始化为 0 秒。
标签: python pandas loops dataframe vectorization