我正在寻找一种内存有效的方法来在pyspark DF中使用一个函数创建一个列,该函数将来自“邻近”的参数值(即,当数据集已在特定列上排序时)记录,是特定哈希的排列。
也就是说,对于一个特定的数据帧,它有几个'hash_n'(最多~5个)列,我希望对每个哈希列进行排序,并根据'this中的哈希函数创建一个新列'列,以及下几个(最多~15)列。该函数实质上是比较散列的“相似性”,如果相似性高于某个阈值,则附加“其他”列的“索引”。
最初我使用窗口函数和pyspark UDF执行此操作,但遇到outOfMemory问题,我现在转换为RDD,通过递增索引创建“附近”RDD的字典,合并生成的字典值,并通过reduceByKey将该函数应用于union。这种方法似乎是一种改进,虽然我仍然遇到内存问题(由于执行程序使用了太多内存导致的纱线杀死容器;已经尝试了几种设置而无法解决这个问题)。
这是我正在使用的代码的相关部分(它稍微复杂一点,因为它必须允许单个列进行分区的可能性(就数据的处理方式而言 - 我们只比较哈希,如果它们在同一个分区中)数据,'partnCol',或者相同的列表,'partnCols';实际上我们总是至少有一个分区列);这里的参数是nPerms,哈希值被置换的次数(因此我们总共有nPerms + 1个哈希列),B是要匹配的相邻记录的数量。
HSHTBLDict = {}; rdd0Dict = {}; rddDictDict = {}
possMtchsDict = {}; mtchsDict = {}
for nn in range(nPerms+1):
if (partnCol):
HSHTBLDict[nn] = _addNamedDFIndex(HSHTBL.orderBy\
(partnCol, 'hash_{0}'.format(nn)), 'newIdx')
rdd0Dict[nn] = HSHTBLDict[nn].select('newIdx', partnCol, '__index__', \
'hash_{0}'.format(nn))\
.orderBy(partnCol, 'hash_{0}'.format(nn)).rdd.map(tuple)\
.map(lambda kv: ((kv[0], kv[1]), (kv[2:])))
elif (partnCols):
HSHTBLDict[nn] = _addNamedDFIndex(HSHTBL.orderBy(\
*(partnCols+['hash_{0}'.format(nn)])), 'newIdx')
rdd0Dict[nn] = HSHTBLDict[nn].select('newIdx', \
*(partnCols+['__index__', 'hash_{0}'.format(nn)]))\
.orderBy(*(partnCols+['hash_{0}'.format(nn)])).rdd.map(tuple)\
.map(lambda kv: ((kv[:len(partnCols)+1]), \
(kv[len(partnCols)+1:])))
else:
HSHTBLDict[nn] = _addNamedDFIndex(HSHTBL.orderBy(\
'hash_{0}'.format(nn)), 'newIdx')
rdd0Dict[nn] = HSHTBLDict[nn].select('newIdx', '__index__', \
'hash_{0}'.format(nn))\
.orderBy('hash_{0}'.format(nn)).rdd.map(tuple)\
.map(lambda kv: ((kv[0], ), (kv[1:])))
rddDictDict[nn] = {}
for b in range(1, B+1):
def funcPos(r, b=b):
return r.map(lambda kv: (tuple([kv[0][x] + b if x==0 else kv[0][x] \
for x in range(len(kv[0]))]), kv[1]))
rddDictDict[nn][b] = funcPos(rdd0Dict[nn])
possMtchsDict[nn] = sc.union([rdd0Dict[nn]] + rddDictDict[nn].values())\
.reduceByKey(lambda x,y: x+y, numPartitions=rddParts)\
.mapValues(lambda v: tuple(v[i:i+2] \
for i in range(0, len(v), 2)))
mtchsDict[nn] = possMtchsDict[nn].mapValues(lambda v: tuple([ tuple([ v[x][0], \
[p[0] for p in v if _hashSim(v[x][1], p[1]) > thr]])\
for x in range(len(v)) ]) )
# union together all combinations from all hash columns
Mtchs = sc.union(mtchsDict.values())
# map to rdd of (__index__, matchList) pairs
mls = Mtchs.flatMap(lambda kv: kv[1]).reduceByKey(lambda x,y: list(set(x+y)), \
numPartitions=rddParts)
如果有人有任何建议/意见,我们将很乐意倾听他们的意见。
我确实尝试减少每个执行程序的核心数量,我发现虽然这个过程(相对非常)很慢但我可以完成它,尽管我还没有找到适用于全范围的设置组合我希望实现的nPerms / B。可能有必要重新编写代码/方法以便这样做。我还想更好地理解确定像shuffle这样的东西允许纱线的记忆量的方式。我发现减少--executor-memory和增加--conf spark.yarn.executor.memoryOverhead似乎有所帮助,但我仍然不清楚如何计算可用的内存量。