![](/img/trans.png)
[英]How to create groups of N elements from a PCollection Apache Beam Python
[英]Apache Beam - Python : How to get the top 10 elements of a PCollection with Accumulation?
我想像這樣提取前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個小時的窗口,我想經常更新...
正如評論中所討論的,一種可能性是過渡到“ 丟棄已觸發的窗格” ,可以通過accumulation_mode=trigger.AccumulationMode.DISCARDING
進行設置。 如果仍要保持“ ACCUMULATING
模式,則可能需要修改TopCombineFn
以便同一用戶重復的窗格會覆蓋以前的值,並避免重復的鍵。 TopDistinctFn
將作為基礎代碼這里為梁SDK 2.13.0。 在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]
( element[0]
)和堆中每個元素的鍵。 堆元素的類型為ComparableValue
因此我們可以使用value
來獲取元組(而使用value[0]
來獲取鍵)。 如果它們匹配,我們將要從堆中彈出它(因為我們累加總和會更大)。 Beam SDK使用heapq
庫,因此我基於此答案來刪除第i-th
元素(我們使用enumerate
來保留索引信息)。
我添加了一些日志記錄,以幫助檢測重復項:
logging.info("Duplicate: " + element[0] + "," + str(element[1]) + ' --- ' + current_top_element[1].value[0] + ',' + str(current_top_element[1].value[1]))
代碼位於combiners
文件夾(帶有__init__.py
)中的top.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()))
完整的代碼可以在這里找到。 generate_messages.py
是發布/ top.py
消息生成器, top.py
包含自定義版本的TopCombineFn
重命名為TopDistinctFn
(可能看起來不知所措,但我僅從425行開始添加了幾行代碼),而test_combine.py
是test_combine.py
道代碼。 要運行此文件,您可以將文件放在正確的文件夾中,如果需要,請安裝Beam SDK 2.13.0,修改generate_messages.py
和test_combine-py
項目ID和Pub / Sub主題。 然后,開始使用python generate_messages.py
發布消息,並在另一個shell中使用DirectRunner
運行管道: python test_combine.py --streaming
。 隨着DataflowRunner
你可能需要添加額外的文件與setup.py
文件。
舉個例子, Bob
以9分領先,當下一次更新時,他的得分高達11分。 他將出現在下一個摘要中,僅包含更新的得分,沒有重復(如在我們的日志記錄中檢測到的)。 9點的條目將不會出現,並且頂部仍然會根據需要保留10個用戶。 Marta
。 我注意到,即使不在前10名中,較舊的分數仍會出現在堆中,但是我不確定垃圾收集如何與heapq
。
INFO:root:>>> Current top 10: [('Bob', 9), ('Connor', 8), ('Eva', 7), ('Hugo', 7), ('Paul', 6), ('Kevin', 6), ('Laura', 6), ('Marta', 6), ('Diane', 4), ('Bacon', 4)]
...
INFO:root:Duplicate: Marta,8 --- Marta,6
INFO:root:Duplicate: Bob,11 --- Bob,9
INFO:root:>>> Current top 10: [('Bob', 11), ('Connor', 8), ('Marta', 8), ('Bacon', 7), ('Eva', 7), ('Hugo', 7), ('Paul', 6), ('Laura', 6), ('Diane', 6), ('Kevin', 6)]
讓我知道這是否也適用於您的用例。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.