ENH: Add groupby().ngroup() method to count groups (#11642)
State: closed

This basically adds a method to give access to the coding returned by `grouper.group_info[0]`, i.e. the number of the group that each row is in.  This is a natural parallel to cumcount(), and while it's not the world's most important feature it's come in handy for me from time to time and deserves a public method, IMHO.
- [X] closes #11642
- [X] 9 tests added / passed
- [X] passes `git diff upstream/master | flake8 --diff`
- [X] whatsnew entry


Labels:
name: Enhancement
name: Groupby

Comments:
I think you can add this function [here](https://github.com/dsm054/pandas/blob/a6e60a7adfe0c2313d8678c77654cbdb55d00ba1/pandas/core/groupby.py#L98), so that `df.groupby(...).transform('enumerate')` also works.    Probably the same with `cumcount`.

Isn't the name `enumerate` confusing? I expected something related to iteration.

can you rebase / update?

looking back on this, shouldn't we call this `g.factorize()`?

@jreback: I'll buy that.  The collision with `enumerate` was always a bit unfortunate.

I agree `enumerate` can be confusing given that it is not in an iteration context here, but I find `factorize` also not ideal (give its notion of categoricals).

The verb is also 'number'. But that is maybe also a bit strange to use given the use of `number` as a noun? According to google translate/oxford dictionary:

> number: Mark with a number or assign a number to, typically to indicate position in a series:
> 
> > Synonyms: assign a number to, categorize by number, specify by number, mark with a number, itemize, enumerate

So this points back to enumerate ..

how about `.categorize()`? or `.group_number()`?

any more thoughts on the name? does SQL call this anything?

summary (and some new)
- ``.factorize()``
- ``.enumerate()``
- ``.categorize()``
- ``.group_number()``
- ``.ngroup()``
- ``.number()``

any further thoughts on this?
@dsm054 thoughts on this?
Looking back I still think that `enumerate` was a poor choice of name because we're not returning an iterable of tuples, and factorize and categorize suggest contracts that I don't think we necessarily want to commit to (namely, that the numbers and/or types will match those that pandas would give otherwise).

`group_number` is explicit, at least, and `ngroup` has a certain parallel with `ngroups` which is kind of appealing.  `number` feels too open-ended to me.  

In SQL I'd use something like DENSE_RANK() to get this.
I like ``.ngroup()``
# [Codecov](https://codecov.io/gh/pandas-dev/pandas/pull/14026?src=pr&el=h1) Report
> :exclamation: No coverage uploaded for pull request base (`master@fb47ee5`). [Click here to learn what that means](https://docs.codecov.io/docs/error-reference#section-missing-base-commit).
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/pandas-dev/pandas/pull/14026/graphs/tree.svg?token=eZ4WkYLtcO&src=pr&width=650&height=150)](https://codecov.io/gh/pandas-dev/pandas/pull/14026?src=pr&el=tree)

```diff
@@            Coverage Diff            @@
##             master   #14026   +/-   ##
=========================================
  Coverage          ?   90.76%           
=========================================
  Files             ?      161           
  Lines             ?    51098           
  Branches          ?        0           
=========================================
  Hits              ?    46377           
  Misses            ?     4721           
  Partials          ?        0
```

| Flag | Coverage Δ | |
|---|---|---|
| #multiple | `88.6% <100%> (?)` | |
| #single | `40.16% <33.33%> (?)` | |

| [Impacted Files](https://codecov.io/gh/pandas-dev/pandas/pull/14026?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [pandas/core/groupby.py](https://codecov.io/gh/pandas-dev/pandas/pull/14026?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvZ3JvdXBieS5weQ==) | `92.07% <100%> (ø)` | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/pandas-dev/pandas/pull/14026?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/pandas-dev/pandas/pull/14026?src=pr&el=footer). Last update [fb47ee5...14d941b](https://codecov.io/gh/pandas-dev/pandas/pull/14026?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

I agree that `factorize` and `categorize` are not ideal (for me due to the link to categoricals, and the result is here is not a categorical). 
I first didn't like `ngroup`, as it looked *too much* as the existing `ngroups`, and it's not the same. But maybe that is not bad after all. `group_number` would be the most explicit. But OK with `ngroup` I think.
Just to be sure we have considered this: we have the question whether this should behave similar to `cumcount` (transformer) or rather similar to `size` (reducer)? 
But I suppose it is mainly the transforming case that is useful? 

In the second case we could always have both behaviours as `.ngroup()` vs `.transform('ngroup')`
can you rebase / update
@dsm054 if you can rebase. IIRC this was basically ready.
@dsm054 if you can update docs as indicated and rebase today would be great.
@jreback: had to rebase this morning 'cause of that linting error you fixed.  I'll keep doing so until we're green.
test_write_fspath_all[to_hdf-writer_kwargs3-tables] failed, which doesn't seem related.
> test_write_fspath_all[to_hdf-writer_kwargs3-tables] failed, which doesn't seem related.

yeah, I didn't realize that test was flaky, but I am able to reproduce it occasionally.
thanks @dsm054 nice PR!