我想这样提取前10名最高得分:
Paul - 38
Michel - 27
Hugo - 27
Bob - 24
Kevin - 19
...
(10 elements)
我正在使用固定的窗口和数据驱动的触发器,该触发器在窗格收集了X个元素之后输出早期结果。 另外,我正在使用组合器获得最高的10个最高得分。
(inputs
| 'Apply Window of time' >> beam.WindowInto(
beam.window.FixedWindows(size=5 * 60))
trigger=trigger.Repeatedly(trigger.AfterCount(2)),
accumulation_mode=trigger.AccumulationMode.ACCUMULATING)
| beam.ParDo(PairWithOne()) # ('key', 1)
| beam.CombinePerKey(sum)
| 'Top 10 scores' >> beam.CombineGlobally(
beam.combiners.TopCombineFn(n=10,
compare=lambda a, b: a[1] < b[
1])).without_defaults())
这里的问题是,第一个输出似乎是正确的,但随后的输出包含类似的重复键:
Paul - 38
Paul - 37
Michel - 27
Paul - 36
Michel - 26
Kevin - 20
...
(10 elements)
如您所见,我没有得到10个不同的K / V对,但是按键重复。
当不使用触发/累加策略时,此方法效果很好。.但是,如果我希望有2个小时的窗口,我想经常进行更新...
答案 0 :(得分:1)
如评论中所述,一种可能性是过渡到Discarding fired panes,可以通过accumulation_mode=trigger.AccumulationMode.DISCARDING
进行设置。如果仍要保持ACCUMULATING
模式,则可能需要修改TopCombineFn
,以使来自同一用户的重复窗格会覆盖以前的值,并避免重复的键。 TopDistinctFn
将以Beam SDK 2.13.0的代码here为基础。在add_input
方法中,我们将进行如下检查:
for current_top_element in enumerate(heap):
if element[0] == current_top_element[1].value[0]:
heap[current_top_element[0]] = heap[-1]
heap.pop()
heapq.heapify(heap)
基本上,我们将要评估的元素(element[0]
)与堆中的每个元素的键进行比较。堆元素的类型为ComparableValue
,因此我们可以使用value
来获取元组(和value[0]
来获取键)。如果它们匹配,我们将要从堆中弹出它(因为我们累加总和会更大)。 Beam SDK使用heapq
库,因此我基于this answer的方法删除了i-th
元素(我们使用enumerate
来保留索引信息)。
我添加了一些日志记录以帮助检测重复项:
logging.info("Duplicate: " + element[0] + "," + str(element[1]) + ' --- ' + current_top_element[1].value[0] + ',' + str(current_top_element[1].value[1]))
该代码位于top.py
文件夹(带有combiners
)的__init__.py
文件中,我使用以下命令导入它:
from combiners.top import TopDistinctFn
然后,我可以在管道中使用TopDistinctFn
,如下所示:
(inputs
| 'Add User as key' >> beam.Map(lambda x: (x, 1)) # ('key', 1)
| 'Apply Window of time' >> beam.WindowInto(
beam.window.FixedWindows(size=10*60),
trigger=beam.trigger.Repeatedly(beam.trigger.AfterCount(2)),
accumulation_mode=beam.trigger.AccumulationMode.ACCUMULATING)
| 'Sum Score' >> beam.CombinePerKey(sum)
| 'Top 10 scores' >> beam.CombineGlobally(
TopDistinctFn(n=10, compare=lambda a, b: a[1] < b[1])).without_defaults()
| 'Print results' >> beam.ParDo(PrintTop10Fn()))
完整代码可在here中找到。 generate_messages.py
是发布/订阅消息生成器,top.py
包含已重命名为TopCombineFn
的{{1}}的自定义版本(可能看起来不知所措,但我仅从第几行开始添加了几行代码425)和TopDistinctFn
主管道代码。要运行此文件,可以将文件放在正确的文件夹中,如果需要,请安装Beam SDK 2.13.0,在test_combine.py
和generate_messages.py
中修改项目ID和Pub / Sub主题。然后,开始使用test_combine-py
发布消息,并在另一个shell中使用python generate_messages.py
运行管道:DirectRunner
。使用python test_combine.py --streaming
,您可能需要在DataflowRunner
文件中添加extra files。
例如,setup.py
领先9分,当下一次更新时,他的得分高达11分。他将出现在下一个摘要中,仅包含更新的得分,没有重复(如在我们的日志记录中检测到的)。 9点的条目将不会出现,并且顶部仍然会根据需要保留10个用户。 Bob
也是如此。我注意到,即使得分排在前10位,得分也仍然会出现在堆中,但是我不确定Marta
的垃圾回收如何工作。
heapq
让我知道这对您的用例是否也很好。