REF/POC: Share groupby/series algos (rank)
State: closed

- [x] passes `black pandas`
- [x] passes `git diff upstream/master -u -- "*.py" | flake8 --diff`

Right now there are 3 rank algorithms which are extremely similar, but have slight differences. This seems to cause some maintenance pain.  First, inconsistencies between implementations allows more bug potential (like #32593, only a bug for `rank_2d`). For issues like #32859 which affect all 3, a fix would have to be applied in 3 places and testing added separately for `Series`, `DataFrame`, and `GroupBy`. This pr attempts to mitigate these issues by combining the implementations `group_rank` and `rank_1d` (which as an additional bonus gives the enhancement of object support for `GroupBy.rank()` (#38278)).

Is this kind of refactor/deduplication helpful? If so, similar logic can probably be applied elsewhere.

The diff here makes the changes look more complicated than they are because `rank_1d` is essentially replaced by `group_rank`. The original `group_rank` implementation is only slightly changed to allow for optional labels and object support.


Benchmarks look ok:
<details>

```
      before           after         ratio
     [9f1a41de]       [5cd81d25]
     <master>         <ref/rank>
       7.87±0.1ms       7.77±0.1ms     0.99  frame_methods.Rank.time_rank('float')
       2.61±0.1ms       2.55±0.1ms     0.97  frame_methods.Rank.time_rank('int')
         57.9±3ms         59.0±6ms     1.02  frame_methods.Rank.time_rank('object')
      2.67±0.04ms      2.58±0.08ms     0.97  frame_methods.Rank.time_rank('uint')
      
       10.8±0.4ms       9.45±0.3ms    ~0.87  series_methods.Rank.time_rank('float')
       7.39±0.2ms         6.79±1ms     0.92  series_methods.Rank.time_rank('int')
         52.9±4ms         47.7±2ms    ~0.90  series_methods.Rank.time_rank('object')
         7.32±1ms       6.58±0.3ms    ~0.90  series_methods.Rank.time_rank('uint')
         
         
          314±3μs         352±40μs    ~1.12  groupby.GroupByMethods.time_dtype_as_field('datetime', 'rank', 'direct')
          316±8μs         322±10μs     1.02  groupby.GroupByMethods.time_dtype_as_field('datetime', 'rank', 'transformation')
         421±10μs         475±60μs    ~1.13  groupby.GroupByMethods.time_dtype_as_field('float', 'rank', 'direct')
         409±10μs         482±60μs    ~1.18  groupby.GroupByMethods.time_dtype_as_field('float', 'rank', 'transformation')
          505±3μs         420±10μs    ~0.83  groupby.GroupByMethods.time_dtype_as_field('int', 'rank', 'direct')
-        510±20μs          410±3μs     0.80  groupby.GroupByMethods.time_dtype_as_field('int', 'rank', 'transformation')
         411±20μs         441±60μs     1.07  groupby.GroupByMethods.time_dtype_as_group('datetime', 'rank', 'direct')
         486±30μs         509±10μs     1.05  groupby.GroupByMethods.time_dtype_as_group('datetime', 'rank', 'transformation')
         470±50μs          424±9μs    ~0.90  groupby.GroupByMethods.time_dtype_as_group('float', 'rank', 'direct')
         409±10μs         505±60μs    ~1.23  groupby.GroupByMethods.time_dtype_as_group('float', 'rank', 'transformation')
          404±7μs         470±60μs    ~1.16  groupby.GroupByMethods.time_dtype_as_group('int', 'rank', 'direct')
          407±7μs          412±9μs     1.01  groupby.GroupByMethods.time_dtype_as_group('int', 'rank', 'transformation')
 ```
 
 </details>
         
         

Labels:
name: Groupby
name: Clean

Comments:
> Is this kind of refactor/deduplication helpful? If so, similar logic can probably be applied elsewhere.

yes absolutely. I am not sure if we have an issue about this, but *certianly* would take things like this. note these might be quite tricky because there are possibilities of sublte differences between the algos & performance is always a concern (e.g. the 1-d with no grouping are often faster than an equivalent groupby with a single group. but the code unification is more important).
> to make the diff easier on the eyes. can you do a pre-cursor PR which just straight up moves things that you are going to need.

Was worried this would be an issue, but not sure how to clean up. The diff is so bad because `rank_1d` is being replaced with `group_rank`, so the diff essentially compares the original `rank_1d` to the original `group_rank` (so just this single simple replacement makes the diff useless).  The helpful diff here would be the new `rank_1d` vs the old `group_rank`.  I can't think of anything which can be moved as a precursor that won't break existing functionality (other than orthogonal additions like the benchmarks), any advice here would be much appreciated if you had something specific in mind.
> > to make the diff easier on the eyes. can you do a pre-cursor PR which just straight up moves things that you are going to need.
> 
> Was worried this would be an issue, but not sure how to clean up. The diff is so bad because `rank_1d` is being replaced with `group_rank`, so the diff essentially compares the original `rank_1d` to the original `group_rank` (so just this single simple replacement makes the diff useless). The helpful diff here would be the new `rank_1d` vs the old `group_rank`. I can't think of anything which can be moved as a precursor that won't break existing functionality (other than orthogonal additions like the benchmarks), any advice here would be much appreciated if you had something specific in mind.

ok its fine if a precusor is not helpful (e.g. just do this one)
can you show the asv's vs master
> can you show the asv's vs master

They're in a `details` block in the pr body
can you merge master
Merged master
cc @jbrockmendel