BUG: algorithms.factorize moves null values when sort=False
State: closed

- [x] closes #46584 (Replace xxxx with the Github issue number)
- [x] [Tests added and passed](https://pandas.pydata.org/pandas-docs/dev/development/contributing_codebase.html#writing-tests) if fixing a bug or adding a new feature
- [x] All [code checks passed](https://pandas.pydata.org/pandas-docs/dev/development/contributing_codebase.html#pre-commit).
- [x] Added an entry in the latest `doc/source/whatsnew/vX.X.X.rst` file if fixing a bug or adding a new feature.

In the example below, the result_index has nan moved even though sort=False. This is the order that will be in any groupby reduction result and the reason why transform currently returns wrong results.

```
df = pd.DataFrame({'a': [1, 3, np.nan, 1, 2], 'b': [3, 4, 5, 6, 7]})
print(df.groupby('a', sort=False, dropna=False).grouper.result_index)

# main
Float64Index([1.0, 3.0, 2.0, nan], dtype='float64', name='a')
# this PR
Float64Index([1.0, 3.0, nan, 2.0], dtype='float64', name='a')
```

cc @jbrockmendel @jreback 

Labels:
name: Bug
name: Groupby
name: Missing-data
description: np.nan, pd.NaT, pd.NA, dropna, isnull, interpolate

Comments:
@jbrockmendel - I've implemented the EA paths. There is a bit of a discrepancy between pd.factorize and EA's factorize (as well as factorize_array). Namely, the former can take `na_sentinel=None` whereas the latter two have `na_sentinel` being integers. Because of this, I added the `dropna` to the EA factorize as well as factorize_array. Could also call this argument `ignore_na`, but I think that isn't used by the public API so `dropna` seemed better.

I wonder if we should add `dropna` to pd.factorize and deprecate `na_sentinel` being None there. Can also go the other route here and allow `na_sentinel` being None in EA's factorize; no strong opinion but I find `na_sentinel=None` meaning "Don't drop null values from uniques; and code nulls as -1" not very clear.
@jbrockmendel - gentle ping. 
> I wonder if we should add dropna to pd.factorize and deprecate na_sentinel being None there. Can also go the other route here and allow na_sentinel being None in EA's factorize; no strong opinion but I find na_sentinel=None meaning "Don't drop null values from uniques; and code nulls as -1" not very clear.

I agree on the lack of clarity, would like to see this cleaned up (separate PR(s))
Thanks for the comments @jbrockmendel, ready for another go.

> I agree on the lack of clarity, would like to see this cleaned up (separate PR(s))

Opened #46910
@jbrockmendel - Based on the discussion in #46910, I reworked the handling of na_sentinel in factorize. Instead of adding `dropna`, this PR now allows `na_sentinel=None`.
@jbrockmendel - gentle ping
@rhshadrach thanks for being patient with me.

I'm not wild about adding na_sentiel=None to EA.factorize (would rather deprecate na_sentiel xref #46910), but if you say it is necessary to fix a bug (at least until such a deprecation is enforced), I'll take your word for it
@jbrockmendel Something is necessary, but I like the idea of deprecating specifying na_sentinel in pd.factorize and changing EA's factorize to match what pd.factorize will look like in 2.0. Let's wait on discussion in #46910. If we do go this route, I'll throw up a PR for the deprecation and we can return to this PR after.
@jbrockmendel - friendly ping
> friendly ping

Thanks for your patience.  This is near the top of my queue.
@mroeschke im flailing on reviewing this.  can you tag in?
Great! Thanks for the effort on this @rhshadrach