You are viewing a plain text version of this content. The canonical link for it is here.
Posted to github@beam.apache.org by "hjtran (via GitHub)" <gi...@apache.org> on 2023/10/23 16:01:50 UTC

[PR] Implement a `Top` partitioner [beam]

hjtran opened a new pull request, #29106:
URL: https://github.com/apache/beam/pull/29106

   This diff implements a new `parittioners` module which includes a `Top` partitioner. `Top` replicates the `combiners.Top` behavior.
   
   This is a WIP. I initially implemented this for my particular case which is global-window-only, but I should probably go back through this and add API for `Smallest`, `Largest`, `PerKey` etc. Posting this early though in case anyone has any comments
   
   GitHub Actions Tests Status (on master branch)
   ------------------------------------------------------------------------------------------------
   [![Build python source distribution and wheels](https://github.com/apache/beam/workflows/Build%20python%20source%20distribution%20and%20wheels/badge.svg?branch=master&event=schedule)](https://github.com/apache/beam/actions?query=workflow%3A%22Build+python+source+distribution+and+wheels%22+branch%3Amaster+event%3Aschedule)
   [![Python tests](https://github.com/apache/beam/workflows/Python%20tests/badge.svg?branch=master&event=schedule)](https://github.com/apache/beam/actions?query=workflow%3A%22Python+Tests%22+branch%3Amaster+event%3Aschedule)
   [![Java tests](https://github.com/apache/beam/workflows/Java%20Tests/badge.svg?branch=master&event=schedule)](https://github.com/apache/beam/actions?query=workflow%3A%22Java+Tests%22+branch%3Amaster+event%3Aschedule)
   [![Go tests](https://github.com/apache/beam/workflows/Go%20tests/badge.svg?branch=master&event=schedule)](https://github.com/apache/beam/actions?query=workflow%3A%22Go+tests%22+branch%3Amaster+event%3Aschedule)
   
   See [CI.md](https://github.com/apache/beam/blob/master/CI.md) for more information about GitHub Actions CI or the [workflows README](https://github.com/apache/beam/blob/master/.github/workflows/README.md) to see a list of phrases to trigger workflows.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: github-unsubscribe@beam.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Implement a `Top` partitioner [beam]

Posted by "codecov-commenter (via GitHub)" <gi...@apache.org>.
codecov-commenter commented on PR #29106:
URL: https://github.com/apache/beam/pull/29106#issuecomment-2081471962

   ## [Codecov](https://app.codecov.io/gh/apache/beam/pull/29106?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) Report
   Attention: Patch coverage is `0%` with `33 lines` in your changes are missing coverage. Please review.
   > Project coverage is 38.36%. Comparing base [(`38720a3`)](https://app.codecov.io/gh/apache/beam/commit/38720a39d91db1853741f86d29819bf5a5d59a36?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) to head [(`206042e`)](https://app.codecov.io/gh/apache/beam/pull/29106?dropdown=coverage&src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache).
   > Report is 1812 commits behind head on master.
   
   | [Files](https://app.codecov.io/gh/apache/beam/pull/29106?dropdown=coverage&src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | Patch % | Lines |
   |---|---|---|
   | [sdks/python/apache\_beam/transforms/partitioners.py](https://app.codecov.io/gh/apache/beam/pull/29106?src=pr&el=tree&filepath=sdks%2Fpython%2Fapache_beam%2Ftransforms%2Fpartitioners.py&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache#diff-c2Rrcy9weXRob24vYXBhY2hlX2JlYW0vdHJhbnNmb3Jtcy9wYXJ0aXRpb25lcnMucHk=) | 0.00% | [33 Missing :warning: ](https://app.codecov.io/gh/apache/beam/pull/29106?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) |
   
   <details><summary>Additional details and impacted files</summary>
   
   
   ```diff
   @@             Coverage Diff             @@
   ##           master   #29106       +/-   ##
   ===========================================
   - Coverage   72.22%   38.36%   -33.87%     
   ===========================================
     Files         684      687        +3     
     Lines      100856   101693      +837     
   ===========================================
   - Hits        72846    39010    -33836     
   - Misses      26434    61107    +34673     
     Partials     1576     1576               
   ```
   
   | [Flag](https://app.codecov.io/gh/apache/beam/pull/29106/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | Coverage Δ | |
   |---|---|---|
   | [python](https://app.codecov.io/gh/apache/beam/pull/29106/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | `29.97% <0.00%> (-52.86%)` | :arrow_down: |
   
   Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache#carryforward-flags-in-the-pull-request-comment) to find out more.
   
   
   </details>
   
   [:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/apache/beam/pull/29106?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache).   
   :loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache).
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: github-unsubscribe@beam.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Implement a `Top` partitioner [beam]

Posted by "github-actions[bot] (via GitHub)" <gi...@apache.org>.
github-actions[bot] commented on PR #29106:
URL: https://github.com/apache/beam/pull/29106#issuecomment-2081471935

   This pull request has been marked as stale due to 60 days of inactivity. It will be closed in 1 week if no further activity occurs. If you think that’s incorrect or this pull request requires a review, please simply write any comment. If closed, you can revive the PR at any time and @mention a reviewer or discuss it on the dev@beam.apache.org list. Thank you for your contributions.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: github-unsubscribe@beam.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Implement a `Top` partitioner [beam]

Posted by "derrickaw (via GitHub)" <gi...@apache.org>.
derrickaw commented on PR #29106:
URL: https://github.com/apache/beam/pull/29106#issuecomment-3998029399

   Hi @hjtran, any next steps on this PR? thanks


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: github-unsubscribe@beam.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Implement a `Top` partitioner [beam]

Posted by "chamikaramj (via GitHub)" <gi...@apache.org>.
chamikaramj closed pull request #29106: Implement a `Top` partitioner
URL: https://github.com/apache/beam/pull/29106


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: github-unsubscribe@beam.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Implement a `Top` partitioner [beam]

Posted by "damccorm (via GitHub)" <gi...@apache.org>.
damccorm commented on code in PR #29106:
URL: https://github.com/apache/beam/pull/29106#discussion_r1369282464


##########
sdks/python/apache_beam/transforms/partitioners.py:
##########
@@ -0,0 +1,111 @@
+import uuid
+import apache_beam as beam
+from apache_beam import pvalue
+from typing import Optional, TypeVar
+from typing import Tuple
+from typing import Any
+from typing import Callable
+
+T = TypeVar('T')
+
+
+class Top(beam.PTransform):
+  """
+    A PTransform that takes a PCollection and partitions it into two
+    PCollections.  The first PCollection contains the largest n elements of the
+    input PCollection, and the second PCollection contains the remaining
+    elements of the input PCollection.
+
+    Parameters:
+        n: The number of elements to take from the input PCollection.
+        key: A function that takes an element of the input PCollection and
+            returns a value to compare for the purpose of determining the top n
+            elements, similar to Python's built-in sorted function.
+        reverse: If True, the top n elements will be the n smallest elements of
+            the input PCollection.
+
+    Example usage:
+
+        >>> with beam.Pipeline() as p:
+        ...     top, remaining = (p
+        ...         | beam.Create(list(range(10)))
+        ...         | partitioners.Top(3))
+        ...     # top will contain [7, 8, 9]
+        ...     # remaining will contain [0, 1, 2, 3, 4, 5, 6]
+
+    .. note::
+
+        This transform requires that the top PCollection fit into memory.
+
+    """
+  def __init__(
+      self, n: int, key: Optional[Callable[[Any], Any]] = None, reverse=False):
+    _validate_nonzero_positive_int(n)
+    self.n = n
+    self.key = key
+    self.reverse = reverse
+
+  def expand(self,
+             pcoll) -> Tuple[pvalue.PCollection[T], pvalue.PCollection[T]]:
+    # **Illustrative Example:**
+    # Our goal is to return two pcollections, top and
+    # remaining.
+
+    # Suppose you want to take the top element from `[1, 2, 2]`. Since we have
+    # identical elements, we need to be able to uniquely identify each one,
+    # so we assign a unique ID to each:
+    # `inputs_with_ids: [(1, "A"), (2, "B"), (2, "C")]`
+
+    # Then we sample, e.g.
+    # ``` sample: [(2, "B")] ```
+    # To get our goal `top` pcollection, we just strip the uuids from
+    # that sample.
+
+    # Now to get the `top` pcollection, we need to return essentially
+    # `inputs_with_ids` but without any of the elements fom the sample. To
+    # do this, we create a set from `sample`, getting `sample_ids:
+    # [set("B")]`. Now that we have this set, we can create our
+    # `remaining_with_ids` pcollection by just filtering out
+    # `inputs_with_ids` and checking for each element "Does this element's
+    # corresponding ID exist in `sample_ids`?"
+
+    # Finally, we just return `top` and strip the IDs as we no longer
+    # need them and the user doesn't care about them.
+    wrapped_key = lambda elem: self.key(elem[0]) if self.key else elem[0]
+    inputs_with_ids = (pcoll | beam.Map(_add_uuid))
+    sample = (
+        inputs_with_ids
+        | beam.combiners.Top.Of(self.n, key=wrapped_key, reverse=self.reverse))
+    sample_ids = (
+        sample
+        | beam.Map(lambda sample_list: set(ele[1] for ele in sample_list)))
+
+    def elem_is_not_sampled(elem, sampled_set):
+      return elem[1] not in sampled_set
+
+    remaining = (

Review Comment:
   I think we can both simplify this implementation and significantly improve performance using [user state](https://beam.apache.org/documentation/programming-guide/#types-of-state). Basically the idea would be to use bag state and:
   1) Add a dummy key if the input is unkeyed
   2) Read state to get our current running set of N elements.
   3) Scan that state to get the smallest element
   4) Check if the current element is larger than the smallest element grabbed from state. If yes, then add it to state. If no, emit it into the non-top PCollection
   5) In StartBundle, set an [event time timer](https://beam.apache.org/documentation/programming-guide/#timers) that expires at the end of the window. In that timer, if you have any buffered items in state emit them and clear your state.
   6) Remove the dummy key
   
   This would have a few advantages:
   1) It wouldn't block execution of later steps for the non-Top PCollection. So, you could partition your data and wait until the end of the window to process your Top PCollection, but you could start processing elements in your non-top PCollection as soon as you've seen enough data to guarantee that they can't be a part of your Top PCollection.
   2) It wouldn't require doing a join which could be at least a bit expensive.
   3) You get per-key Top for free since state is per-key - you just would skip adding/removing the dummy key.
   
   There's even a few optimizations we could potentially add:
   1) Store the current minimum value in valueState (duplicated from the bag state) and only read that initially (since it is a potentially much cheaper read if N gets large)
   2) Cache the current minimum value in the DoFn and route any future entries that are smaller than that to the non-Top PCollection. This would get out of date, so we'd still need to do the comparison to state if our value was larger than the min_val.
   
   Thoughts?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: github-unsubscribe@beam.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Implement a `Top` partitioner [beam]

Posted by "damccorm (via GitHub)" <gi...@apache.org>.
damccorm commented on PR #29106:
URL: https://github.com/apache/beam/pull/29106#issuecomment-1776058665

   > How exactly does the codecov bot invoke tests? I suspect that the new module should have ~100% coverage but the new test probably is getting missed somehow by codecov
   
   Yeah, the codecov bot is frustratingly wrong sometimes, but especially on new code it seems? Your tests did run - https://ci-beam.apache.org/job/beam_PreCommit_Python_Coverage_Commit/4639/testReport/apache_beam.transforms.partitioners_test/ - so I'm not too worried about the bad signal


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: github-unsubscribe@beam.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Implement a `Top` partitioner [beam]

Posted by "hjtran (via GitHub)" <gi...@apache.org>.
hjtran commented on PR #29106:
URL: https://github.com/apache/beam/pull/29106#issuecomment-1775567184

   > ## [Codecov](https://app.codecov.io/gh/apache/beam/pull/29106?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) Report
   > > Merging [#29106](https://app.codecov.io/gh/apache/beam/pull/29106?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) ([206042e](https://github.com/apache/beam/commit/206042eb3e9529a92c13d1a050ffd89f307f6e7b)) into [master](https://app.codecov.io/gh/apache/beam/commit/38720a39d91db1853741f86d29819bf5a5d59a36?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) ([38720a3](https://github.com/apache/beam/commit/38720a39d91db1853741f86d29819bf5a5d59a36)) will **decrease** coverage by `33.87%`.
   > > Report is 446 commits behind head on master.
   > > The diff coverage is `0.00%`.
   > 
   > ```diff
   > @@             Coverage Diff             @@
   > ##           master   #29106       +/-   ##
   > ===========================================
   > - Coverage   72.22%   38.36%   -33.87%     
   > ===========================================
   >   Files         684      687        +3     
   >   Lines      100856   101693      +837     
   > ===========================================
   > - Hits        72846    39010    -33836     
   > - Misses      26434    61107    +34673     
   >   Partials     1576     1576               
   > ```
   > 
   > [Flag](https://app.codecov.io/gh/apache/beam/pull/29106/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache)	Coverage Δ	
   > [python](https://app.codecov.io/gh/apache/beam/pull/29106/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache)	`29.97% <0.00%> (-52.86%)`	⬇️
   > Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache#carryforward-flags-in-the-pull-request-comment) to find out more.
   > 
   > [Files](https://app.codecov.io/gh/apache/beam/pull/29106?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache)	Coverage Δ	
   > [sdks/python/apache_beam/transforms/partitioners.py](https://app.codecov.io/gh/apache/beam/pull/29106?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache#diff-c2Rrcy9weXRob24vYXBhY2hlX2JlYW0vdHJhbnNmb3Jtcy9wYXJ0aXRpb25lcnMucHk=)	`0.00% <0.00%> (ø)`	
   > ... and [316 files with indirect coverage changes](https://app.codecov.io/gh/apache/beam/pull/29106/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache)
   > 
   > 📣 We’re building smart automated test selection to slash your CI/CD build times. [Learn more](https://about.codecov.io/iterative-testing/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache)
   
   How exactly does the codecov bot invoke tests? I suspect that the new module should have ~100% coverage but the new test probably is getting missed somehow by codecov


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: github-unsubscribe@beam.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Implement a `Top` partitioner [beam]

Posted by "github-actions[bot] (via GitHub)" <gi...@apache.org>.
github-actions[bot] commented on PR #29106:
URL: https://github.com/apache/beam/pull/29106#issuecomment-2441492852

   This pull request has been marked as stale due to 60 days of inactivity. It will be closed in 1 week if no further activity occurs. If you think that’s incorrect or this pull request requires a review, please simply write any comment. If closed, you can revive the PR at any time and @mention a reviewer or discuss it on the dev@beam.apache.org list. Thank you for your contributions.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: github-unsubscribe@beam.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Implement a `Top` partitioner [beam]

Posted by "github-actions[bot] (via GitHub)" <gi...@apache.org>.
github-actions[bot] commented on PR #29106:
URL: https://github.com/apache/beam/pull/29106#issuecomment-2833442006

   This pull request has been marked as stale due to 60 days of inactivity. It will be closed in 1 week if no further activity occurs. If you think that’s incorrect or this pull request requires a review, please simply write any comment. If closed, you can revive the PR at any time and @mention a reviewer or discuss it on the dev@beam.apache.org list. Thank you for your contributions.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: github-unsubscribe@beam.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Implement a `Top` partitioner [beam]

Posted by "hjtran (via GitHub)" <gi...@apache.org>.
hjtran commented on PR #29106:
URL: https://github.com/apache/beam/pull/29106#issuecomment-3998040159

   Hi @derrickaw - I'm not actively working on this. Feel free to take it over if you're interested!


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: github-unsubscribe@beam.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Implement a `Top` partitioner [beam]

Posted by "codecov[bot] (via GitHub)" <gi...@apache.org>.
codecov[bot] commented on PR #29106:
URL: https://github.com/apache/beam/pull/29106#issuecomment-1775558867

   ## [Codecov](https://app.codecov.io/gh/apache/beam/pull/29106?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) Report
   > Merging [#29106](https://app.codecov.io/gh/apache/beam/pull/29106?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) (206042e) into [master](https://app.codecov.io/gh/apache/beam/commit/38720a39d91db1853741f86d29819bf5a5d59a36?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) (38720a3) will **decrease** coverage by `33.87%`.
   > Report is 446 commits behind head on master.
   > The diff coverage is `0.00%`.
   
   ```diff
   @@             Coverage Diff             @@
   ##           master   #29106       +/-   ##
   ===========================================
   - Coverage   72.22%   38.36%   -33.87%     
   ===========================================
     Files         684      687        +3     
     Lines      100856   101693      +837     
   ===========================================
   - Hits        72846    39010    -33836     
   - Misses      26434    61107    +34673     
     Partials     1576     1576               
   ```
   
   | [Flag](https://app.codecov.io/gh/apache/beam/pull/29106/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | Coverage Δ | |
   |---|---|---|
   | [python](https://app.codecov.io/gh/apache/beam/pull/29106/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | `29.97% <0.00%> (-52.86%)` | :arrow_down: |
   
   Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache#carryforward-flags-in-the-pull-request-comment) to find out more.
   
   | [Files](https://app.codecov.io/gh/apache/beam/pull/29106?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache) | Coverage Δ | |
   |---|---|---|
   | [sdks/python/apache\_beam/transforms/partitioners.py](https://app.codecov.io/gh/apache/beam/pull/29106?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache#diff-c2Rrcy9weXRob24vYXBhY2hlX2JlYW0vdHJhbnNmb3Jtcy9wYXJ0aXRpb25lcnMucHk=) | `0.00% <0.00%> (ø)` | |
   
   ... and [316 files with indirect coverage changes](https://app.codecov.io/gh/apache/beam/pull/29106/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache)
   
   :mega: We’re building smart automated test selection to slash your CI/CD build times. [Learn more](https://about.codecov.io/iterative-testing/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=apache)
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: github-unsubscribe@beam.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Implement a `Top` partitioner [beam]

Posted by "hjtran (via GitHub)" <gi...@apache.org>.
hjtran commented on code in PR #29106:
URL: https://github.com/apache/beam/pull/29106#discussion_r1373733774


##########
sdks/python/apache_beam/transforms/partitioners.py:
##########
@@ -0,0 +1,111 @@
+import uuid
+import apache_beam as beam
+from apache_beam import pvalue
+from typing import Optional, TypeVar
+from typing import Tuple
+from typing import Any
+from typing import Callable
+
+T = TypeVar('T')
+
+
+class Top(beam.PTransform):
+  """
+    A PTransform that takes a PCollection and partitions it into two
+    PCollections.  The first PCollection contains the largest n elements of the
+    input PCollection, and the second PCollection contains the remaining
+    elements of the input PCollection.
+
+    Parameters:
+        n: The number of elements to take from the input PCollection.
+        key: A function that takes an element of the input PCollection and
+            returns a value to compare for the purpose of determining the top n
+            elements, similar to Python's built-in sorted function.
+        reverse: If True, the top n elements will be the n smallest elements of
+            the input PCollection.
+
+    Example usage:
+
+        >>> with beam.Pipeline() as p:
+        ...     top, remaining = (p
+        ...         | beam.Create(list(range(10)))
+        ...         | partitioners.Top(3))
+        ...     # top will contain [7, 8, 9]
+        ...     # remaining will contain [0, 1, 2, 3, 4, 5, 6]
+
+    .. note::
+
+        This transform requires that the top PCollection fit into memory.
+
+    """
+  def __init__(
+      self, n: int, key: Optional[Callable[[Any], Any]] = None, reverse=False):
+    _validate_nonzero_positive_int(n)
+    self.n = n
+    self.key = key
+    self.reverse = reverse
+
+  def expand(self,
+             pcoll) -> Tuple[pvalue.PCollection[T], pvalue.PCollection[T]]:
+    # **Illustrative Example:**
+    # Our goal is to return two pcollections, top and
+    # remaining.
+
+    # Suppose you want to take the top element from `[1, 2, 2]`. Since we have
+    # identical elements, we need to be able to uniquely identify each one,
+    # so we assign a unique ID to each:
+    # `inputs_with_ids: [(1, "A"), (2, "B"), (2, "C")]`
+
+    # Then we sample, e.g.
+    # ``` sample: [(2, "B")] ```
+    # To get our goal `top` pcollection, we just strip the uuids from
+    # that sample.
+
+    # Now to get the `top` pcollection, we need to return essentially
+    # `inputs_with_ids` but without any of the elements fom the sample. To
+    # do this, we create a set from `sample`, getting `sample_ids:
+    # [set("B")]`. Now that we have this set, we can create our
+    # `remaining_with_ids` pcollection by just filtering out
+    # `inputs_with_ids` and checking for each element "Does this element's
+    # corresponding ID exist in `sample_ids`?"
+
+    # Finally, we just return `top` and strip the IDs as we no longer
+    # need them and the user doesn't care about them.
+    wrapped_key = lambda elem: self.key(elem[0]) if self.key else elem[0]
+    inputs_with_ids = (pcoll | beam.Map(_add_uuid))
+    sample = (
+        inputs_with_ids
+        | beam.combiners.Top.Of(self.n, key=wrapped_key, reverse=self.reverse))
+    sample_ids = (
+        sample
+        | beam.Map(lambda sample_list: set(ele[1] for ele in sample_list)))
+
+    def elem_is_not_sampled(elem, sampled_set):
+      return elem[1] not in sampled_set
+
+    remaining = (

Review Comment:
   Is batch processing rare enough that it's not considered in "most use cases"? (Coming from a batch-only user).
   
   I don't quite follow your suggestion.
   > one optimization we could make to greatly reduce our bottleneck in cases where Top is small would be to add a pre-step where we filter out the non-Top elements
   
   Isn't this still doing the join that's being proposed in the current changeset?
   
   > send them downstream to be joined with the remaining non-Top elements 
   
   I don't follow the distinctino between these "remaining non-Top elements" and the non-Top elements we're filtering for in the pre-step



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: github-unsubscribe@beam.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Implement a `Top` partitioner [beam]

Posted by "github-actions[bot] (via GitHub)" <gi...@apache.org>.
github-actions[bot] commented on PR #29106:
URL: https://github.com/apache/beam/pull/29106#issuecomment-1964054886

   This pull request has been marked as stale due to 60 days of inactivity. It will be closed in 1 week if no further activity occurs. If you think that’s incorrect or this pull request requires a review, please simply write any comment. If closed, you can revive the PR at any time and @mention a reviewer or discuss it on the dev@beam.apache.org list. Thank you for your contributions.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: github-unsubscribe@beam.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Implement a `Top` partitioner [beam]

Posted by "github-actions[bot] (via GitHub)" <gi...@apache.org>.
github-actions[bot] commented on PR #29106:
URL: https://github.com/apache/beam/pull/29106#issuecomment-2564713389

   This pull request has been marked as stale due to 60 days of inactivity. It will be closed in 1 week if no further activity occurs. If you think that’s incorrect or this pull request requires a review, please simply write any comment. If closed, you can revive the PR at any time and @mention a reviewer or discuss it on the dev@beam.apache.org list. Thank you for your contributions.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: github-unsubscribe@beam.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Implement a `Top` partitioner [beam]

Posted by "github-actions[bot] (via GitHub)" <gi...@apache.org>.
github-actions[bot] commented on PR #29106:
URL: https://github.com/apache/beam/pull/29106#issuecomment-1869519717

   This pull request has been marked as stale due to 60 days of inactivity. It will be closed in 1 week if no further activity occurs. If you think that’s incorrect or this pull request requires a review, please simply write any comment. If closed, you can revive the PR at any time and @mention a reviewer or discuss it on the dev@beam.apache.org list. Thank you for your contributions.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: github-unsubscribe@beam.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Implement a `Top` partitioner [beam]

Posted by "damccorm (via GitHub)" <gi...@apache.org>.
damccorm commented on code in PR #29106:
URL: https://github.com/apache/beam/pull/29106#discussion_r1373826995


##########
sdks/python/apache_beam/transforms/partitioners.py:
##########
@@ -0,0 +1,111 @@
+import uuid
+import apache_beam as beam
+from apache_beam import pvalue
+from typing import Optional, TypeVar
+from typing import Tuple
+from typing import Any
+from typing import Callable
+
+T = TypeVar('T')
+
+
+class Top(beam.PTransform):
+  """
+    A PTransform that takes a PCollection and partitions it into two
+    PCollections.  The first PCollection contains the largest n elements of the
+    input PCollection, and the second PCollection contains the remaining
+    elements of the input PCollection.
+
+    Parameters:
+        n: The number of elements to take from the input PCollection.
+        key: A function that takes an element of the input PCollection and
+            returns a value to compare for the purpose of determining the top n
+            elements, similar to Python's built-in sorted function.
+        reverse: If True, the top n elements will be the n smallest elements of
+            the input PCollection.
+
+    Example usage:
+
+        >>> with beam.Pipeline() as p:
+        ...     top, remaining = (p
+        ...         | beam.Create(list(range(10)))
+        ...         | partitioners.Top(3))
+        ...     # top will contain [7, 8, 9]
+        ...     # remaining will contain [0, 1, 2, 3, 4, 5, 6]
+
+    .. note::
+
+        This transform requires that the top PCollection fit into memory.
+
+    """
+  def __init__(
+      self, n: int, key: Optional[Callable[[Any], Any]] = None, reverse=False):
+    _validate_nonzero_positive_int(n)
+    self.n = n
+    self.key = key
+    self.reverse = reverse
+
+  def expand(self,
+             pcoll) -> Tuple[pvalue.PCollection[T], pvalue.PCollection[T]]:
+    # **Illustrative Example:**
+    # Our goal is to return two pcollections, top and
+    # remaining.
+
+    # Suppose you want to take the top element from `[1, 2, 2]`. Since we have
+    # identical elements, we need to be able to uniquely identify each one,
+    # so we assign a unique ID to each:
+    # `inputs_with_ids: [(1, "A"), (2, "B"), (2, "C")]`
+
+    # Then we sample, e.g.
+    # ``` sample: [(2, "B")] ```
+    # To get our goal `top` pcollection, we just strip the uuids from
+    # that sample.
+
+    # Now to get the `top` pcollection, we need to return essentially
+    # `inputs_with_ids` but without any of the elements fom the sample. To
+    # do this, we create a set from `sample`, getting `sample_ids:
+    # [set("B")]`. Now that we have this set, we can create our
+    # `remaining_with_ids` pcollection by just filtering out
+    # `inputs_with_ids` and checking for each element "Does this element's
+    # corresponding ID exist in `sample_ids`?"
+
+    # Finally, we just return `top` and strip the IDs as we no longer
+    # need them and the user doesn't care about them.
+    wrapped_key = lambda elem: self.key(elem[0]) if self.key else elem[0]
+    inputs_with_ids = (pcoll | beam.Map(_add_uuid))
+    sample = (
+        inputs_with_ids
+        | beam.combiners.Top.Of(self.n, key=wrapped_key, reverse=self.reverse))
+    sample_ids = (
+        sample
+        | beam.Map(lambda sample_list: set(ele[1] for ele in sample_list)))
+
+    def elem_is_not_sampled(elem, sampled_set):
+      return elem[1] not in sampled_set
+
+    remaining = (

Review Comment:
   > Is batch processing rare enough that it's not considered in "most use cases"? (Coming from a batch-only user).
   
   No, in general I think we have a pretty even spread of batch/streaming usage (though its hard to say for sure). My point was more that I think this is beneficial for most use cases and this is especially true in streaming mode (but also many and possibly most batch cases). Even in batch mode, everything I said is true, you're just also probably more likely to have scenarios where you're particularly concerned about quick fan out. I do think my suggested optimization (explained in more detail below) resolves most of these concerns though.
   
   Basically, we're making a tradeoff, but I think the large majority of the time we can come out ahead with a stateful approach.
   
   > Isn't this still doing the join that's being proposed in the current changeset?
   
   No, there shouldn't be a join (Flatten is the closest thing, but that's not actually a join, its just conceptually treating 2 PCollections as one). Let me try explaining again in more depth; basically your flow would be:
   
   ```
   top_candidates, non_top1 = (pcoll
   | _TopPerBundle(...)) # modified to emit 2 pcollections
   top, non_top2 = (top_candidates
   | StatefulTopPartitioner(...)) # likely a composite where you add a key as discussed above
   non_top = Flatten(non_top1, non_top2)
   
   return top, non_top
   ```
   
   In this way, you don't need to do any expensive joins, you get the benefit of parallelism for `_TopPerBundle` (which should reduce your cardinality by `O(1000s)/N` where `N` is your `Top` size in batch mode (it won't help much in streaming which usually has smaller bundles depending on the runner), and your bottleneck is on a small subset of the data.
   
   `_TopPerBundle` would emit 2 pcollections, the first containing the top `N` elements from each bundle, and the 2nd containing the remaining elements.
   
   `StatefulTopPartitioner` would do the same, but over the entire input dataset.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: github-unsubscribe@beam.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Implement a `Top` partitioner [beam]

Posted by "hjtran (via GitHub)" <gi...@apache.org>.
hjtran commented on code in PR #29106:
URL: https://github.com/apache/beam/pull/29106#discussion_r1373836567


##########
sdks/python/apache_beam/transforms/partitioners.py:
##########
@@ -0,0 +1,111 @@
+import uuid
+import apache_beam as beam
+from apache_beam import pvalue
+from typing import Optional, TypeVar
+from typing import Tuple
+from typing import Any
+from typing import Callable
+
+T = TypeVar('T')
+
+
+class Top(beam.PTransform):
+  """
+    A PTransform that takes a PCollection and partitions it into two
+    PCollections.  The first PCollection contains the largest n elements of the
+    input PCollection, and the second PCollection contains the remaining
+    elements of the input PCollection.
+
+    Parameters:
+        n: The number of elements to take from the input PCollection.
+        key: A function that takes an element of the input PCollection and
+            returns a value to compare for the purpose of determining the top n
+            elements, similar to Python's built-in sorted function.
+        reverse: If True, the top n elements will be the n smallest elements of
+            the input PCollection.
+
+    Example usage:
+
+        >>> with beam.Pipeline() as p:
+        ...     top, remaining = (p
+        ...         | beam.Create(list(range(10)))
+        ...         | partitioners.Top(3))
+        ...     # top will contain [7, 8, 9]
+        ...     # remaining will contain [0, 1, 2, 3, 4, 5, 6]
+
+    .. note::
+
+        This transform requires that the top PCollection fit into memory.
+
+    """
+  def __init__(
+      self, n: int, key: Optional[Callable[[Any], Any]] = None, reverse=False):
+    _validate_nonzero_positive_int(n)
+    self.n = n
+    self.key = key
+    self.reverse = reverse
+
+  def expand(self,
+             pcoll) -> Tuple[pvalue.PCollection[T], pvalue.PCollection[T]]:
+    # **Illustrative Example:**
+    # Our goal is to return two pcollections, top and
+    # remaining.
+
+    # Suppose you want to take the top element from `[1, 2, 2]`. Since we have
+    # identical elements, we need to be able to uniquely identify each one,
+    # so we assign a unique ID to each:
+    # `inputs_with_ids: [(1, "A"), (2, "B"), (2, "C")]`
+
+    # Then we sample, e.g.
+    # ``` sample: [(2, "B")] ```
+    # To get our goal `top` pcollection, we just strip the uuids from
+    # that sample.
+
+    # Now to get the `top` pcollection, we need to return essentially
+    # `inputs_with_ids` but without any of the elements fom the sample. To
+    # do this, we create a set from `sample`, getting `sample_ids:
+    # [set("B")]`. Now that we have this set, we can create our
+    # `remaining_with_ids` pcollection by just filtering out
+    # `inputs_with_ids` and checking for each element "Does this element's
+    # corresponding ID exist in `sample_ids`?"
+
+    # Finally, we just return `top` and strip the IDs as we no longer
+    # need them and the user doesn't care about them.
+    wrapped_key = lambda elem: self.key(elem[0]) if self.key else elem[0]
+    inputs_with_ids = (pcoll | beam.Map(_add_uuid))
+    sample = (
+        inputs_with_ids
+        | beam.combiners.Top.Of(self.n, key=wrapped_key, reverse=self.reverse))
+    sample_ids = (
+        sample
+        | beam.Map(lambda sample_list: set(ele[1] for ele in sample_list)))
+
+    def elem_is_not_sampled(elem, sampled_set):
+      return elem[1] not in sampled_set
+
+    remaining = (

Review Comment:
   Ah that makes sense! I'll take a shot at implementing that, thanks for the explanation!



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: github-unsubscribe@beam.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Implement a `Top` partitioner [beam]

Posted by "github-actions[bot] (via GitHub)" <gi...@apache.org>.
github-actions[bot] commented on PR #29106:
URL: https://github.com/apache/beam/pull/29106#issuecomment-2315221154

   This pull request has been marked as stale due to 60 days of inactivity. It will be closed in 1 week if no further activity occurs. If you think that’s incorrect or this pull request requires a review, please simply write any comment. If closed, you can revive the PR at any time and @mention a reviewer or discuss it on the dev@beam.apache.org list. Thank you for your contributions.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: github-unsubscribe@beam.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Implement a `Top` partitioner [beam]

Posted by "hjtran (via GitHub)" <gi...@apache.org>.
hjtran commented on code in PR #29106:
URL: https://github.com/apache/beam/pull/29106#discussion_r1373733774


##########
sdks/python/apache_beam/transforms/partitioners.py:
##########
@@ -0,0 +1,111 @@
+import uuid
+import apache_beam as beam
+from apache_beam import pvalue
+from typing import Optional, TypeVar
+from typing import Tuple
+from typing import Any
+from typing import Callable
+
+T = TypeVar('T')
+
+
+class Top(beam.PTransform):
+  """
+    A PTransform that takes a PCollection and partitions it into two
+    PCollections.  The first PCollection contains the largest n elements of the
+    input PCollection, and the second PCollection contains the remaining
+    elements of the input PCollection.
+
+    Parameters:
+        n: The number of elements to take from the input PCollection.
+        key: A function that takes an element of the input PCollection and
+            returns a value to compare for the purpose of determining the top n
+            elements, similar to Python's built-in sorted function.
+        reverse: If True, the top n elements will be the n smallest elements of
+            the input PCollection.
+
+    Example usage:
+
+        >>> with beam.Pipeline() as p:
+        ...     top, remaining = (p
+        ...         | beam.Create(list(range(10)))
+        ...         | partitioners.Top(3))
+        ...     # top will contain [7, 8, 9]
+        ...     # remaining will contain [0, 1, 2, 3, 4, 5, 6]
+
+    .. note::
+
+        This transform requires that the top PCollection fit into memory.
+
+    """
+  def __init__(
+      self, n: int, key: Optional[Callable[[Any], Any]] = None, reverse=False):
+    _validate_nonzero_positive_int(n)
+    self.n = n
+    self.key = key
+    self.reverse = reverse
+
+  def expand(self,
+             pcoll) -> Tuple[pvalue.PCollection[T], pvalue.PCollection[T]]:
+    # **Illustrative Example:**
+    # Our goal is to return two pcollections, top and
+    # remaining.
+
+    # Suppose you want to take the top element from `[1, 2, 2]`. Since we have
+    # identical elements, we need to be able to uniquely identify each one,
+    # so we assign a unique ID to each:
+    # `inputs_with_ids: [(1, "A"), (2, "B"), (2, "C")]`
+
+    # Then we sample, e.g.
+    # ``` sample: [(2, "B")] ```
+    # To get our goal `top` pcollection, we just strip the uuids from
+    # that sample.
+
+    # Now to get the `top` pcollection, we need to return essentially
+    # `inputs_with_ids` but without any of the elements fom the sample. To
+    # do this, we create a set from `sample`, getting `sample_ids:
+    # [set("B")]`. Now that we have this set, we can create our
+    # `remaining_with_ids` pcollection by just filtering out
+    # `inputs_with_ids` and checking for each element "Does this element's
+    # corresponding ID exist in `sample_ids`?"
+
+    # Finally, we just return `top` and strip the IDs as we no longer
+    # need them and the user doesn't care about them.
+    wrapped_key = lambda elem: self.key(elem[0]) if self.key else elem[0]
+    inputs_with_ids = (pcoll | beam.Map(_add_uuid))
+    sample = (
+        inputs_with_ids
+        | beam.combiners.Top.Of(self.n, key=wrapped_key, reverse=self.reverse))
+    sample_ids = (
+        sample
+        | beam.Map(lambda sample_list: set(ele[1] for ele in sample_list)))
+
+    def elem_is_not_sampled(elem, sampled_set):
+      return elem[1] not in sampled_set
+
+    remaining = (

Review Comment:
   Is batch processing rare enough that it's not considered in "most use cases"? (Coming from a batch-only user).
   
   I don't quite follow your suggestion.
   > one optimization we could make to greatly reduce our bottleneck in cases where Top is small would be to add a pre-step where we filter out the non-Top elements a
   Isn't this still doing the join that's being proposed in the current changeset?
   
   > send them downstream to be joined with the remaining non-Top elements 
   I don't follow the distinctino between these "remaining non-Top elements" and the non-Top elements we're filtering for in the pre-step



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: github-unsubscribe@beam.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Implement a `Top` partitioner [beam]

Posted by "claudevdm (via GitHub)" <gi...@apache.org>.
claudevdm commented on PR #29106:
URL: https://github.com/apache/beam/pull/29106#issuecomment-2682215354

   Hi @hjtran , are there any next steps for this PR? 


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: github-unsubscribe@beam.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Implement a `Top` partitioner [beam]

Posted by "derrickaw (via GitHub)" <gi...@apache.org>.
derrickaw commented on PR #29106:
URL: https://github.com/apache/beam/pull/29106#issuecomment-3161044129

   waiting on author


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: github-unsubscribe@beam.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Implement a `Top` partitioner [beam]

Posted by "hjtran (via GitHub)" <gi...@apache.org>.
hjtran commented on PR #29106:
URL: https://github.com/apache/beam/pull/29106#issuecomment-1775524984

   reviewer: @damccorm 


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: github-unsubscribe@beam.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Implement a `Top` partitioner [beam]

Posted by "github-actions[bot] (via GitHub)" <gi...@apache.org>.
github-actions[bot] commented on PR #29106:
URL: https://github.com/apache/beam/pull/29106#issuecomment-1775527577

   Stopping reviewer notifications for this pull request: review requested by someone other than the bot, ceding control


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: github-unsubscribe@beam.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Implement a `Top` partitioner [beam]

Posted by "hjtran (via GitHub)" <gi...@apache.org>.
hjtran commented on code in PR #29106:
URL: https://github.com/apache/beam/pull/29106#discussion_r1373239257


##########
sdks/python/apache_beam/transforms/partitioners.py:
##########
@@ -0,0 +1,111 @@
+import uuid
+import apache_beam as beam
+from apache_beam import pvalue
+from typing import Optional, TypeVar
+from typing import Tuple
+from typing import Any
+from typing import Callable
+
+T = TypeVar('T')
+
+
+class Top(beam.PTransform):
+  """
+    A PTransform that takes a PCollection and partitions it into two
+    PCollections.  The first PCollection contains the largest n elements of the
+    input PCollection, and the second PCollection contains the remaining
+    elements of the input PCollection.
+
+    Parameters:
+        n: The number of elements to take from the input PCollection.
+        key: A function that takes an element of the input PCollection and
+            returns a value to compare for the purpose of determining the top n
+            elements, similar to Python's built-in sorted function.
+        reverse: If True, the top n elements will be the n smallest elements of
+            the input PCollection.
+
+    Example usage:
+
+        >>> with beam.Pipeline() as p:
+        ...     top, remaining = (p
+        ...         | beam.Create(list(range(10)))
+        ...         | partitioners.Top(3))
+        ...     # top will contain [7, 8, 9]
+        ...     # remaining will contain [0, 1, 2, 3, 4, 5, 6]
+
+    .. note::
+
+        This transform requires that the top PCollection fit into memory.
+
+    """
+  def __init__(
+      self, n: int, key: Optional[Callable[[Any], Any]] = None, reverse=False):
+    _validate_nonzero_positive_int(n)
+    self.n = n
+    self.key = key
+    self.reverse = reverse
+
+  def expand(self,
+             pcoll) -> Tuple[pvalue.PCollection[T], pvalue.PCollection[T]]:
+    # **Illustrative Example:**
+    # Our goal is to return two pcollections, top and
+    # remaining.
+
+    # Suppose you want to take the top element from `[1, 2, 2]`. Since we have
+    # identical elements, we need to be able to uniquely identify each one,
+    # so we assign a unique ID to each:
+    # `inputs_with_ids: [(1, "A"), (2, "B"), (2, "C")]`
+
+    # Then we sample, e.g.
+    # ``` sample: [(2, "B")] ```
+    # To get our goal `top` pcollection, we just strip the uuids from
+    # that sample.
+
+    # Now to get the `top` pcollection, we need to return essentially
+    # `inputs_with_ids` but without any of the elements fom the sample. To
+    # do this, we create a set from `sample`, getting `sample_ids:
+    # [set("B")]`. Now that we have this set, we can create our
+    # `remaining_with_ids` pcollection by just filtering out
+    # `inputs_with_ids` and checking for each element "Does this element's
+    # corresponding ID exist in `sample_ids`?"
+
+    # Finally, we just return `top` and strip the IDs as we no longer
+    # need them and the user doesn't care about them.
+    wrapped_key = lambda elem: self.key(elem[0]) if self.key else elem[0]
+    inputs_with_ids = (pcoll | beam.Map(_add_uuid))
+    sample = (
+        inputs_with_ids
+        | beam.combiners.Top.Of(self.n, key=wrapped_key, reverse=self.reverse))
+    sample_ids = (
+        sample
+        | beam.Map(lambda sample_list: set(ele[1] for ele in sample_list)))
+
+    def elem_is_not_sampled(elem, sampled_set):
+      return elem[1] not in sampled_set
+
+    remaining = (

Review Comment:
   Thanks for the review!
   
   That does seem simple. I don't have a strong understanding of how user state works. Does this imply that a single worker will need to go through the entire set of inputs? Won't that become a bottleneck or am I misunderstanding how state works?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: github-unsubscribe@beam.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Implement a `Top` partitioner [beam]

Posted by "damccorm (via GitHub)" <gi...@apache.org>.
damccorm commented on code in PR #29106:
URL: https://github.com/apache/beam/pull/29106#discussion_r1373707395


##########
sdks/python/apache_beam/transforms/partitioners.py:
##########
@@ -0,0 +1,111 @@
+import uuid
+import apache_beam as beam
+from apache_beam import pvalue
+from typing import Optional, TypeVar
+from typing import Tuple
+from typing import Any
+from typing import Callable
+
+T = TypeVar('T')
+
+
+class Top(beam.PTransform):
+  """
+    A PTransform that takes a PCollection and partitions it into two
+    PCollections.  The first PCollection contains the largest n elements of the
+    input PCollection, and the second PCollection contains the remaining
+    elements of the input PCollection.
+
+    Parameters:
+        n: The number of elements to take from the input PCollection.
+        key: A function that takes an element of the input PCollection and
+            returns a value to compare for the purpose of determining the top n
+            elements, similar to Python's built-in sorted function.
+        reverse: If True, the top n elements will be the n smallest elements of
+            the input PCollection.
+
+    Example usage:
+
+        >>> with beam.Pipeline() as p:
+        ...     top, remaining = (p
+        ...         | beam.Create(list(range(10)))
+        ...         | partitioners.Top(3))
+        ...     # top will contain [7, 8, 9]
+        ...     # remaining will contain [0, 1, 2, 3, 4, 5, 6]
+
+    .. note::
+
+        This transform requires that the top PCollection fit into memory.
+
+    """
+  def __init__(
+      self, n: int, key: Optional[Callable[[Any], Any]] = None, reverse=False):
+    _validate_nonzero_positive_int(n)
+    self.n = n
+    self.key = key
+    self.reverse = reverse
+
+  def expand(self,
+             pcoll) -> Tuple[pvalue.PCollection[T], pvalue.PCollection[T]]:
+    # **Illustrative Example:**
+    # Our goal is to return two pcollections, top and
+    # remaining.
+
+    # Suppose you want to take the top element from `[1, 2, 2]`. Since we have
+    # identical elements, we need to be able to uniquely identify each one,
+    # so we assign a unique ID to each:
+    # `inputs_with_ids: [(1, "A"), (2, "B"), (2, "C")]`
+
+    # Then we sample, e.g.
+    # ``` sample: [(2, "B")] ```
+    # To get our goal `top` pcollection, we just strip the uuids from
+    # that sample.
+
+    # Now to get the `top` pcollection, we need to return essentially
+    # `inputs_with_ids` but without any of the elements fom the sample. To
+    # do this, we create a set from `sample`, getting `sample_ids:
+    # [set("B")]`. Now that we have this set, we can create our
+    # `remaining_with_ids` pcollection by just filtering out
+    # `inputs_with_ids` and checking for each element "Does this element's
+    # corresponding ID exist in `sample_ids`?"
+
+    # Finally, we just return `top` and strip the IDs as we no longer
+    # need them and the user doesn't care about them.
+    wrapped_key = lambda elem: self.key(elem[0]) if self.key else elem[0]
+    inputs_with_ids = (pcoll | beam.Map(_add_uuid))
+    sample = (
+        inputs_with_ids
+        | beam.combiners.Top.Of(self.n, key=wrapped_key, reverse=self.reverse))
+    sample_ids = (
+        sample
+        | beam.Map(lambda sample_list: set(ele[1] for ele in sample_list)))
+
+    def elem_is_not_sampled(elem, sampled_set):
+      return elem[1] not in sampled_set
+
+    remaining = (

Review Comment:
   It does, though in exchange you're getting the ability to start processing your data in the rest of your pipeline before you reach the end of the window (which probably saves time/resources in most use cases, especially in streaming). I think this is a fair concern though, especially in batch; one optimization we could make to greatly reduce our bottleneck in cases where Top is small would be to add a pre-step where we filter out the non-Top elements and send them downstream to be joined with the remaining non-Top elements using a `Flatten` (something like [_TopPerBundle](https://github.com/apache/beam/blob/206042eb3e9529a92c13d1a050ffd89f307f6e7b/sdks/python/apache_beam/transforms/combiners.py#L327)). Cases where Top is large will still end up running into a similar bottleneck even if we use a combiner since we won't be able to do much combiner lifting (local reduction before sending it over the wire).
   
   I'll also note that state is per key/window, so we're actually just talking the set of inputs for a single window and you will get parallelization with multiple windows.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: github-unsubscribe@beam.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Implement a `Top` partitioner [beam]

Posted by "github-actions[bot] (via GitHub)" <gi...@apache.org>.
github-actions[bot] commented on PR #29106:
URL: https://github.com/apache/beam/pull/29106#issuecomment-3401638775

   This pull request has been marked as stale due to 60 days of inactivity. It will be closed in 1 week if no further activity occurs. If you think that’s incorrect or this pull request requires a review, please simply write any comment. If closed, you can revive the PR at any time and @mention a reviewer or discuss it on the dev@beam.apache.org list. Thank you for your contributions.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: github-unsubscribe@beam.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org


Re: [PR] Implement a `Top` partitioner [beam]

Posted by "chamikaramj (via GitHub)" <gi...@apache.org>.
chamikaramj commented on PR #29106:
URL: https://github.com/apache/beam/pull/29106#issuecomment-4597265400

   Closing this for now. Please reopen if needed. Thanks!


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: github-unsubscribe@beam.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org