簡體   English   中英

Apache Beam-Python:如何通過累積獲取PCollection的前10個元素?

[英]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.pytest_combine.py道代碼。 要運行此文件,您可以將文件放在正確的文件夾中,如果需要,請安裝Beam SDK 2.13.0,修改generate_messages.pytest_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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM