假设我们有一个100.000行和3列的数据帧,结构如下:
| visitorId | timestamp | url |
1 | 1 | 11 | A |
2 | 1 | 12 | B |
3 | 2 | 21 | A |
4 | 3 | 31 | A |
5 | 3 | 32 | C |
.
.
n | z | Z1 | A |
此数据框始终排序,并存储在名为sortedData
的变量中。首先,我们将所有唯一身份访问者ID提取到名为visitors
的列表中,并创建一个路径变量来保存此访问者访问过的所有网址。
visitors = sortedData.visitorId.unique()
visitors = visitors.tolist()
paths = []
visitor_length = len(visitors)
现在我所做的是为每个访问者进入一个循环,以便查找并存储每个访问者的遍历路径,他们的时间戳和ID,并将其用作辅助算法(不相关)中的输入。 我有两种方法可以做到这一点:
A:
for visitor in visitors:
dataResult = sortedData.loc[sortedData['visitorId'] == visitor]
timestamps = dataResult.timestamp.tolist()
path = dataResult.pageUrl.tolist()
paths.append((visitor, timestamps, path))
sortedData = sortedData.iloc[len(dataResult):]
这使用内置的pandas loc
函数来查找匹配visitorId
的行,并将时间戳和路径提取到列表中,最后将它们一起追加。然后继续删除前x行(等于查询结果的长度,即匹配数),以便在进行类似匹配时不会在将来遍历它们。
对visitor
列表中的每个唯一visitors
重复此过程。
通过计时,我发现大约需要6.31
秒才能完成。在我的例子中,这是一个11.7MB的文件,100.000行。在1.2GB的文件上,这可以扩展到14个小时。因此,我试图B希望加速。
方式B使用数据始终排序的逻辑,因为visitors
中的访问者1将始终是sortedData
中的第一个访问者,访问者2中的第二个访客等等。所以我可以使用大熊猫value_counts()
功能可以计算当前访问者的x
次出现次数,只需使用x
从第一行head(x)
行中提取数据,因为它们始终匹配。这样,它不必每次都迭代并搜索整个数据帧。然后和以前一样,我从数据框中删除这些行,并为下一个visitor
重复循环。
B:
for visitor in visitors:
x = sortedData.visitorId.value_counts()[visitor]
timestamps = sortedData.timestamp.head(x).tolist()
path = sortedData.pageUrl.head(x).tolist()
paths.append((visitor, timestamps, path))
sortedData = sortedData.iloc[x:]
令我惊讶的是,与来自A的10.89
相比,它的运行速度几乎是6.31
秒的两倍。
定时loc
和value_counts()
看起来后者更快,但是当在循环中使用时则相反。
考虑到B,我们知道访问者的位置,我们只需要遍历数据帧的前x行,而在A中我们每次都必须搜索整个数据帧,是什么导致了这种性能差异?
在之前的优化过程中,删除已经遍历的行,加速比较大,每次数据帧大小减半时加倍,而不是整数。这让我怀疑它每次都以A方式遍历整个数据帧,除非我遗漏了什么?
我正在使用在PyCharm 2018上运行的MacBook Pro 2014,Python 3.6.4(Anaconda)。
答案 0 :(得分:1)
创建自己的访问者列表,迭代这些访问者并反复搜索数据框架并不理想。
如果我能正确理解您的问题,请查看可以在您的情况下使用的groupby。
要使代码与您现有的代码类似,您可以这样开始:
grouped = sortedData.groupby('visitorId')
for visitorId, group in grouped:
print(vistorId)
# your code here
custom_url_algorithm(group)